Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Withings data source work
- Loading branch information
1 parent
7736ecd
commit c359ee1
Showing
4 changed files
with
284 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
management/commands/pdk_status_withings_device_check_last_upload.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
# pylint: disable=line-too-long, no-member | ||
|
||
from django.core.management.base import BaseCommand | ||
from django.utils import timezone | ||
|
||
from ...decorators import handle_lock | ||
|
||
from ...models import DataPoint, DataSource, DataSourceAlert | ||
|
||
GENERATOR = 'pdk-withings-device' | ||
CRITICAL_DAYS = 2 | ||
WARNING_DAYS = 1 | ||
|
||
class Command(BaseCommand): | ||
help = 'Runs the Withings device upload status check to alert when a device sync is overdue.' | ||
|
||
@handle_lock | ||
def handle(self, *args, **options): # pylint: disable=too-many-branches, too-many-statements | ||
now = timezone.now() | ||
|
||
for source in DataSource.objects.all(): | ||
last_alert = DataSourceAlert.objects.filter(data_source=source, generator_identifier=GENERATOR, active=True).order_by('-created').first() | ||
|
||
last_upload = DataPoint.objects.filter(source=source.identifier, generator_identifier=GENERATOR).order_by('-created').first() | ||
|
||
alert_name = None | ||
alert_details = {} | ||
alert_level = 'info' | ||
|
||
delta = now - last_upload.created | ||
|
||
if last_upload is not None: | ||
if delta.days >= CRITICAL_DAYS: | ||
alert_name = 'Withings upload is critically overdue' | ||
alert_details['message'] = 'Latest Withings upload was ' + str(delta.days) + ' days ago.' | ||
alert_level = 'critical' | ||
|
||
elif delta.days >= WARNING_DAYS: | ||
alert_name = 'Withings upload is overdue' | ||
alert_details['message'] = 'Latest Withings upload was 1 day ago.' | ||
alert_level = 'warning' | ||
|
||
if alert_name is not None: | ||
if last_alert is None or last_alert.alert_name != alert_name or last_alert.alert_level != alert_level: | ||
if last_alert is not None: | ||
last_alert.active = False | ||
last_alert.updated = timezone.now() | ||
last_alert.save() | ||
|
||
new_alert = DataSourceAlert(alert_name=alert_name, data_source=source, generator_identifier=GENERATOR) | ||
new_alert.alert_level = alert_level | ||
new_alert.update_alert_details(alert_details) | ||
new_alert.created = timezone.now() | ||
new_alert.updated = timezone.now() | ||
new_alert.active = True | ||
|
||
new_alert.save() | ||
else: | ||
last_alert.updated = timezone.now() | ||
last_alert.update_alert_details(alert_details) | ||
|
||
last_alert.save() | ||
elif last_alert is not None: | ||
last_alert.updated = timezone.now() | ||
last_alert.active = False | ||
|
||
last_alert.save() | ||
else: | ||
alert_name = 'Withing data never uploaded' | ||
alert_details['message'] = 'No Withings data is available from this user.' | ||
|
||
if last_alert is None or last_alert.alert_name != alert_name or last_alert.alert_level != alert_level: | ||
if last_alert is not None: | ||
last_alert.active = False | ||
last_alert.updated = timezone.now() | ||
last_alert.save() | ||
|
||
new_alert = DataSourceAlert(alert_name=alert_name, data_source=source, generator_identifier=GENERATOR) | ||
new_alert.alert_level = alert_level | ||
new_alert.update_alert_details(alert_details) | ||
new_alert.created = timezone.now() | ||
new_alert.updated = timezone.now() | ||
new_alert.active = True | ||
|
||
new_alert.save() | ||
else: | ||
last_alert.updated = timezone.now() | ||
last_alert.update_alert_details(alert_details) | ||
|
||
last_alert.save() |
162 changes: 162 additions & 0 deletions
162
templates/pdk_wearable_withings_device_table_template.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
{% load mathfilters %} | ||
{% load passive_data_kit %} | ||
<div id="withings_tabs" style="display: none;"> | ||
<!-- Nav tabs --> | ||
<ul class="nav nav-tabs" role="tablist"> | ||
<li role="presentation" class="active"><a href="#activity" aria-controls="activity" role="tab" data-toggle="tab">Activity Measures</a></li> | ||
<li role="presentation"><a href="#intraday" aria-controls="intraday" role="tab" data-toggle="tab">Intraday Activity</a></li> | ||
<li role="presentation"><a href="#sleep" aria-controls="sleep" role="tab" data-toggle="tab">Sleep Measures</a></li> | ||
<li role="presentation"><a href="#body" aria-controls="body" role="tab" data-toggle="tab">Body Measures</a></li> | ||
</ul> | ||
|
||
<!-- Tab panes --> | ||
<div class="tab-content"> | ||
<div role="tabpanel" class="tab-pane active" id="activity"> | ||
<table id="activity_values_table" class="table-striped" data-toggle="table" data-sort-name="created" data-sort-order="desc" data-pagination="true" style="z-index: 10;"> | ||
<thead> | ||
<tr> | ||
<th data-sortable="true" data-field="created">Created</th> | ||
<th data-sortable="true">Distance</th> | ||
<th data-sortable="true">Steps</th> | ||
<th data-sortable="true">Intense Duration</th> | ||
<th data-sortable="true">Moderate Duration</th> | ||
<th data-sortable="true">Soft Duration</th> | ||
<th data-sortable="true">Active Calories</th> | ||
<th data-sortable="true">Total Calories</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{% for row in activity_values %} | ||
{% with props=row.fetch_properties %} | ||
<tr> | ||
<td> | ||
<span style="display: none;"> | ||
{{ row.created.isoformat }} | ||
</span> | ||
{{ row.created }} | ||
</td> | ||
<td>{{ props.distance }}m</td> | ||
<td>{{ props.steps }}</td> | ||
|
||
<td>{{ props.intense_activity_duration }}</td> | ||
<td>{{ props.moderate_activity_duration }}</td> | ||
<td>{{ props.soft_activity_duration }}</td> | ||
|
||
<td>{{ props.active_calories }}</td> | ||
<td>{{ props.total_calories }}</td> | ||
</tr> | ||
{% endwith %} | ||
{% endfor %} | ||
</tbody> | ||
</table> | ||
</div> | ||
<div role="tabpanel" class="tab-pane" id="intraday"> | ||
<table id="intraday_table" class="table-striped" data-toggle="table" data-sort-name="created" data-sort-order="desc" data-pagination="true" style="z-index: 10;"> | ||
<thead> | ||
<tr> | ||
<th data-sortable="true" data-field="created">Created</th> | ||
<th data-sortable="true">Distance</th> | ||
<th data-sortable="true">Steps</th> | ||
<th data-sortable="true">Activity Duration</th> | ||
<th data-sortable="true">Elevation Climbed</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{% for row in intraday_values %} | ||
{% with props=row.fetch_properties %} | ||
<tr> | ||
<td> | ||
{% with start_date=props.activity_start|to_datetime %} | ||
<span style="display: none;"> | ||
{{ start_date.isoformat }} | ||
</span> | ||
|
||
{{ start_date }} | ||
{% endwith %} | ||
</td> | ||
<td>{{ props.distance }}</td> | ||
<td>{{ props.steps }}</td> | ||
<td>{{ props.activity_duration }}</td> | ||
<td>{{ props.elevation_climbed }}</td> | ||
</tr> | ||
{% endwith %} | ||
{% endfor %} | ||
</tbody> | ||
</table> | ||
</div> | ||
<div role="tabpanel" class="tab-pane" id="sleep"> | ||
<table id="sleep_measures_table" class="table-striped" data-toggle="table" data-sort-name="created" data-sort-order="desc" data-pagination="true" style="z-index: 10;"> | ||
<thead> | ||
<tr> | ||
<th data-sortable="true" data-field="created">Created</th> | ||
<th data-sortable="true">State</th> | ||
<th data-sortable="true">Duration (Seconds)</th> | ||
<th data-sortable="true">Measurement Device</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{% for row in sleep_values %} | ||
{% with props=row.fetch_properties %} | ||
<tr> | ||
<td> | ||
{% with start_date=props.start_date|to_datetime %} | ||
<span style="display: none;"> | ||
{{ start_date.isoformat }} | ||
</span> | ||
|
||
{{ start_date }} -- {{ row.pk }} | ||
{% endwith %} | ||
</td> | ||
<td>{{ props.state }}</td> | ||
<td>{{ props.end_date|sub:props.start_date }}</td> | ||
|
||
<td>{{ props.measurement_device }}</td> | ||
</tr> | ||
{% endwith %} | ||
{% endfor %} | ||
</tbody> | ||
</table> | ||
</div> | ||
<div role="tabpanel" class="tab-pane" id="body"> | ||
|
||
<table id="body_table" class="table-striped" data-toggle="table" data-sort-name="created" data-sort-order="desc" data-pagination="true" style="z-index: 10;"> | ||
<thead> | ||
<tr> | ||
<th data-sortable="true" data-field="created">Created</th> | ||
<th data-sortable="true">Type</th> | ||
<th data-sortable="true">Category</th> | ||
<th data-sortable="true">Value</th> | ||
<th data-sortable="true">Status</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{% for row in body_values %} | ||
{% with props=row.fetch_properties %} | ||
<tr> | ||
<td> | ||
{% with start_date=props.measure_date|to_datetime %} | ||
<span style="display: none;"> | ||
{{ start_date.isoformat }} | ||
</span> | ||
|
||
{{ start_date }} | ||
{% endwith %} | ||
</td> | ||
<td>{{ props.measure_type }}</td> | ||
<td>{{ props.measure_category }}</td> | ||
<td>{{ props.measure_value }}</td> | ||
<td>{{ props.measure_status }}</td> | ||
</tr> | ||
{% endwith %} | ||
{% endfor %} | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<script> | ||
window.showValues = function() { | ||
$("#withings_tabs").show(); | ||
}; | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters