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() 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): class WorkoutMultipleCompareForm(forms.Form):
workouts = forms.ModelMultipleChoiceField( workouts = forms.ModelMultipleChoiceField(
+113 -87
View File
@@ -79,108 +79,134 @@
</a> </a>
{% endif %} {% endif %}
{% 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> </span>
</li> </li>
{% if workouts %} {% if workouts %}
{% for workout in workouts %} {% if request.GET.selectworkouts %}
<li class="grid_4 divlines"> <li class="grid_4">
{{ workout.date|date:"Y-m-d" }} {{ workout.starttime|date:"H:i" }} <form enctype="multipart/form-data" method="post">
{% if workout.duplicate %} <h3>Bulk Operation</h3>
<span style="font-size: smaller"><i class="fal fa-angle-double-up"></i></span> <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 %} {% endif %}
{% if workout.rankingpiece %} </h3>
<span style="font-size: smaller"><i class="fal fa-asterisk"></i></span> {% else %}
{% endif %} <h3>
{% if workout.rpe == 0 and not workout.duplicate %} {% if team %}
<span style="font-size: smaller"> <a
<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"
href={% url rower.defaultlandingpage id=workout.id|encode %} href={% url rower.defaultlandingpage id=workout.id|encode %}
title="{{ rower.defaultlandingpage|verbose }}"> 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> <i class="{{ rower.defaultlandingpage|landingicon }}"></i></a>
{% else %} {% else %}
&nbsp; &nbsp;
{% endif %} {% endif %}
</div> </div>
<div class="workoutelement"> <div class="workoutelement">
{% if workout|may_edit:request %} {% if workout|may_edit:request %}
{% if rower.defaultlandingpage2 != 'workout_delete' %} {% if rower.defaultlandingpage2 != 'workout_delete' %}
<a class="small" <a class="small"
href={% url rower.defaultlandingpage2 id=workout.id|encode %} href={% url rower.defaultlandingpage2 id=workout.id|encode %}
title="{{ rower.defaultlandingpage2|verbose }}"> title="{{ rower.defaultlandingpage2|verbose }}">
<i class="{{ rower.defaultlandingpage2|landingicon }}"></i></a> <i class="{{ rower.defaultlandingpage2|landingicon }}"></i></a>
{% else %} {% else %}
<a class="small" <a class="small"
href={% url rower.defaultlandingpage2 id=workout.id|encode %} href={% url rower.defaultlandingpage2 id=workout.id|encode %}
title="{{ rower.defaultlandingpage2|verbose }}"> title="{{ rower.defaultlandingpage2|verbose }}">
<i class="{{ rower.defaultlandingpage2|landingicon }}"></i></a> <i class="{{ rower.defaultlandingpage2|landingicon }}"></i></a>
{% endif %} {% endif %}
{% else %} {% else %}
&nbsp; &nbsp;
{% endif %} {% endif %}
</div> </div>
</div> </div>
</li> </li>
{% endfor %} {% endfor %}
{% else %} {% else %}
<li> No workouts found </li> <li> No workouts found </li>
{% endif %} {% endif %}
{% if request.GET.selectworkouts %}
</form>
{% endif %}
<li class="grid_4"> <li class="grid_4">
<p>Filter on date <p>Filter on date
<form enctype="multipart/form-data" method="post"> <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'), views.agegrouprecordview, name='agegrouprecordview'),
re_path(r'^agegrouprecords/$', re_path(r'^agegrouprecords/$',
views.agegrouprecordview, name='agegrouprecordview'), 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, re_path(r'^workouts/setrpe/$', views.workouts_setrpe_view,
name='workouts_setrpe_view'), name='workouts_setrpe_view'),
re_path(r'^workouts/setrpe/user/(?P<userid>\d+)/$', views.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 django.contrib.auth import authenticate, login, logout
from rowers.forms import ( from rowers.forms import (
WorkoutBulkActions, ExportChoices,
ForceCurveOptionsForm, HistoForm, TeamMessageForm, ForceCurveOptionsForm, HistoForm, TeamMessageForm,
LoginForm, DocumentsForm, UploadOptionsForm, ImageForm, CourseForm, LoginForm, DocumentsForm, UploadOptionsForm, ImageForm, CourseForm,
CourseConfirmForm, ResampleForm, CourseConfirmForm, ResampleForm,
+67
View File
@@ -2,6 +2,7 @@ from shutil import copyfile
from rowers.views.statements import * from rowers.views.statements import *
import rowers.mytypes as mytypes import rowers.mytypes as mytypes
from rowers.integrations import importsources
import numpy import numpy
import rowers.uploads as uploads import rowers.uploads as uploads
import rowers.utils as utils 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 # List Workouts
@login_required() @login_required()
def workouts_view(request, message='', successmessage='', 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)) enddate = datetime.datetime.combine(enddate, datetime.time(23, 59, 59))
query = None query = None
if request.method == 'POST': 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) dateform = DateRangeForm(request.POST)
searchform = SearchForm(request.POST) searchform = SearchForm(request.POST)
if dateform.is_valid(): # pragma: no cover 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. \ 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>.') You can switch off this warning in <a href="/rowers/me/edit">settings</a>.')
actionform = WorkoutBulkActions()
return render(request, 'list_workouts.html', return render(request, 'list_workouts.html',
{'workouts': workouts, {'workouts': workouts,
'active': 'nav-workouts', 'active': 'nav-workouts',
'rower': r, 'rower': r,
'actionform': actionform,
'searchform': searchform, 'searchform': searchform,
'breadcrumbs': breadcrumbs, 'breadcrumbs': breadcrumbs,
'dateform': dateform, 'dateform': dateform,