Private
Public Access
1
0

courses export and bulk export

This commit is contained in:
2026-03-16 10:24:03 +01:00
parent 18c7a6f7a7
commit 16291e135c
4 changed files with 336 additions and 0 deletions

View File

@@ -55,6 +55,17 @@
<li>
<a href="/rowers/list-courses/?liked=true">Courses I like</a>
</li>
{% if request.user.is_authenticated %}
<li class="grid_4">
<a href="{% url 'course_export_zip_view' %}"
target="download_frame"
class="btn btn-default">
Download my courses (ZIP)
</a> - With this button you can download all your courses in one ZIP file. The ZIP file contains one KML file per course, which you can open in Google Earth or CrewNerd and port to the new courses library supporting CrewNerd.
<iframe name="download_frame" style="display:none"></iframe>
</a>
<li>
{% endif %}
<li class="grid_4">
{% if courses %}
<p>

View File

@@ -1089,6 +1089,8 @@ urlpatterns = [
views.course_delete_view, name='course_delete_view'),
re_path(r'^courses/(?P<id>\d+)/downloadkml/$', views.course_kmldownload_view,
name='course_kmldownload_view'),
re_path(r'^courses/export-zip/$', views.course_export_zip_view,
name='course_export_zip_view'),
re_path(r'^courses/(?P<id>\d+)/$', views.course_view, name='course_view'),
re_path(r'^standards/(?P<id>\d+)/$',
views.standard_view, name='standard_view'),

View File

@@ -9,6 +9,9 @@ from rowers.plannedsessions import timefield_to_seconds_duration
from rowers.courses import getnearestraces, getnearestcourses,coursetokml, coursestokml
from random import sample
import io
import json
import zipfile
# landing page for challenges & courses
def courses_challenges_view(request):
@@ -3894,3 +3897,37 @@ def virtualevent_entry_edit_view(request, id=0, entryid=0):
'userid': r.user.id,
'race': race,
})
# download_courses view where logged in user can download
# their own courses (they are course_manager)
# should download a zip file with all courses as KML files
# and a manifest.json file with owned and liked course ID and name lists
@login_required()
def course_export_zip_view(request):
r = getrower(request.user)
owned_courses = GeoCourse.objects.filter(manager=r)
liked_courses = GeoCourse.objects.filter(followers=r).exclude(manager=r)
if not owned_courses and not liked_courses:
messages.info(request, "You don't have any courses to download")
return HttpResponseRedirect(reverse('courses_view'))
manifest = {
'version': 1,
'exported_at': timezone.now().isoformat(),
'owned': [{'id': str(c.id), 'name': c.name} for c in owned_courses],
'liked': [{'id': str(c.id), 'name': c.name} for c in liked_courses],
}
in_memory = io.BytesIO()
with zipfile.ZipFile(in_memory, 'w', zipfile.ZIP_DEFLATED) as zf:
zf.writestr('manifest.json', json.dumps(manifest, indent=2))
for course in owned_courses:
kml = coursetokml(course, cn=False)
safe_name = course.name.replace('/', '-').replace('\\', '-')
zf.writestr(f'courses/{safe_name}_{course.id}.kml', kml)
in_memory.seek(0)
response = HttpResponse(in_memory, content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename="my-rowsandall-courses.zip"'
return response