Private
Public Access
1
0

export functionality v1

This commit is contained in:
2026-03-09 19:38:07 +01:00
parent 3ce1d88185
commit 2079f7ac5f
8 changed files with 168 additions and 0 deletions

View File

@@ -357,6 +357,80 @@ def correct_intensity(workout):
return workout
import io
import zipfile
@app.task
def email_all_user_workouts_zip(rower, start_date=None, end_date=None, debug=False, **kwargs):
# Get all workouts for this user, optionally filtered by date range
workouts = Workout.objects.filter(user=rower).order_by('-date')
# Apply date filters if provided
if start_date:
workouts = workouts.filter(date__gte=start_date)
if end_date:
workouts = workouts.filter(date__lte=end_date)
# for debug, limit to 5 workouts
if settings.DEBUG:
workouts = workouts[:5]
if not workouts.exists():
dologging('export_all_workouts.log', f"No workouts found for user {rower.user.id} in date range {start_date} to {end_date}")
return 0
# Create ZIP file in memory
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
for workout in workouts:
try:
rowdata = rdata(csvfile=workout.csvfilename)
rowdate = rowdata.rowdatetime
starttimeunix = arrow.get(rowdate).timestamp()
df = rowdata.df
try:
df[' ElapsedTime (sec)'] = df['TimeStamp (sec)']
df['TimeStamp (sec)'] = df['TimeStamp (sec)'] + starttimeunix
except KeyError:
pass
csv_filename = f"workout_{workout.id}_{workout.date.strftime('%Y%m%d')}.csv"
zip_file.writestr(csv_filename, df.to_csv(index=False))
except Exception as e: # pragma: no cover
dologging('export_all_workouts.log', f"Error exporting workout {workout.id}: {str(e)}")
continue
# Save ZIP file to disk
export_date = datetime.datetime.now().strftime('%Y%m%d')
filename = f"{rower.user.username}_workouts_{export_date}_from_{start_date}_to_{end_date}_{uuid4().hex[:8]}.zip"
zip_file_path = os.path.join(settings.MEDIA_ROOT, filename)
try:
with open(zip_file_path, 'wb') as f:
f.write(zip_buffer.getvalue())
except Exception as e: # pragma: no cover
dologging('export_all_workouts.log', f"Error saving ZIP file: {str(e)}")
return 0
# Send email with download link
subject = "Rowsandall Workouts Export"
from_email = 'Rowsandall <info@rowsandall.com>'
useremail = rower.user.email
# Generate download URL
download_url = f"{SITE_URL}/rowers/workouts/download/?file={filename}"
_ = send_template_email(
from_email, [useremail],
subject,
'workouts_export_email.html',
{'download_url': download_url, 'filename': filename},
)
return 1
@app.task
def handle_loadnextweek(rower, debug=False, **kwargs):
@@ -4522,3 +4596,4 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
stravaid=stravaid, userid=userid))
return 1

View File

@@ -0,0 +1,24 @@
{% extends "newbase.html" %}
{% load static %}
{% load rowerfilters %}
{% block title %}Export Workouts{% endblock %}
{% block main %}
<div class="container mt-5">
<div class="row">
<div class="col-md-8 offset-md-2">
<h2>Export All Your Workouts</h2>
<p>Select a date range to export your workouts as a ZIP file containing individual CSV files.</p>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input name="daterange" type="submit" value="Export Workouts">
</form>
</div>
</div>
</div>
{% endblock %}

View File

@@ -7,6 +7,10 @@
{% block main %}
<h1>Import and Export Settings for {{ rower.user.first_name }} {{ rower.user.last_name }}</h1>
<p>
<a href="/rowers/workouts/alluserworkouts/">Export all workouts</a>
</p>
<form enctype="multipart/form-data" action="" method="post">
{% csrf_token %}
<ul class="main-content">

View File

@@ -0,0 +1,11 @@
{% extends "emailbase.html" %}
{% block body %}
<p>
You can download the file {{ filename }} from the following link: {{ download_url }}. The file will be deleted after downloading, so please make sure to download it as soon as possible.
</p>
<p>
Best Regards, the Rowsandall Team
</p>
{% endblock %}

Binary file not shown.

View File

@@ -432,6 +432,10 @@ urlpatterns = [
views.graphs_view, name='graphs_view'),
re_path(r'^createmarkerworkouts/user/(?P<userid>\d+)/$', views.create_marker_workouts_view,
name='create_marker_workouts_view'),
re_path(r'^workouts/alluserworkouts/$', views.export_all_workouts_zip_view,
name='workout_export_all_workouts_zip_view'),
re_path(r'^workouts/download/$', views.download_zip_file_view,
name='download_zip_file_view'),
re_path(r'^createmarkerworkouts/$', views.create_marker_workouts_view,
name='create_marker_workouts_view'),
re_path(r'^goldmedalscores/$', views.goldmedalscores_view,

View File

@@ -252,6 +252,53 @@ def workout_csvemail_view(request, id=0):
return response
import io
# Export all workouts as ZIP file with individual CSV files
@login_required()
def export_all_workouts_zip_view(request):
from datetime import datetime
r = getrower(request.user)
if request.method == 'GET':
form = DateRangeForm()
elif request.method == 'POST':
form = DateRangeForm(request.POST)
if not form.is_valid():
messages.error(request, "Invalid date range. Please try again.")
return render(request, "export_workouts_daterange.html", {'form': form})
startdate = form.cleaned_data['startdate']
enddate = form.cleaned_data['enddate']
myqueue(queuehigh, email_all_user_workouts_zip, r, startdate, enddate)
successmessage = "A download link will be sent to you per email"
messages.info(request, successmessage)
# return to export settings view
return render(request, "export_workouts_daterange.html", {'form': form})
def download_zip_file_view(request):
# This view would be called when the user clicks the download link in the email
zip_file_path = request.GET.get('file')
print("Requested ZIP file path:", zip_file_path) # Debugging statement
# add media folder
zip_file_path = os.path.join(settings.MEDIA_ROOT, zip_file_path)
if not zip_file_path or not os.path.exists(zip_file_path):
messages.error(request, "The requested file does not exist.")
return HttpResponseRedirect(reverse('workouts_view'))
with open(zip_file_path, 'rb') as f:
response = HttpResponse(f.read(), content_type='application/zip')
response['Content-Disposition'] = f'attachment; filename="{os.path.basename(zip_file_path)}"'
# remove the file after sending
os.remove(zip_file_path)
return response
# Get Workout CSV file and send it to user's email address
@login_required()
@@ -276,3 +323,5 @@ def workout_csvtoadmin_view(request, id=0): # pragma: no cover
response = HttpResponseRedirect(url)
return response

View File

@@ -254,6 +254,7 @@ from rowers.rows import handle_uploaded_file, handle_uploaded_image
from rowers.plannedsessions import *
from rowers.tasks import handle_makeplot, handle_otwsetpower, handle_sendemailtcx, handle_sendemailcsv
from rowers.tasks import (
email_all_user_workouts_zip,
handle_intervals_updateworkout,
handle_post_workout_api,
handle_sendemail_newftp,