source: OpenWorkouts-current/ow/views/workout.py @ 5ec3a0b

currentfeature/docs
Last change on this file since 5ec3a0b was 5ec3a0b, checked in by borja <borja@…>, 5 years ago

Imported sources from the old python2-only repository:

  • Modified the code so it is python 3.6 compatible
  • Fixed deprecation warnings, pyramid 1.10.x supported now
  • Fixed deprecation warnings about some libraries, like pyramid-simpleform
  • Added pytest-pycodestyle and pytest-flakes for automatic checks on the source code files when running tests.
  • Added default pytest.ini setup to enforce some default parameters when running tests.
  • Cleaned up the code a bit, catched up with tests coverage.
  • Property mode set to 100644
File size: 7.2 KB
Line 
1from datetime import datetime, timedelta, time, timezone
2
3import gpxpy
4
5from pyramid.httpexceptions import HTTPFound, HTTPNotFound
6from pyramid.view import view_config
7from pyramid.response import Response
8from pyramid_simpleform import Form
9from pyramid_simpleform.renderers import FormRenderer
10
11from ..schemas.workout import (
12    UploadedWorkoutSchema,
13    ManualWorkoutSchema,
14    UpdateWorkoutSchema
15)
16from ..models.workout import Workout
17from ..models.user import User
18from ..utilities import slugify
19from ..catalog import get_catalog, reindex_object, remove_from_catalog
20
21
22@view_config(
23    context=User,
24    name='add-workout-manually',
25    renderer='ow:templates/add_manual_workout.pt')
26def add_workout_manually(context, request):
27    form = Form(request, schema=ManualWorkoutSchema(),
28                defaults={'duration_hours': '0',
29                          'duration_minutes': '0',
30                          'duration_seconds': '0'})
31
32    if 'submit' in request.POST and form.validate():
33        # exclude the three duration_* and start_* fields, so they won't be
34        # "bind" to the object, we do calculate both the full duration in
35        # seconds and the full datetime "start" and we save that
36        excluded = ['duration_hours', 'duration_minutes', 'duration_seconds',
37                    'start_date', 'start_time']
38        workout = form.bind(Workout(), exclude=excluded)
39        duration = timedelta(hours=form.data['duration_hours'],
40                             minutes=form.data['duration_minutes'],
41                             seconds=form.data['duration_seconds'])
42        workout.duration = duration
43        # create a time object first using the given hours and minutes
44        start_time = time(form.data['start_time'][0],
45                          form.data['start_time'][1])
46        # combine the given start date with the built time object
47        start = datetime.combine(form.data['start_date'], start_time,
48                                 tzinfo=timezone.utc)
49        workout.start = start
50        context.add_workout(workout)
51        return HTTPFound(location=request.resource_url(workout))
52
53    return {
54        'form': FormRenderer(form)
55    }
56
57
58@view_config(
59    context=User,
60    name='add-workout',
61    renderer='ow:templates/add_workout.pt')
62def add_workout(context, request):
63    """
64    Add a workout uploading a tracking file
65    """
66    form = Form(request, schema=UploadedWorkoutSchema())
67
68    if 'submit' in request.POST and form.validate():
69        # Grab some information from the tracking file
70        trackfile_ext = request.POST['tracking_file'].filename.split('.')[-1]
71        # Create a Workout instance based on the input from the form
72        workout = form.bind(Workout())
73        # Add the type of tracking file
74        workout.tracking_filetype = trackfile_ext
75        # Add basic info gathered from the file
76        workout.load_from_file()
77        # Add the workout
78        context.add_workout(workout)
79        return HTTPFound(location=request.resource_url(workout))
80
81    return {
82        'form': FormRenderer(form)
83    }
84
85
86@view_config(
87    context=Workout,
88    name='edit',
89    renderer='ow:templates/edit_manual_workout.pt')
90def edit_workout(context, request):
91    """
92    Edit manually an existing workout. This won't let users attach/update
93    tracking files, just manually edit of the values.
94    """
95    form = Form(request, schema=ManualWorkoutSchema(), obj=context)
96    if 'submit' in request.POST and form.validate():
97        # exclude the three duration_* and start_* fields, so they won't be
98        # "bind" to the object, we do calculate both the full duration in
99        # seconds and the full datetime "start" and we save that
100        excluded = ['duration_hours', 'duration_minutes', 'duration_seconds',
101                    'start_date', 'start_time']
102
103        form.bind(context, exclude=excluded)
104
105        duration = timedelta(hours=form.data['duration_hours'],
106                             minutes=form.data['duration_minutes'],
107                             seconds=form.data['duration_seconds'])
108        context.duration = duration
109
110        # create a time object first using the given hours and minutes
111        start_time = time(form.data['start_time'][0],
112                          form.data['start_time'][1])
113        # combine the given start date with the built time object
114        start = datetime.combine(form.data['start_date'], start_time,
115                                 tzinfo=timezone.utc)
116        context.start = start
117        catalog = get_catalog(context)
118        reindex_object(catalog, context)
119        return HTTPFound(location=request.resource_url(context))
120
121    return {
122        'form': FormRenderer(form)
123    }
124
125
126@view_config(
127    context=Workout,
128    name='update-from-file',
129    renderer='ow:templates/update_workout_from_file.pt')
130def update_workout_from_file(context, request):
131    form = Form(request, schema=UpdateWorkoutSchema())
132    if 'submit' in request.POST and form.validate():
133        # Grab some information from the tracking file
134        trackfile_ext = request.POST['tracking_file'].filename.split('.')[-1]
135        # Update the type of tracking file
136        context.tracking_filetype = trackfile_ext
137        form.bind(context)
138        # Override basic info gathered from the file
139        context.load_from_file()
140        catalog = get_catalog(context)
141        reindex_object(catalog, context)
142        return HTTPFound(location=request.resource_url(context))
143    return {
144        'form': FormRenderer(form)
145    }
146
147
148@view_config(
149    context=Workout,
150    name='delete',
151    renderer='ow:templates/delete_workout.pt')
152def delete_workout(context, request):
153    """
154    Delete a workout
155    """
156    if 'submit' in request.POST:
157        if request.POST.get('delete', None) == 'yes':
158            catalog = get_catalog(context)
159            remove_from_catalog(catalog, context)
160            del request.root[request.authenticated_userid][context.workout_id]
161            return HTTPFound(location=request.resource_url(request.root))
162    return {}
163
164
165@view_config(
166    context=Workout,
167    renderer='ow:templates/workout.pt')
168def workout(context, request):
169    """
170    Details page for a workout
171    """
172    start_point = {}
173    if context.has_gpx:
174        with context.tracking_file.open() as gpx_file:
175            gpx_contents = gpx_file.read()
176            gpx_contents = gpx_contents.decode('utf-8')
177            gpx = gpxpy.parse(gpx_contents)
178            if gpx.tracks:
179                track = gpx.tracks[0]
180                center_point = track.get_center()
181                start_point = {'latitude': center_point.latitude,
182                               'longitude': center_point.longitude,
183                               'elevation': center_point.elevation}
184    return {'start_point': start_point}
185
186
187@view_config(
188    context=Workout,
189    name='gpx')
190def workout_gpx(context, request):
191    """
192    Return a gpx file with the workout tracking information, if any.
193    For now, simply return the gpx file if it has been attached to the
194    workout.
195    """
196    if not context.has_gpx:
197        return HTTPNotFound()
198    # Generate a proper file name to suggest on the download
199    gpx_slug = slugify(context.title) + '.gpx'
200    return Response(
201        content_type='application/xml',
202        content_disposition='attachment; filename="%s"' % gpx_slug,
203        body_file=context.tracking_file.open())
Note: See TracBrowser for help on using the repository browser.