From 4dad4cf0686fefee5eb1a15e9d39f664d9bbc344 Mon Sep 17 00:00:00 2001 From: Julian Lobbes Date: Thu, 27 Jul 2023 16:37:18 +0200 Subject: [PATCH] feat: implement datamodel --- app/core/settings.py | 6 +- app/gotify/__init__.py | 0 app/gotify/admin.py | 3 + app/gotify/apps.py | 6 ++ app/gotify/migrations/0001_initial.py | 32 +++++++ app/gotify/migrations/__init__.py | 0 app/gotify/models.py | 14 +++ app/gotify/tests.py | 3 + app/gotify/views.py | 3 + app/medwings/migrations/0001_initial.py | 70 ++++++++++++-- app/medwings/models.py | 71 ++++++++++++-- app/medwings/validators.py | 109 +++++++++++++++++++++ app/withings/__init__.py | 0 app/withings/admin.py | 3 + app/withings/apps.py | 6 ++ app/withings/migrations/0001_initial.py | 40 ++++++++ app/withings/migrations/__init__.py | 0 app/withings/models.py | 20 ++++ app/withings/tests.py | 3 + app/withings/views.py | 3 + package-lock.json | 121 +++++++++++------------- package.json | 1 - 22 files changed, 430 insertions(+), 84 deletions(-) create mode 100644 app/gotify/__init__.py create mode 100644 app/gotify/admin.py create mode 100644 app/gotify/apps.py create mode 100644 app/gotify/migrations/0001_initial.py create mode 100644 app/gotify/migrations/__init__.py create mode 100644 app/gotify/models.py create mode 100644 app/gotify/tests.py create mode 100644 app/gotify/views.py create mode 100644 app/medwings/validators.py create mode 100644 app/withings/__init__.py create mode 100644 app/withings/admin.py create mode 100644 app/withings/apps.py create mode 100644 app/withings/migrations/0001_initial.py create mode 100644 app/withings/migrations/__init__.py create mode 100644 app/withings/models.py create mode 100644 app/withings/tests.py create mode 100644 app/withings/views.py diff --git a/app/core/settings.py b/app/core/settings.py index 651f311..a320b08 100644 --- a/app/core/settings.py +++ b/app/core/settings.py @@ -33,8 +33,10 @@ ALLOWED_HOSTS = [] INSTALLED_APPS = [ 'core', - 'authentication.apps.AuthenticationConfig', - 'medwings.apps.MedwingsConfig', + 'authentication', + 'medwings', + 'withings', + 'gotify', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', diff --git a/app/gotify/__init__.py b/app/gotify/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/gotify/admin.py b/app/gotify/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/app/gotify/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/app/gotify/apps.py b/app/gotify/apps.py new file mode 100644 index 0000000..da4dfd6 --- /dev/null +++ b/app/gotify/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class GotifyConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'gotify' diff --git a/app/gotify/migrations/0001_initial.py b/app/gotify/migrations/0001_initial.py new file mode 100644 index 0000000..e387c92 --- /dev/null +++ b/app/gotify/migrations/0001_initial.py @@ -0,0 +1,32 @@ +# Generated by Django 4.2.3 on 2023-07-27 14:35 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='GotifyUser', + fields=[ + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)), + ('id', models.PositiveIntegerField(verbose_name='Gotify User ID')), + ], + ), + migrations.CreateModel( + name='GotifyApplication', + fields=[ + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='gotify.gotifyuser')), + ('id', models.PositiveIntegerField(verbose_name='Gotify Application ID')), + ('token', models.CharField(max_length=256, verbose_name='Gotify Application Token')), + ], + ), + ] diff --git a/app/gotify/migrations/__init__.py b/app/gotify/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/gotify/models.py b/app/gotify/models.py new file mode 100644 index 0000000..523b844 --- /dev/null +++ b/app/gotify/models.py @@ -0,0 +1,14 @@ +from django.db import models + +from django.contrib.auth.models import User + + +class GotifyUser(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True) + id = models.PositiveIntegerField(verbose_name="Gotify User ID") + + +class GotifyApplication(models.Model): + user = models.OneToOneField(GotifyUser, on_delete=models.CASCADE, primary_key=True) + id = models.PositiveIntegerField(verbose_name="Gotify Application ID") + token = models.CharField(max_length=256, verbose_name="Gotify Application Token") diff --git a/app/gotify/tests.py b/app/gotify/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/app/gotify/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/app/gotify/views.py b/app/gotify/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/app/gotify/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/app/medwings/migrations/0001_initial.py b/app/medwings/migrations/0001_initial.py index 3e20f98..fb71fd4 100644 --- a/app/medwings/migrations/0001_initial.py +++ b/app/medwings/migrations/0001_initial.py @@ -1,7 +1,9 @@ -# Generated by Django 4.2.3 on 2023-07-20 13:28 +# Generated by Django 4.2.3 on 2023-07-27 14:35 +from django.conf import settings from django.db import migrations, models import django.db.models.deletion +import medwings.validators class Migration(migrations.Migration): @@ -9,24 +11,76 @@ class Migration(migrations.Migration): initial = True dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('auth', '0012_alter_user_first_name_max_length'), ] operations = [ migrations.CreateModel( - name='Question', + name='BloodPressureRecord', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('question_text', models.CharField(max_length=200)), - ('pub_date', models.DateTimeField(verbose_name='date published')), + ('recorded', models.DateTimeField(validators=[medwings.validators.AbstractRecordValidator.recorded], verbose_name='Time at which measurement was taken')), + ('value_systolic_mmhg', models.PositiveIntegerField(validators=[medwings.validators.BloodPressureRecordValidator.value_systolic_mmhg], verbose_name='Systolic Blood Pressure (mmhg)')), + ('value_diastolic_mmhg', models.PositiveIntegerField(validators=[medwings.validators.BloodPressureRecordValidator.value_diastolic_mmhg], verbose_name='Diastolic Blood Pressure (mmhg)')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], ), migrations.CreateModel( - name='Choice', + name='BodyTempRecord', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('choice_text', models.CharField(max_length=200)), - ('votes', models.IntegerField(default=0)), - ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='medwings.question')), + ('recorded', models.DateTimeField(validators=[medwings.validators.AbstractRecordValidator.recorded], verbose_name='Time at which measurement was taken')), + ('value_celsius', models.PositiveIntegerField(validators=[medwings.validators.BodyTempRecordValidator.value_celsius], verbose_name='Body Temperature (°C)')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='HeartRateRecord', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('recorded', models.DateTimeField(validators=[medwings.validators.AbstractRecordValidator.recorded], verbose_name='Time at which measurement was taken')), + ('value_bpm', models.PositiveIntegerField(validators=[medwings.validators.HeartRateRecordValidator.value_bpm], verbose_name='Heart Rate (bpm)')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Profile', + fields=[ + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)), + ('date_of_birth', models.DateField(validators=[medwings.validators.PersonValidator.date_of_birth], verbose_name='Date of birth')), + ('sex', models.CharField(choices=[('F', 'Female'), ('M', 'Male')], max_length=1, verbose_name='Sex assigned at birth')), + ], + ), + migrations.CreateModel( + name='Spo2LevelRecord', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('recorded', models.DateTimeField(validators=[medwings.validators.AbstractRecordValidator.recorded], verbose_name='Time at which measurement was taken')), + ('value_percent', models.PositiveIntegerField(validators=[medwings.validators.Spo2LevelRecordValidator.value_percent], verbose_name='SPO2 (%)')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='RespirationScoreRecord', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('recorded', models.DateTimeField(validators=[medwings.validators.AbstractRecordValidator.recorded], verbose_name='Time at which measurement was taken')), + ('value_severity', models.PositiveIntegerField(choices=[(0, 'No shortness of breath'), (1, 'A little shortness of breath'), (2, 'Severe shortness of breath')], validators=[medwings.validators.RespirationScoreRecordValidator.value_severity], verbose_name='Shortness Of Breath Severity')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='MewsRecord', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('recorded', models.DateTimeField(validators=[medwings.validators.AbstractRecordValidator.recorded], verbose_name='Time at which measurement was calculated')), + ('value_n', models.PositiveIntegerField(validators=[medwings.validators.MewsRecordValidator.value_n], verbose_name='Modified Early Warning Score')), + ('blood_pressure_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='medwings.bloodpressurerecord')), + ('body_temp_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='medwings.bodytemprecord')), + ('heart_rate_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='medwings.heartraterecord')), + ('respiration_score_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='medwings.spo2levelrecord')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], ), ] diff --git a/app/medwings/models.py b/app/medwings/models.py index deeef3a..7ea6093 100644 --- a/app/medwings/models.py +++ b/app/medwings/models.py @@ -1,11 +1,68 @@ from django.db import models +from django.contrib.auth.models import User -class Question(models.Model): - question_text = models.CharField(max_length=200) - pub_date = models.DateTimeField("date published") +from . import validators -class Choice(models.Model): - question = models.ForeignKey(Question, on_delete=models.CASCADE) - choice_text = models.CharField(max_length=200) - votes = models.IntegerField(default=0) +class Profile(models.Model): + SEX_FEMALE = "F" + SEX_MALE = "M" + SEX_CHOICES = [ + (SEX_FEMALE, "Female"), + (SEX_MALE, "Male") + ] + + user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True) + date_of_birth = models.DateField(validators=[validators.PersonValidator.date_of_birth], verbose_name="Date of birth") + sex = models.CharField(max_length=1, choices=SEX_CHOICES, verbose_name="Sex assigned at birth") + + +class BloodPressureRecord(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE) + recorded = models.DateTimeField(validators=[validators.BloodPressureRecordValidator.recorded], verbose_name="Time at which measurement was taken") + value_systolic_mmhg = models.PositiveIntegerField(validators=[validators.BloodPressureRecordValidator.value_systolic_mmhg], verbose_name="Systolic Blood Pressure (mmhg)") + value_diastolic_mmhg = models.PositiveIntegerField(validators=[validators.BloodPressureRecordValidator.value_diastolic_mmhg], verbose_name="Diastolic Blood Pressure (mmhg)") + + +class BodyTempRecord(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE) + recorded = models.DateTimeField(validators=[validators.BodyTempRecordValidator.recorded], verbose_name="Time at which measurement was taken") + value_celsius = models.PositiveIntegerField(validators=[validators.BodyTempRecordValidator.value_celsius], verbose_name="Body Temperature (\u00B0C)") + + +class HeartRateRecord(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE) + recorded = models.DateTimeField(validators=[validators.HeartRateRecordValidator.recorded], verbose_name="Time at which measurement was taken") + value_bpm = models.PositiveIntegerField(validators=[validators.HeartRateRecordValidator.value_bpm], verbose_name="Heart Rate (bpm)") + + +class RespirationScoreRecord(models.Model): + SEVERITY_NONE = 0 + SEVERITY_LOW = 1 + SEVERITY_HIGH = 2 + SEVERITY_CHOICES = [ + (SEVERITY_NONE, "No shortness of breath"), + (SEVERITY_LOW, "A little shortness of breath"), + (SEVERITY_HIGH, "Severe shortness of breath"), + ] + + user = models.ForeignKey(User, on_delete=models.CASCADE) + recorded = models.DateTimeField(validators=[validators.RespirationScoreRecordValidator.recorded], verbose_name="Time at which measurement was taken") + value_severity = models.PositiveIntegerField(choices=SEVERITY_CHOICES, validators=[validators.RespirationScoreRecordValidator.value_severity], verbose_name="Shortness Of Breath Severity") + + +class Spo2LevelRecord(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE) + recorded = models.DateTimeField(validators=[validators.Spo2LevelRecordValidator.recorded], verbose_name="Time at which measurement was taken") + value_percent = models.PositiveIntegerField(validators=[validators.Spo2LevelRecordValidator.value_percent], verbose_name="SPO2 (\u0025)") + + +class MewsRecord(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE) + recorded = models.DateTimeField(validators=[validators.MewsRecordValidator.recorded], verbose_name="Time at which measurement was calculated") + value_n = models.PositiveIntegerField(validators=[validators.MewsRecordValidator.value_n], verbose_name="Modified Early Warning Score") + + blood_pressure_record = models.ForeignKey(BloodPressureRecord, on_delete=models.CASCADE) + body_temp_record = models.ForeignKey(BodyTempRecord, on_delete=models.CASCADE) + heart_rate_record = models.ForeignKey(HeartRateRecord, on_delete=models.CASCADE) + respiration_score_record = models.ForeignKey(Spo2LevelRecord, on_delete=models.CASCADE) diff --git a/app/medwings/validators.py b/app/medwings/validators.py new file mode 100644 index 0000000..d940b15 --- /dev/null +++ b/app/medwings/validators.py @@ -0,0 +1,109 @@ +from datetime import date, datetime +from abc import ABC + +from django.core.exceptions import ValidationError + + +class DateValidator: + @staticmethod + def past_date(value: date, name: str = "date"): + if value > date.today(): + raise ValidationError(f"The {name} cannot be in the future.") + + @staticmethod + def past_datetime(value: datetime, name: str = "timestamp"): + if value > datetime.now(): + raise ValidationError(f"The {name} cannot be in the future.") + + +class PersonValidator: + @staticmethod + def date_of_birth(value): + DateValidator.past_date(value, "date of birth") + + +class AbstractRecordValidator(ABC): + @staticmethod + def recorded(value): + DateValidator.past_datetime(value, "time when the value was recorded") + + +class BloodPressureRecordValidator(AbstractRecordValidator): + MIN_VALUE_SYSTOLIC_MMHG = 0 + MAX_VALUE_SYSTOLIC_MMHG = 1000 + MIN_VALUE_DIASTOLIC_MMHG = 0 + MAX_VALUE_DIASTOLIC_MMHG = 1000 + + @staticmethod + def value_systolic_mmhg(value: int): + if value < BloodPressureRecordValidator.MIN_VALUE_SYSTOLIC_MMHG: + raise ValidationError(f"Systolic Blood Pressure cannot be below {BloodPressureRecordValidator.MIN_VALUE_SYSTOLIC_MMHG}") + if value > BloodPressureRecordValidator.MAX_VALUE_SYSTOLIC_MMHG: + raise ValidationError(f"Systolic Blood Pressure cannot be above {BloodPressureRecordValidator.MAX_VALUE_SYSTOLIC_MMHG}") + + @staticmethod + def value_diastolic_mmhg(value: int): + if value < BloodPressureRecordValidator.MIN_VALUE_DIASTOLIC_MMHG: + raise ValidationError(f"Diastolic Blood Pressure cannot be below {BloodPressureRecordValidator.MIN_VALUE_DIASTOLIC_MMHG}") + if value > BloodPressureRecordValidator.MAX_VALUE_DIASTOLIC_MMHG: + raise ValidationError(f"Diastolic Blood Pressure cannot be above {BloodPressureRecordValidator.MAX_VALUE_DIASTOLIC_MMHG}") + + +class BodyTempRecordValidator(AbstractRecordValidator): + MIN_VALUE_CELSIUS = 0 + MAX_VALUE_CELSIUS = 100 + + @staticmethod + def value_celsius(value: int): + if value < BodyTempRecordValidator.MIN_VALUE_CELSIUS: + raise ValidationError(f"Body Temperature cannot be below {BodyTempRecordValidator.MIN_VALUE_CELSIUS}") + if value > BodyTempRecordValidator.MAX_VALUE_CELSIUS: + raise ValidationError(f"Body Temperature cannot be above {BodyTempRecordValidator.MAX_VALUE_CELSIUS}") + + +class HeartRateRecordValidator(AbstractRecordValidator): + MIN_VALUE_BPM = 0 + MAX_VALUE_BPM = 1000 + + @staticmethod + def value_bpm(value: int): + if value < HeartRateRecordValidator.MIN_VALUE_BPM: + raise ValidationError(f"Heart Rate cannot be below {HeartRateRecordValidator.MIN_VALUE_BPM}") + if value > HeartRateRecordValidator.MAX_VALUE_BPM: + raise ValidationError(f"Heart Rate cannot be above {HeartRateRecordValidator.MAX_VALUE_BPM}") + + +class RespirationScoreRecordValidator(AbstractRecordValidator): + MIN_VALUE_SEVERITY = 0 + MAX_VALUE_SEVERITY = 2 + + @staticmethod + def value_severity(value: int): + if value < RespirationScoreRecordValidator.MIN_VALUE_SEVERITY: + raise ValidationError(f"Respiratory Inhibition Severity cannot be below {RespirationScoreRecordValidator.MIN_VALUE_SEVERITY}") + if value > RespirationScoreRecordValidator.MAX_VALUE_SEVERITY: + raise ValidationError(f"Respiratory Inhibition Severity cannot be above {RespirationScoreRecordValidator.MAX_VALUE_SEVERITY}") + + +class Spo2LevelRecordValidator(AbstractRecordValidator): + MIN_VALUE_PERCENT = 0 + MAX_VALUE_PERCENT = 100 + + @staticmethod + def value_percent(value: int): + if value < Spo2LevelRecordValidator.MIN_VALUE_PERCENT: + raise ValidationError(f"SPO2 cannot be below {Spo2LevelRecordValidator.MIN_VALUE_PERCENT}") + if value > Spo2LevelRecordValidator.MAX_VALUE_PERCENT: + raise ValidationError(f"SPO2 cannot be above {Spo2LevelRecordValidator.MAX_VALUE_PERCENT}") + + +class MewsRecordValidator(AbstractRecordValidator): + MIN_VALUE_N = 0 + MAX_VALUE_N = 100 + + @staticmethod + def value_n(value: int): + if value < MewsRecordValidator.MIN_VALUE_N: + raise ValidationError(f"MEWS cannot be below {MewsRecordValidator.MIN_VALUE_N}") + if value > MewsRecordValidator.MAX_VALUE_N: + raise ValidationError(f"MEWS cannot be above {MewsRecordValidator.MAX_VALUE_N}") diff --git a/app/withings/__init__.py b/app/withings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/withings/admin.py b/app/withings/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/app/withings/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/app/withings/apps.py b/app/withings/apps.py new file mode 100644 index 0000000..d1847ce --- /dev/null +++ b/app/withings/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class WithingsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'withings' diff --git a/app/withings/migrations/0001_initial.py b/app/withings/migrations/0001_initial.py new file mode 100644 index 0000000..9be9273 --- /dev/null +++ b/app/withings/migrations/0001_initial.py @@ -0,0 +1,40 @@ +# Generated by Django 4.2.3 on 2023-07-27 14:35 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='ApiAccount', + fields=[ + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)), + ('userid', models.PositiveIntegerField(verbose_name='Withings API User ID')), + ], + ), + migrations.CreateModel( + name='AccessToken', + fields=[ + ('account', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='withings.apiaccount')), + ('value', models.CharField(max_length=256, verbose_name='Withings API Access Token')), + ('expires', models.DateTimeField(verbose_name='Time of expiration')), + ], + ), + migrations.CreateModel( + name='RefreshToken', + fields=[ + ('account', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='withings.apiaccount')), + ('value', models.CharField(max_length=256, verbose_name='Withings API Refresh Token')), + ('expires', models.DateTimeField(verbose_name='Time of expiration')), + ], + ), + ] diff --git a/app/withings/migrations/__init__.py b/app/withings/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/withings/models.py b/app/withings/models.py new file mode 100644 index 0000000..7980ad9 --- /dev/null +++ b/app/withings/models.py @@ -0,0 +1,20 @@ +from django.db import models + +from django.contrib.auth.models import User + + +class ApiAccount(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True) + userid = models.PositiveIntegerField(verbose_name="Withings API User ID") + + +class AccessToken(models.Model): + account = models.OneToOneField(ApiAccount, on_delete=models.CASCADE, primary_key=True) + value = models.CharField(max_length=256, verbose_name="Withings API Access Token") + expires = models.DateTimeField(verbose_name="Time of expiration") + + +class RefreshToken(models.Model): + account = models.OneToOneField(ApiAccount, on_delete=models.CASCADE, primary_key=True) + value = models.CharField(max_length=256, verbose_name="Withings API Refresh Token") + expires = models.DateTimeField(verbose_name="Time of expiration") diff --git a/app/withings/tests.py b/app/withings/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/app/withings/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/app/withings/views.py b/app/withings/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/app/withings/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/package-lock.json b/package-lock.json index d19bdb4..8914626 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,6 @@ "name": "medwings", "version": "0.0.1", "license": "AGPL-3.0", - "dependencies": { - "htmx.org": "^1.9.3" - }, "devDependencies": { "autoprefixer": "^10.4.14", "parcel": "^2.9.3", @@ -1847,9 +1844,9 @@ } }, "node_modules/@swc/core": { - "version": "1.3.70", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.70.tgz", - "integrity": "sha512-LWVWlEDLlOD25PvA2NEz41UzdwXnlDyBiZbe69s3zM0DfCPwZXLUm79uSqH9ItsOjTrXSL5/1+XUL6C/BZwChA==", + "version": "1.3.71", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.71.tgz", + "integrity": "sha512-T8dqj+SV/S8laW/FGmKHhCGw1o4GRUvJ2jHfbYgEwiJpeutT9uavHvG02t39HJvObBJ52EZs/krGtni4U5928Q==", "dev": true, "hasInstallScript": true, "engines": { @@ -1860,16 +1857,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.3.70", - "@swc/core-darwin-x64": "1.3.70", - "@swc/core-linux-arm-gnueabihf": "1.3.70", - "@swc/core-linux-arm64-gnu": "1.3.70", - "@swc/core-linux-arm64-musl": "1.3.70", - "@swc/core-linux-x64-gnu": "1.3.70", - "@swc/core-linux-x64-musl": "1.3.70", - "@swc/core-win32-arm64-msvc": "1.3.70", - "@swc/core-win32-ia32-msvc": "1.3.70", - "@swc/core-win32-x64-msvc": "1.3.70" + "@swc/core-darwin-arm64": "1.3.71", + "@swc/core-darwin-x64": "1.3.71", + "@swc/core-linux-arm-gnueabihf": "1.3.71", + "@swc/core-linux-arm64-gnu": "1.3.71", + "@swc/core-linux-arm64-musl": "1.3.71", + "@swc/core-linux-x64-gnu": "1.3.71", + "@swc/core-linux-x64-musl": "1.3.71", + "@swc/core-win32-arm64-msvc": "1.3.71", + "@swc/core-win32-ia32-msvc": "1.3.71", + "@swc/core-win32-x64-msvc": "1.3.71" }, "peerDependencies": { "@swc/helpers": "^0.5.0" @@ -1881,9 +1878,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.3.70", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.70.tgz", - "integrity": "sha512-31+mcl0dgdRHvZRjhLOK9V6B+qJ7nxDZYINr9pBlqGWxknz37Vld5KK19Kpr79r0dXUZvaaelLjCnJk9dA2PcQ==", + "version": "1.3.71", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.71.tgz", + "integrity": "sha512-xOm0hDbcO2ShwQu1CjLtq3fwrG9AvhuE0s8vtBc8AsamYExHmR8bo6GQHJUtfPG1FVPk5a8xoQSd1fs09FQjLg==", "cpu": [ "arm64" ], @@ -1897,9 +1894,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.3.70", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.70.tgz", - "integrity": "sha512-GMFJ65E18zQC80t0os+TZvI+8lbRuitncWVge/RXmXbVLPRcdykP4EJ87cqzcG5Ah0z18/E0T+ixD6jHRisrYQ==", + "version": "1.3.71", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.71.tgz", + "integrity": "sha512-9sbDXBWgM22w/3Ll5kPhXMPkOiHRoqwMOyxLJBfGtIMnFlh5O+NRN3umRerK3pe4Q6/7hj2M5V+crEHYrXmuxg==", "cpu": [ "x64" ], @@ -1913,9 +1910,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.3.70", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.70.tgz", - "integrity": "sha512-wjhCwS8LCiAq2VedF1b4Bryyw68xZnfMED4pLRazAl8BaUlDFANfRBORNunxlfHQj4V3x39IaiLgCZRHMdzXBg==", + "version": "1.3.71", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.71.tgz", + "integrity": "sha512-boKdMZsfKvhBs0FDeqH7KQj0lfYe0wCtrL1lv50oYMEeLajY9o4U5xSmc61Sg4HRXjlbR6dlM2cFfL84t7NpAA==", "cpu": [ "arm" ], @@ -1929,9 +1926,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.3.70", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.70.tgz", - "integrity": "sha512-9D/Rx67cAOnMiexvCqARxvhj7coRajTp5HlJHuf+rfwMqI2hLhpO9/pBMQxBUAWxODO/ksQ/OF+GJRjmtWw/2A==", + "version": "1.3.71", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.71.tgz", + "integrity": "sha512-yDatyHYMiOVwhyIA/LBwknPs2CUtLYWEMzPZjgLc+56PbgPs3oiEbNWeVUND5onPrfDQgK7NK1y8JeiXZqTgGQ==", "cpu": [ "arm64" ], @@ -1945,9 +1942,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.3.70", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.70.tgz", - "integrity": "sha512-gkjxBio7XD+1GlQVVyPP/qeFkLu83VhRHXaUrkNYpr5UZG9zZurBERT9nkS6Y+ouYh+Q9xmw57aIyd2KvD2zqQ==", + "version": "1.3.71", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.71.tgz", + "integrity": "sha512-xAdCA0L/hoa0ULL5SR4sMZCxkWk7C90DOU7wJalNVG9qNWYICfq3G7AR0E9Ohphzqyahfb5QJED/nA7N0+XwbQ==", "cpu": [ "arm64" ], @@ -1961,9 +1958,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.3.70", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.70.tgz", - "integrity": "sha512-/nCly+V4xfMVwfEUoLLAukxUSot/RcSzsf6GdsGTjFcrp5sZIntAjokYRytm3VT1c2TK321AfBorsi9R5w8Y7Q==", + "version": "1.3.71", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.71.tgz", + "integrity": "sha512-j94qLXP/yqhu2afnABAq/xrJIU8TEqcNkp1TlsAeO3R2nVLYL1w4XX8GW71SPnXmd2bwF102c3Cfv/2ilf2y2A==", "cpu": [ "x64" ], @@ -1977,9 +1974,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.3.70", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.70.tgz", - "integrity": "sha512-HoOsPJbt361KGKaivAK0qIiYARkhzlxeAfvF5NlnKxkIMOZpQ46Lwj3tR0VWohKbrhS+cYKFlVuDi5XnDkx0XA==", + "version": "1.3.71", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.71.tgz", + "integrity": "sha512-YiyU848ql6dLlmt0BHccGAaZ36Cf61VzCAMDKID/gd72snvzWcMCHrwSRW0gEFNXHsjBJrmNl+SLYZHfqoGwUA==", "cpu": [ "x64" ], @@ -1993,9 +1990,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.3.70", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.70.tgz", - "integrity": "sha512-hm4IBK/IaRil+aj1cWU6f0GyAdHpw/Jr5nyFYLM2c/tt7w2t5hgb8NjzM2iM84lOClrig1fG6edj2vCF1dFzNQ==", + "version": "1.3.71", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.71.tgz", + "integrity": "sha512-1UsJ+6hnIRe/PVdgDPexvgGaN4KpBncT/bAOqlWc9XC7KeBXAWcGA08LrPUz2Ei00DJXzR622IGZVEYOHNkUOw==", "cpu": [ "arm64" ], @@ -2009,9 +2006,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.3.70", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.70.tgz", - "integrity": "sha512-5cgKUKIT/9Fp5fCA+zIjYCQ4dSvjFYOeWGZR3QiTXGkC4bGa1Ji9SEPyeIAX0iruUnKjYaZB9RvHK2tNn7RLrQ==", + "version": "1.3.71", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.71.tgz", + "integrity": "sha512-KnuI89+zojR9lDFELdQYZpxzPZ6pBfLwJfWTSGatnpL1ZHhIsV3tK1jwqIdJK1zkRxpBwc6p6FzSZdZwCSpnJw==", "cpu": [ "ia32" ], @@ -2025,9 +2022,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.3.70", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.70.tgz", - "integrity": "sha512-LE8lW46+TQBzVkn2mHBlk8DIElPIZ2dO5P8AbJiARNBAnlqQWu67l9gWM89UiZ2l33J2cI37pHzON3tKnT8f9g==", + "version": "1.3.71", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.71.tgz", + "integrity": "sha512-Pcw7fFirpaBOZsU8fhO48ZCb7NxIjuLnLRPrHqWQ4Mapx1+w9ZNdGya2DKP9n8EAiUrJO20WDsrBNMT2MQSWkA==", "cpu": [ "x64" ], @@ -2652,9 +2649,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.467", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.467.tgz", - "integrity": "sha512-2qI70O+rR4poYeF2grcuS/bCps5KJh6y1jtZMDDEteyKJQrzLOEhFyXCLcHW6DTBjKjWkk26JhWoAi+Ux9A0fg==", + "version": "1.4.473", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.473.tgz", + "integrity": "sha512-aVfC8+440vGfl06l8HKKn8/PD5jRfSnLkTTD65EFvU46igbpQRri1gxSzW9/+TeUlwYzrXk1sw867T96zlyECA==", "dev": true }, "node_modules/entities": { @@ -2697,9 +2694,9 @@ } }, "node_modules/fast-glob": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", - "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -2927,14 +2924,6 @@ "entities": "^3.0.1" } }, - "node_modules/htmx.org": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.3.tgz", - "integrity": "sha512-gsOttHnAcs/mXivSSYAIPF7hwksGjobb65MyZ46Csj2sJa1bS21Pfn5iag1DTm3GQ1Gxxx2/hlehKo6qfkW1Eg==", - "engines": { - "node": "15.x" - } - }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -3660,9 +3649,9 @@ } }, "node_modules/postcss": { - "version": "8.4.26", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.26.tgz", - "integrity": "sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==", + "version": "8.4.27", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", + "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", "dev": true, "funding": [ { @@ -4208,9 +4197,9 @@ "dev": true }, "node_modules/tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==", "dev": true }, "node_modules/type-fest": { diff --git a/package.json b/package.json index d77917c..d47672b 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,5 @@ "tailwindcss": "^3.3.3" }, "dependencies": { - "htmx.org": "^1.9.3" } }