From 95bc02e1225decaeb9723a693acb58a95e044a2f Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Wed, 22 Jan 2025 20:17:47 +0100
Subject: [PATCH 1/3] step s
---
rowers/models.py | 6 +
rowers/templates/stepeditor.html | 331 ++++++++++++++++---------------
rowers/views/planviews.py | 3 +
3 files changed, 176 insertions(+), 164 deletions(-)
diff --git a/rowers/models.py b/rowers/models.py
index b805b234..b69cb380 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -2755,6 +2755,12 @@ class PlannedSessionStep(models.Model):
return d
+ # string
+ def __str__(self):
+ str = 'Step {id} {name} {intensity}'.format(id=self.pk, name=self.name, intensity=self.intensity)
+
+ return str
+
class StepEditorForm(ModelForm):
class Meta:
model = PlannedSessionStep
diff --git a/rowers/templates/stepeditor.html b/rowers/templates/stepeditor.html
index efc9daf1..9500219b 100644
--- a/rowers/templates/stepeditor.html
+++ b/rowers/templates/stepeditor.html
@@ -20,9 +20,9 @@
Training Steps for {{ ps.name }}
-
{% for step in currentsteps %}
@@ -175,216 +175,219 @@
descriptions["{{ key }}"] = "{{ value }}"
{% endfor %}
$(document).ready(function() {
- csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
+ csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
});
function csrfSafeMethod(method) {
- // these HTTP methods do not require CSRF protection
- return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
+ // these HTTP methods do not require CSRF protection
+ return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
- beforeSend: function(xhr, settings) {
- if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
- xhr.setRequestHeader("X-CSRFToken", csrftoken);
+ beforeSend: function(xhr, settings) {
+ if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
+ xhr.setRequestHeader("X-CSRFToken", csrftoken);
+ }
}
- }
});
-
- let dragged;
- let origcolor;
-
- function saveSession() {
-
+
+ let dragged;
+ let origcolor;
+
+ function saveSession() {
+
var list = [];
steps = document.querySelector('.drop-zone');
steps.childNodes.forEach(function(item) {
- if (item.className && item.className.includes("trainingstep")) {
- item.childNodes.forEach(function(child) {
- if (child.id && child.className == 'stepcontent') {
- list.push(child.id);
- }
- })
- }
+ if (item.className && item.className.includes("trainingstep")) {
+ item.childNodes.forEach(function(child) {
+ if (child.id && child.className == 'stepcontent') {
+ list.push(child.id);
+ }
+ })
+ }
});
$.ajax({
- data: JSON.stringify(list),
- type: 'POST',
- url: '/rowers/plans/stepadder/{{ ps.id }}/?save=1',
- error: function(result) {
- $("#id_waiting").replaceWith(
- 'Your upload failed
'
- );
- },
- success: function(result) {
- console.log(result)
- }
- })
- window.location.reload(true);
- }
+ data: JSON.stringify(list),
+ type: 'POST',
+ url: '/rowers/plans/stepadder/{{ ps.id }}/?save=1',
+ error: function(result) {
+ $("#id_waiting").replaceWith(
+ 'Your upload failed
'
+ );
+ },
+ success: function(result) {
+ console.log(result)
+ }
+ })
+ saveState();
+ // reload the page
+ location.reload();
+ // window.location.reload(true);
+ }
- function saveState() {
+ function saveState() {
var cntr = 0;
var list = [];
steps = document.querySelector('.drop-zone');
steps.childNodes.forEach(function(item) {
- if (item.className && item.className.includes("trainingstep")) {
- item.childNodes.forEach(function(child) {
- if (child.id && child.className == 'stepcontent') {
- list.push(child.id);
- }
- if (child.className == "stepid") {
- s = `(${cntr})`
- child.replaceWith(s);
- cntr++;
- }
- })
- }
+ if (item.className && item.className.includes("trainingstep")) {
+ item.childNodes.forEach(function(child) {
+ if (child.id && child.className == 'stepcontent') {
+ list.push(child.id);
+ }
+ if (child.className == "stepid") {
+ s = `(${cntr})`
+ child.replaceWith(s);
+ cntr++;
+ }
+ })
+ }
});
$.ajax({
- data: JSON.stringify(list),
- type: 'POST',
- url: '/rowers/plans/stepadder/{{ ps.id }}/',
- error: function(result) {
- $("#id_waiting").replaceWith(
- 'Your upload failed
'
- );
- },
- success: function(result) {
- console.log(result)
- }
- })
+ data: JSON.stringify(list),
+ type: 'POST',
+ url: '/rowers/plans/stepadder/{{ ps.id }}/',
+ error: function(result) {
+ $("#id_waiting").replaceWith(
+ 'Your upload failed
'
+ );
+ },
+ success: function(result) {
+ console.log(result)
+ }
+ })
};
-
- function handleDragStart(event) {
+
+ function handleDragStart(event) {
let target = event.target;
if (target) {
- dragged = target;
- origcolor = dragged.style.backgroundColor;
- event.dataTransfer.setData('text', target.id);
- event.dataTransfer.dropEffect = 'move';
- // Make it half transparent
- event.target.style.opacity = .3;
+ dragged = target;
+ origcolor = dragged.style.backgroundColor;
+ event.dataTransfer.setData('text', target.id);
+ event.dataTransfer.dropEffect = 'move';
+ // Make it half transparent
+ event.target.style.opacity = .3;
}
- }
-
- function handleDragEnd(event) {
+ }
+
+ function handleDragEnd(event) {
if (event.target) {
- // Reset the transparency
- event.target.style.opacity = ''; // reset opacity when drag ends
- items.forEach(function (item) {
- item.classList.remove('over');
- });
- dragged = null;
- origcolor = null;
+ // Reset the transparency
+ event.target.style.opacity = ''; // reset opacity when drag ends
+ items.forEach(function (item) {
+ item.classList.remove('over');
+ });
+ dragged = null;
+ origcolor = null;
}
saveState();
- }
-
- function handleMouseOver(event) {
+ }
+
+ function handleMouseOver(event) {
+ if (event.preventDefault) {
+ const id = event.target.id;
+ if (id) {
+ var name = mysteps[id]['name'];
+ var txt = descriptions[id];
+ var divstring = `${name}
${txt}
`
+ const div = document.getElementById("stepinfo");
+ div.innerHTML = divstring;
+ }
+ }
+
+ return false;
+ }
+
+ function handleMouseLeave(event) {
if (event.preventDefault) {
- const id = event.target.id;
- if (id) {
- var name = mysteps[id]['name'];
- var txt = descriptions[id];
- var divstring = `${name}
${txt}
`
const div = document.getElementById("stepinfo");
- div.innerHTML = divstring;
- }
+ div.innerHTML = 'Hover over a step to get details
'
}
return false;
- }
-
- function handleMouseLeave(event) {
- if (event.preventDefault) {
- const div = document.getElementById("stepinfo");
- div.innerHTML = 'Hover over a step to get details
'
- }
-
- return false;
- }
-
- function handleDragOver(event) {
+ }
+
+ function handleDragOver(event) {
event.preventDefault();
- }
-
- function handleDragEnter(event) {
+ }
+
+ function handleDragEnter(event) {
this.classList.add('over');
const target = event.target;
overcolor = event.target.style.backgroundColor;
-
+
if (target && dragged) {
- event.preventDefault();
- // Set the dropEffect to move
- event.dataTransfer.dropEffect = 'move'
- target.style.background = '#1f904e';
+ event.preventDefault();
+ // Set the dropEffect to move
+ event.dataTransfer.dropEffect = 'move'
+ target.style.background = '#1f904e';
}
- }
-
- function handleDragLeave(event) {
+ }
+
+ function handleDragLeave(event) {
event.target.style.backgroundColor = '';
-
+
if (dragged.parentNode.className.includes("drop-zone")) {
- trash(event);
+ trash(event);
}
saveState();
- }
-
- function trash(event) {
+ }
+
+ function trash(event) {
const target = event.target;
event.target.style.backgroundColor = '';
if (target && dragged) {
- event.preventDefault();
- if (dragged.parentNode.className.includes("drop-zone")) {
- dragged.remove();
- }
+ event.preventDefault();
+ if (dragged.parentNode.className.includes("drop-zone")) {
+ dragged.remove();
+ }
}
- }
-
- function handleDrop(event) {
+ }
+
+ function handleDrop(event) {
const target = event.target;
if (target && dragged) {
- target.style.backgroundColor = '';
- event.preventDefault();
- // Get the id of the target and add the moved element to the target's DOM
- // dragged.parentNode.removeChild(dragged);
- if (target.nodeName == "SECTION") {
- dragged.style.opacity = '';
- dragged.style.backgroundColor = origcolor;
- var dropped = dragged.cloneNode(true);
- dropped.id = 0;
- target.appendChild(dropped);
- }
- if (target.nodeName == 'DIV') {
- // insert after
- dragged.style.opacity = '';
- dragged.style.backgroundColor = origcolor;
- var dropped = dragged.cloneNode(true);
- if (target.parentNode.nodeName == 'SECTION') {
- target.after(dropped);
+ target.style.backgroundColor = '';
+ event.preventDefault();
+ // Get the id of the target and add the moved element to the targets DOM
+ // dragged.parentNode.removeChild(dragged);
+ if (target.nodeName == "SECTION") {
+ dragged.style.opacity = '';
+ dragged.style.backgroundColor = origcolor;
+ var dropped = dragged.cloneNode(true);
+ dropped.id = 0;
+ target.appendChild(dropped);
}
- if (target.parentNode.parentNode.nodeName == 'SECTION') {
- target.parentNode.after(dropped);
+ if (target.nodeName == 'DIV') {
+ // insert after
+ dragged.style.opacity = '';
+ dragged.style.backgroundColor = origcolor;
+ var dropped = dragged.cloneNode(true);
+ if (target.parentNode.nodeName == 'SECTION') {
+ target.after(dropped);
+ }
+ if (target.parentNode.parentNode.nodeName == 'SECTION') {
+ target.parentNode.after(dropped);
+ }
}
- }
}
saveState();
}
-
+
function noDrop(event) {
- event.preventDefault();
+ event.preventDefault();
}
-
-
- let dropzone = document.querySelector('.drop-zone')
- dropzone.addEventListener('dragenter', handleDragEnter);
- dropzone.addEventListener('dragleave', handleDragLeave);
- dropzone.addEventListener('drop', handleDrop)
- dropzone.addEventListener('dragover', handleDragOver);
- dropzone.addEventListener('dragstart', handleDragStart);
- dropzone.addEventListener('dragend', handleDragEnd);
-
- let items = document.querySelectorAll('.stepcontainer .trainingstep');
- items.forEach(function(item) {
+
+
+ let dropzone = document.querySelector('.drop-zone')
+ dropzone.addEventListener('dragenter', handleDragEnter);
+ dropzone.addEventListener('dragleave', handleDragLeave);
+ dropzone.addEventListener('drop', handleDrop)
+ dropzone.addEventListener('dragover', handleDragOver);
+ dropzone.addEventListener('dragstart', handleDragStart);
+ dropzone.addEventListener('dragend', handleDragEnd);
+
+ let items = document.querySelectorAll('.stepcontainer .trainingstep');
+ items.forEach(function(item) {
item.addEventListener('dragstart', handleDragStart);
item.addEventListener('dragend', handleDragEnd);
item.addEventListener('dragleave', handleDragLeave);
@@ -392,10 +395,10 @@
item.addEventListener('dragenter',noDrop);
item.addEventListener('mouseover',handleMouseOver);
item.addEventListener('mouseleave',handleMouseLeave);
- });
-
-
-
+ });
+
+
+
{% endblock %}
{% block sidebar %}
diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py
index 33255a17..8ba2c0ba 100644
--- a/rowers/views/planviews.py
+++ b/rowers/views/planviews.py
@@ -3077,6 +3077,7 @@ def rower_create_trainingplan(request, id=0):
'old_targets': old_targets,
})
+@csrf_exempt
@user_passes_test(can_plan, login_url="/rowers/paidplans",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
@@ -3116,6 +3117,7 @@ def stepadder(request, id=0):
'message': 'permission denied for host '+hostt[0]}
return JSONResponse(status=403, data=message)
+
if ps.steps:
filename = ps.steps.get('filename','')
sport = ps.steps.get('sport','rowing')
@@ -3208,6 +3210,7 @@ def stepedit(request, id=0, psid=0):
ps.fitfile = None
ps.interval_string = ""
+
ps.save()
step.durationtype = form.cleaned_data['durationtype']
From 7190d97d29d4d0463cfa7c58ca0c22675bc9ed89 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Wed, 22 Jan 2025 20:50:02 +0100
Subject: [PATCH 2/3] fixing step editor
---
rowers/utils.py | 4 ++++
rowers/views/otherviews.py | 1 +
rowers/views/planviews.py | 2 +-
3 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/rowers/utils.py b/rowers/utils.py
index 0072805b..070f1a45 100644
--- a/rowers/utils.py
+++ b/rowers/utils.py
@@ -740,6 +740,10 @@ def steps_write_fit(steps):
# convert to json, value of keys called wkt_step_name to string
for step in steps['steps']:
step['wkt_step_name'] = str(step['wkt_step_name'])
+ # convert numerical values in the dict to integers
+ for key in step.keys():
+ if isinstance(step[key], (int, float)):
+ step[key] = int(step[key])
response = requests.post(url=url, headers=headers, json=steps)
diff --git a/rowers/views/otherviews.py b/rowers/views/otherviews.py
index 3fef3dd5..bd59a9bf 100644
--- a/rowers/views/otherviews.py
+++ b/rowers/views/otherviews.py
@@ -50,6 +50,7 @@ def download_fit(request, filename=''):
try:
response = HttpResponse(fitfile)
except FileNotFoundError:
+ print(fitfile, "not found")
raise Http404("File not found")
response['Content-Disposition'] = 'attachment; filename="%s"' % filename # pragma: no cover
diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py
index 8ba2c0ba..5833c5a5 100644
--- a/rowers/views/planviews.py
+++ b/rowers/views/planviews.py
@@ -3204,7 +3204,7 @@ def stepedit(request, id=0, psid=0):
if form.cleaned_data['durationtype'] == 'Time':
ss['durationValue'] = form.cleaned_data['durationvalue']*60000
elif form.cleaned_data['durationtype'] == 'Distance':
- ss[durationValue] = form.cleaned_data['durationvalue']*100
+ ss['durationValue'] = form.cleaned_data['durationvalue']*100
ss['durationValue'] = int(ss['durationValue'])
ps.fitfile = None
From 3c686726780760074f7eae837592bf1dbe3e4aeb Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Sat, 25 Jan 2025 14:56:37 +0100
Subject: [PATCH 3/3] fixes fixes
---
rowers/forms.py | 5 +++--
rowers/integrations/trainingpeaks.py | 6 +++++-
rowers/tests/testdata/testdata.tcx.gz | Bin 3989 -> 3989 bytes
rowers/uploads.py | 9 +++++----
rowers/views/otherviews.py | 1 -
rowers/views/workoutviews.py | 9 +++++++--
6 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/rowers/forms.py b/rowers/forms.py
index 2db9b8bf..face5583 100644
--- a/rowers/forms.py
+++ b/rowers/forms.py
@@ -1232,10 +1232,11 @@ bulkactions = (
('unset commute','unset commute'),
)
destinations = (
- ('c2','c2'),
+ ('c2','concept2'),
('strava','strava'),
('sporttracks','sporttracks'),
- ('trainingpeaks','trainingpeaks')
+ ('trainingpeaks','trainingpeaks'),
+ ('intervals','intervals.icu')
)
class WorkoutBulkActions(forms.Form):
diff --git a/rowers/integrations/trainingpeaks.py b/rowers/integrations/trainingpeaks.py
index cd3b6bb4..e56095c7 100644
--- a/rowers/integrations/trainingpeaks.py
+++ b/rowers/integrations/trainingpeaks.py
@@ -79,13 +79,17 @@ class TPIntegration(SyncIntegration):
def workout_export(self, workout, *args, **kwargs) -> str:
thetoken = self.open()
tcxfilename = self.createworkoutdata(workout)
+ try:
+ wtype = tpmapping[workout.workouttype]
+ except KeyError:
+ wtype = 'rowing'
job = myqueue(
queue,
handle_workout_tp_upload,
workout,
thetoken,
tcxfilename,
- tpmapping[workout.workouttype]
+ wtype
)
return job.id
diff --git a/rowers/tests/testdata/testdata.tcx.gz b/rowers/tests/testdata/testdata.tcx.gz
index 415ce353fe916f370478946207c46bf181052e89..8c06d57a103c30792fa538ffe0fe9c1a591b7590 100644
GIT binary patch
delta 16
XcmbO#KUJPxzMF$%;_WFL+57kbEQ$qx
delta 16
XcmbO#KUJPxzMF&Njm*T2?0x(IDt-kR
diff --git a/rowers/uploads.py b/rowers/uploads.py
index 48f54ede..06273c63 100644
--- a/rowers/uploads.py
+++ b/rowers/uploads.py
@@ -130,6 +130,9 @@ def make_plot(r, w, f1, f2, plottype, title, imagename='', plotnr=0):
def do_sync(w, options, quick=False):
+ if w.duplicate:
+ return 0
+
do_strava_export = False
if w.user.strava_auto_export is True:
do_strava_export = True
@@ -236,8 +239,6 @@ def do_sync(w, options, quick=False):
except KeyError:
pass
- if w.duplicate:
- return 0
if do_c2_export: # pragma: no cover
dologging('c2_log.log','Exporting workout to C2 for user {user}'.format(user=w.user.user.id))
@@ -305,7 +306,6 @@ def do_sync(w, options, quick=False):
except NoTokenError:
dologging('st_export.log','No Token Error')
- return 0
do_tp_export = w.user.trainingpeaks_auto_export
try:
@@ -315,6 +315,8 @@ def do_sync(w, options, quick=False):
do_tp_export = upload_to_tp
except KeyError:
upload_to_st = False
+
+
if do_tp_export:
try:
tp_integration = TPIntegration(w.user.user)
@@ -327,7 +329,6 @@ def do_sync(w, options, quick=False):
)
except NoTokenError:
dologging('tp_export.log','No Token Error')
- return 0
# we do Strava last.
if do_strava_export: # pragma: no cover
diff --git a/rowers/views/otherviews.py b/rowers/views/otherviews.py
index bd59a9bf..3fef3dd5 100644
--- a/rowers/views/otherviews.py
+++ b/rowers/views/otherviews.py
@@ -50,7 +50,6 @@ def download_fit(request, filename=''):
try:
response = HttpResponse(fitfile)
except FileNotFoundError:
- print(fitfile, "not found")
raise Http404("File not found")
response['Content-Disposition'] = 'attachment; filename="%s"' % filename # pragma: no cover
diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py
index 31864c0d..b8dd6c72 100644
--- a/rowers/views/workoutviews.py
+++ b/rowers/views/workoutviews.py
@@ -2044,6 +2044,12 @@ def workouts_bulk_actions(request):
workoutids = request.session.get('ids',[])
workouts = []
exportchoice = 'strava'
+
+ # exportchoice = ExportChoices()
+ actionform = WorkoutBulkActions()
+ actionform.fields["action"].initial = action
+ assignchoices = AssignChoices()
+
try:
for encid in workoutids:
w = get_workout_by_opaqueid(request, encid)
@@ -2286,8 +2292,7 @@ def workouts_view(request, message='', successmessage='',
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list)),
- exclude_strava=False,
- )
+ ).exclude(workoutsource='strava')
searchform = SearchForm(initial={'q': query})
else:
searchform = SearchForm()