Private
Public Access
1
0

Merge branch 'feature/deletemany' into develop

This commit is contained in:
2024-01-20 15:34:16 +01:00
7 changed files with 243 additions and 88 deletions
+19
View File
@@ -1265,6 +1265,25 @@ class ForceCurveMultipleCompareForm(forms.Form):
widget=forms.CheckboxSelectMultiple()
)
bulkactions = (
('remove','remove'),
('export','export'),
)
destinations = (
('C2','C2'),
('strava','strava'),
('sporttracks','sporttracks'),
('trainingpeaks','trainingpeaks')
)
class WorkoutBulkActions(forms.Form):
action = forms.ChoiceField(
choices=bulkactions, label='Action', required=True, initial='remove')
class ExportChoices(forms.Form):
destination = forms.ChoiceField(
choices=destinations, label='Destination', required=False, initial='strava'
)
class WorkoutMultipleCompareForm(forms.Form):
workouts = forms.ModelMultipleChoiceField(
+114 -88
View File
@@ -79,108 +79,134 @@
</a>
{% endif %}
{% endif %}
{% if not request.GET.selectworkouts %}
<span>
<a href="./?selectworkouts=true">Bulk Actions</a>
</span>
{% else %}
<span>
<a href="./">Close Bulk Actions</a>
</span>
{% endif %}
</span>
</li>
{% if workouts %}
{% for workout in workouts %}
<li class="grid_4 divlines">
{{ workout.date|date:"Y-m-d" }} {{ workout.starttime|date:"H:i" }}
{% if workout.duplicate %}
<span style="font-size: smaller"><i class="fal fa-angle-double-up"></i></span>
{% if request.GET.selectworkouts %}
<li class="grid_4">
<form enctype="multipart/form-data" method="post">
<h3>Bulk Operation</h3>
<p>Please select workouts from the list to do a bulk operation on.</p>
{{ actionform.as_p }}
{% csrf_token %}
<input name='selectworkouts' type="submit" value="Submit">
</li>
{% endif %}
{% for workout in workouts %}
<li class="grid_4 divlines">
{% if request.GET.selectworkouts %}
<input type="checkbox" id="workoutid" value={{ workout.id|encode }} name="workoutid" />
{% endif %}
{{ workout.date|date:"Y-m-d" }} {{ workout.starttime|date:"H:i" }}
{% if workout.duplicate %}
<span style="font-size: smaller"><i class="fal fa-angle-double-up"></i></span>
{% endif %}
{% if workout.rankingpiece %}
<span style="font-size: smaller"><i class="fal fa-asterisk"></i></span>
{% endif %}
{% if workout.rpe == 0 and not workout.duplicate %}
<span style="font-size: smaller">
<a href="/rowers/workout/{{ workout.id|encode }}/edit/">No RPE</a>
</span>
{% endif %}
<div>
{% if workout.name != '' %}
<h3>
{% if team %}
<a
href={% url rower.defaultlandingpage3 id=workout.id|encode %}
title="{{ rower.defaultlandingpage3|verbose }}">
{{ workout.name }} ({{ workout.user.user.first_name }} {{ workout.user.user.last_name}})
</a>
{% else %}
<a
href={% url rower.defaultlandingpage3 id=workout.id|encode %}
title="{{ rower.defaultlandingpage3|verbose }}">
{{ workout.name }}
</a>
{% endif %}
{% if workout.rankingpiece %}
<span style="font-size: smaller"><i class="fal fa-asterisk"></i></span>
{% endif %}
{% if workout.rpe == 0 and not workout.duplicate %}
<span style="font-size: smaller">
<a href="/rowers/workout/{{ workout.id|encode }}/edit/">No RPE</a>
</span>
{% endif %}
<div>
{% if workout.name != '' %}
<h3>
{% if team %}
<a
href={% url rower.defaultlandingpage3 id=workout.id|encode %}
title="{{ rower.defaultlandingpage3|verbose }}">
{{ workout.name }} ({{ workout.user.user.first_name }} {{ workout.user.user.last_name}})
</a>
{% else %}
<a
href={% url rower.defaultlandingpage3 id=workout.id|encode %}
title="{{ rower.defaultlandingpage3|verbose }}">
{{ workout.name }}
</a>
{% endif %}
</h3>
{% else %}
<h3>
{% if team %}
<a
href={% url rower.defaultlandingpage id=workout.id|encode %}
title="{{ rower.defaultlandingpage|verbose }}">
No Name ({{ workout.user.user.first_name }} {{ workout.user.user.last_name}})
</a>
{% else %}
<a
href={% url rower.defaultlandingpage id=workout.id|encode %}
title="{{ rower.defaultlandingpage|verbose }}">
No Name
</a>
{% endif %}
</h3>
{% endif %}
</div>
<div class="workoutcontainer">
<div class="icondiv">
{% with workout.workouttype|icon|safe as templateName %}
{% include templateName %}
{% endwith %}
</div>
<div class="workoutelement">
<span style="color:#555">Distance</span><br>
{{ workout.distance|distanceprint }}
</div>
<div class="workoutelement">
<span style="color:#555">Time</span><br>
{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
</div>
<div class="workoutelement">
{% if workout|may_edit:request %}
<a class="small"
</h3>
{% else %}
<h3>
{% if team %}
<a
href={% url rower.defaultlandingpage id=workout.id|encode %}
title="{{ rower.defaultlandingpage|verbose }}">
No Name ({{ workout.user.user.first_name }} {{ workout.user.user.last_name}})
</a>
{% else %}
<a
href={% url rower.defaultlandingpage id=workout.id|encode %}
title="{{ rower.defaultlandingpage|verbose }}">
No Name
</a>
{% endif %}
</h3>
{% endif %}
</div>
<div class="workoutcontainer">
<div class="icondiv">
{% with workout.workouttype|icon|safe as templateName %}
{% include templateName %}
{% endwith %}
</div>
<div class="workoutelement">
<span style="color:#555">Distance</span><br>
{{ workout.distance|distanceprint }}
</div>
<div class="workoutelement">
<span style="color:#555">Time</span><br>
{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
</div>
<div class="workoutelement">
{% if workout|may_edit:request %}
<a class="small"
href={% url rower.defaultlandingpage id=workout.id|encode %}
title="{{ rower.defaultlandingpage|verbose }}">
<i class="{{ rower.defaultlandingpage|landingicon }}"></i></a>
{% else %}
&nbsp;
{% endif %}
</div>
<div class="workoutelement">
{% if workout|may_edit:request %}
{% if rower.defaultlandingpage2 != 'workout_delete' %}
<a class="small"
href={% url rower.defaultlandingpage2 id=workout.id|encode %}
title="{{ rower.defaultlandingpage2|verbose }}">
{% else %}
&nbsp;
{% endif %}
</div>
<div class="workoutelement">
{% if workout|may_edit:request %}
{% if rower.defaultlandingpage2 != 'workout_delete' %}
<a class="small"
href={% url rower.defaultlandingpage2 id=workout.id|encode %}
title="{{ rower.defaultlandingpage2|verbose }}">
<i class="{{ rower.defaultlandingpage2|landingicon }}"></i></a>
{% else %}
<a class="small"
href={% url rower.defaultlandingpage2 id=workout.id|encode %}
title="{{ rower.defaultlandingpage2|verbose }}">
{% else %}
<a class="small"
href={% url rower.defaultlandingpage2 id=workout.id|encode %}
title="{{ rower.defaultlandingpage2|verbose }}">
<i class="{{ rower.defaultlandingpage2|landingicon }}"></i></a>
{% endif %}
{% else %}
&nbsp;
{% endif %}
</div>
</div>
</li>
{% endfor %}
{% endif %}
{% else %}
&nbsp;
{% endif %}
</div>
</div>
</li>
{% endfor %}
{% else %}
<li> No workouts found </li>
{% endif %}
{% if request.GET.selectworkouts %}
</form>
{% endif %}
<li class="grid_4">
<p>Filter on date
<form enctype="multipart/form-data" method="post">
@@ -0,0 +1,39 @@
{% extends "newbase.html" %}
{% load static %}
{% load rowerfilters %}
{% block title %}Change Workout {% endblock %}
{% block main %}
<h1>Bulk Actions</h1>
<ul class="main-content">
<li class="grid_4">
<form action="" method="post">
<p>
Edit and confirm the action you want to perform on the following workouts:
</p>
<p>
{{ form.as_p }}
</p>
<p>
{{ actionform.as_p }}
</p>
{% if action == 'export' %}
<p>
{{ exportchoice.as_p }}
</p>
{% endif %}
</li>
<li class="grid_2">
{% csrf_token %}
<input type="submit" value="Confirm">
</li>
</form>
</ul>
{% endblock %}
{% block sidebar %}
{% include 'menu_workout.html' %}
{% endblock %}
Binary file not shown.
+3
View File
@@ -292,6 +292,9 @@ urlpatterns = [
views.agegrouprecordview, name='agegrouprecordview'),
re_path(r'^agegrouprecords/$',
views.agegrouprecordview, name='agegrouprecordview'),
re_path(r'^workouts/actions/$',
views.workouts_bulk_actions,
name='workouts_bulk_actions'),
re_path(r'^workouts/setrpe/$', views.workouts_setrpe_view,
name='workouts_setrpe_view'),
re_path(r'^workouts/setrpe/user/(?P<userid>\d+)/$', views.workouts_setrpe_view,
+1
View File
@@ -96,6 +96,7 @@ from django.http import (
)
from django.contrib.auth import authenticate, login, logout
from rowers.forms import (
WorkoutBulkActions, ExportChoices,
ForceCurveOptionsForm, HistoForm, TeamMessageForm,
LoginForm, DocumentsForm, UploadOptionsForm, ImageForm, CourseForm,
CourseConfirmForm, ResampleForm,
+67
View File
@@ -2,6 +2,7 @@ from shutil import copyfile
from rowers.views.statements import *
import rowers.mytypes as mytypes
from rowers.integrations import importsources
import numpy
import rowers.uploads as uploads
import rowers.utils as utils
@@ -2031,6 +2032,63 @@ def workouts_setrpe_view(request,userid=0):
})
# Workout bulk actions
@login_required()
def workouts_bulk_actions(request):
r = getrower(request.user)
action = request.session['action']
workoutids = request.session['ids']
workouts = []
try:
for encid in workoutids:
w = get_workout_by_opaqueid(request, encid)
if w.user == r:
workouts.append(w)
except KeyError:
pass
if request.method == 'POST':
actionform = WorkoutBulkActions(request.POST)
form = WorkoutMultipleCompareForm(request.POST)
if form.is_valid() and actionform.is_valid():
workouts = form.cleaned_data['workouts']
action = actionform.cleaned_data['action']
if action == 'remove':
for w in workouts:
messages.info(request,'Removed workout '+str(encoder.encode_hex(w.id)))
w.delete()
elif action == 'export':
exportchoice = ExportChoices(request.POST)
if exportchoice.is_valid():
destination = exportchoice.cleaned_data['destination']
for w in workouts:
integration = importsources[destination](request.user)
try:
id = integration.workout_export(w)
messages.info(request,'Workout {id} exported to {destination}'.format(id=encoder.encode_hex(w.id),
destination=destination))
except NoTokenError:
messages.error(request,'Export to {destination} of workout {id} failed'.format(id=encoder.encode_hex(w.id),
destination=destination))
url = reverse('workouts_view')
return HttpResponseRedirect(url)
else:
exportchoice = ExportChoices()
actionform = WorkoutBulkActions()
actionform.fields["action"].initial = action
form = WorkoutMultipleCompareForm()
form.fields["workouts"].queryset = Workout.objects.filter(id__in=[w.id for w in workouts])
form.fields["workouts"].initial = workouts
return render(request,'workout_bulk_actions.html',
{'action':action,
'exportchoice':exportchoice,
'actionform':actionform,
'form':form,
'workouts':workouts})
# List Workouts
@login_required()
def workouts_view(request, message='', successmessage='',
@@ -2047,7 +2105,13 @@ def workouts_view(request, message='', successmessage='',
enddate = datetime.datetime.combine(enddate, datetime.time(23, 59, 59))
query = None
if request.method == 'POST':
if 'selectworkouts' in request.POST:
request.session['action']=request.POST['action']
request.session['ids'] = request.POST.getlist('workoutid')
url = reverse('workouts_bulk_actions')
return HttpResponseRedirect(url)
dateform = DateRangeForm(request.POST)
searchform = SearchForm(request.POST)
if dateform.is_valid(): # pragma: no cover
@@ -2227,10 +2291,13 @@ def workouts_view(request, message='', successmessage='',
Click <a href="/rowers/workouts/setrpe">here</a> to update them. \
You can switch off this warning in <a href="/rowers/me/edit">settings</a>.')
actionform = WorkoutBulkActions()
return render(request, 'list_workouts.html',
{'workouts': workouts,
'active': 'nav-workouts',
'rower': r,
'actionform': actionform,
'searchform': searchform,
'breadcrumbs': breadcrumbs,
'dateform': dateform,