M activities/jinja2/health.html => activities/jinja2/health.html +9 -1
@@ 1,7 1,7 @@
{% extends 'profile_main.html' %}
{% block profile %}
-<form action="TODO:store_measurement" method="post" enctype="multipart/form-data">
+<form action="{{ url('activities:health-measurement') }}" method="post" enctype="application/x-www-form-urlencoded">
{{ csrf_input }}
<p>
You can leave fields empty, only set fields will be stored.
@@ 27,6 27,14 @@
<input class="w3" type="number" name="max_hr" min="1" max="1000" step="1">
BPM
</p>
+ <p>
+ <label for="measurement_date" class="db pv2">Date of measurement</label>
+ <input class="" type="date" name="measurement_date" required
+ value="{{ now() | isodate}}">
+ <label for="measurement_time" class="dn">Time of measurement</label>
+ <input class="" type="time" name="measurement_time" required
+ value="{{ now() | isotime }}">
+ </p>
<input type=submit class="{{styles['action-button']}}" value="Add measurements">
</form>
{% endblock %}
M activities/urls.py => activities/urls.py +1 -0
@@ 8,6 8,7 @@ urlpatterns = [
path('', views.index, name='index'),
path('profile', views.profile, name='profile'),
path('health', views.health, name='health'),
+ path('health/measurement', views.health_measurement, name='health-measurement'),
path('trigger/schedule', views.schedule, name='trigger-schedule'),
path('upload', views.upload, name='upload'),
path('activities', views.overview, name='overview'),
M activities/views.py => activities/views.py +27 -2
@@ 1,5 1,5 @@
from django.http import HttpResponse
-from django.shortcuts import render
+from django.shortcuts import redirect, render
from django.core.exceptions import RequestDataTooBig
from django.views.decorators.csrf import csrf_exempt
@@ 8,7 8,7 @@ from django.contrib.auth.models import User
from django import forms
-from .models import Activity
+from .models import Activity, HealthData
from .services import UPLOAD_MAX_SIZE, create_activity
from .tasks import process_update_activity
from . import graph
@@ 23,6 23,14 @@ class UploadFileForm(forms.Form):
title = forms.CharField(max_length=1024, required=False)
content = forms.FileField()
+class HealthMeasurementForm(forms.Form):
+ body_weight = forms.FloatField(required=False)
+ body_height = forms.FloatField(required=False)
+ min_hr = forms.IntegerField(required=False)
+ max_hr = forms.IntegerField(required=False)
+ measurement_date = forms.DateField()
+ measurement_time = forms.TimeField()
+
@login_required
def index(request):
return render(request, 'upload.html')
@@ 35,6 43,23 @@ def profile(request):
def health(request):
return render(request, 'health.html')
+def save_healthdata(user, time, typ, value):
+ if value is not None:
+ m = HealthData(user=user, measurement_ts=time, measurement_type=typ, measurement_value=value)
+ m.save()
+
+@login_required
+def health_measurement(request):
+ if request.method == 'POST':
+ form = HealthMeasurementForm(request.POST)
+ if form.is_valid():
+ t = datetime.datetime.combine(form.cleaned_data['measurement_date'], form.cleaned_data['measurement_time'])
+ save_healthdata(request.user, t, 'WEIGHT', form.cleaned_data.get('body_weight'))
+ save_healthdata(request.user, t, 'HEIGHT', form.cleaned_data.get('body_height'))
+ save_healthdata(request.user, t, 'RESTING_HR', form.cleaned_data.get('min_hr'))
+ save_healthdata(request.user, t, 'MAX_HR', form.cleaned_data.get('max_hr'))
+ return redirect('activities:health')
+
def schedule(request):
unprocessed = Activity.objects.filter(status='PENDING').all()[:1000]
for act in unprocessed:
M trakka/jinja2.py => trakka/jinja2.py +13 -0
@@ 29,6 29,16 @@ def distance(meters: Optional[float]):
return '-'
return f'{meters/1000:0.1f} km'
+def isodate(d: Optional[dt.datetime]) -> str:
+ if d is None:
+ return '-'
+ return d.strftime('%Y-%m-%d')
+
+def isotime(d: Optional[dt.datetime]) -> str:
+ if d is None:
+ return '-'
+ return d.strftime('%H:%M')
+
def date(d: Optional[dt.datetime]) -> str:
if d is None:
return '-'
@@ 47,6 57,7 @@ def optional_int(x: Optional[Any]) -> str:
def environment(**options):
env = Environment(**options)
env.globals.update({
+ 'now': dt.datetime.now,
'static': static,
'url': reverse,
'styles': GLOBAL_STYLES,
@@ 56,6 67,8 @@ def environment(**options):
'datetime': datetime,
'distance': distance,
'duration': duration,
+ 'isodate': isodate,
+ 'isotime': isotime,
'optional_int': optional_int,
})
return env