merge experimental #1
@ -33,8 +33,10 @@ ALLOWED_HOSTS = []
|
|||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'core',
|
'core',
|
||||||
'authentication.apps.AuthenticationConfig',
|
'authentication',
|
||||||
'medwings.apps.MedwingsConfig',
|
'medwings',
|
||||||
|
'withings',
|
||||||
|
'gotify',
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
|
0
app/gotify/__init__.py
Normal file
0
app/gotify/__init__.py
Normal file
3
app/gotify/admin.py
Normal file
3
app/gotify/admin.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
6
app/gotify/apps.py
Normal file
6
app/gotify/apps.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class GotifyConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'gotify'
|
32
app/gotify/migrations/0001_initial.py
Normal file
32
app/gotify/migrations/0001_initial.py
Normal file
@ -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')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
0
app/gotify/migrations/__init__.py
Normal file
0
app/gotify/migrations/__init__.py
Normal file
14
app/gotify/models.py
Normal file
14
app/gotify/models.py
Normal file
@ -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")
|
3
app/gotify/tests.py
Normal file
3
app/gotify/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
3
app/gotify/views.py
Normal file
3
app/gotify/views.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
@ -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
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
import medwings.validators
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@ -9,24 +11,76 @@ class Migration(migrations.Migration):
|
|||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('auth', '0012_alter_user_first_name_max_length'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Question',
|
name='BloodPressureRecord',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('question_text', models.CharField(max_length=200)),
|
('recorded', models.DateTimeField(validators=[medwings.validators.AbstractRecordValidator.recorded], verbose_name='Time at which measurement was taken')),
|
||||||
('pub_date', models.DateTimeField(verbose_name='date published')),
|
('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(
|
migrations.CreateModel(
|
||||||
name='Choice',
|
name='BodyTempRecord',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('choice_text', models.CharField(max_length=200)),
|
('recorded', models.DateTimeField(validators=[medwings.validators.AbstractRecordValidator.recorded], verbose_name='Time at which measurement was taken')),
|
||||||
('votes', models.IntegerField(default=0)),
|
('value_celsius', models.PositiveIntegerField(validators=[medwings.validators.BodyTempRecordValidator.value_celsius], verbose_name='Body Temperature (°C)')),
|
||||||
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='medwings.question')),
|
('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)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
@ -1,11 +1,68 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
class Question(models.Model):
|
from . import validators
|
||||||
question_text = models.CharField(max_length=200)
|
|
||||||
pub_date = models.DateTimeField("date published")
|
|
||||||
|
|
||||||
|
|
||||||
class Choice(models.Model):
|
class Profile(models.Model):
|
||||||
question = models.ForeignKey(Question, on_delete=models.CASCADE)
|
SEX_FEMALE = "F"
|
||||||
choice_text = models.CharField(max_length=200)
|
SEX_MALE = "M"
|
||||||
votes = models.IntegerField(default=0)
|
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)
|
||||||
|
109
app/medwings/validators.py
Normal file
109
app/medwings/validators.py
Normal file
@ -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}")
|
0
app/withings/__init__.py
Normal file
0
app/withings/__init__.py
Normal file
3
app/withings/admin.py
Normal file
3
app/withings/admin.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
6
app/withings/apps.py
Normal file
6
app/withings/apps.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class WithingsConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'withings'
|
40
app/withings/migrations/0001_initial.py
Normal file
40
app/withings/migrations/0001_initial.py
Normal file
@ -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')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
0
app/withings/migrations/__init__.py
Normal file
0
app/withings/migrations/__init__.py
Normal file
20
app/withings/models.py
Normal file
20
app/withings/models.py
Normal file
@ -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")
|
3
app/withings/tests.py
Normal file
3
app/withings/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
3
app/withings/views.py
Normal file
3
app/withings/views.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
121
package-lock.json
generated
121
package-lock.json
generated
@ -8,9 +8,6 @@
|
|||||||
"name": "medwings",
|
"name": "medwings",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
|
||||||
"htmx.org": "^1.9.3"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"parcel": "^2.9.3",
|
"parcel": "^2.9.3",
|
||||||
@ -1847,9 +1844,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core": {
|
"node_modules/@swc/core": {
|
||||||
"version": "1.3.70",
|
"version": "1.3.71",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.70.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.71.tgz",
|
||||||
"integrity": "sha512-LWVWlEDLlOD25PvA2NEz41UzdwXnlDyBiZbe69s3zM0DfCPwZXLUm79uSqH9ItsOjTrXSL5/1+XUL6C/BZwChA==",
|
"integrity": "sha512-T8dqj+SV/S8laW/FGmKHhCGw1o4GRUvJ2jHfbYgEwiJpeutT9uavHvG02t39HJvObBJ52EZs/krGtni4U5928Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -1860,16 +1857,16 @@
|
|||||||
"url": "https://opencollective.com/swc"
|
"url": "https://opencollective.com/swc"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@swc/core-darwin-arm64": "1.3.70",
|
"@swc/core-darwin-arm64": "1.3.71",
|
||||||
"@swc/core-darwin-x64": "1.3.70",
|
"@swc/core-darwin-x64": "1.3.71",
|
||||||
"@swc/core-linux-arm-gnueabihf": "1.3.70",
|
"@swc/core-linux-arm-gnueabihf": "1.3.71",
|
||||||
"@swc/core-linux-arm64-gnu": "1.3.70",
|
"@swc/core-linux-arm64-gnu": "1.3.71",
|
||||||
"@swc/core-linux-arm64-musl": "1.3.70",
|
"@swc/core-linux-arm64-musl": "1.3.71",
|
||||||
"@swc/core-linux-x64-gnu": "1.3.70",
|
"@swc/core-linux-x64-gnu": "1.3.71",
|
||||||
"@swc/core-linux-x64-musl": "1.3.70",
|
"@swc/core-linux-x64-musl": "1.3.71",
|
||||||
"@swc/core-win32-arm64-msvc": "1.3.70",
|
"@swc/core-win32-arm64-msvc": "1.3.71",
|
||||||
"@swc/core-win32-ia32-msvc": "1.3.70",
|
"@swc/core-win32-ia32-msvc": "1.3.71",
|
||||||
"@swc/core-win32-x64-msvc": "1.3.70"
|
"@swc/core-win32-x64-msvc": "1.3.71"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@swc/helpers": "^0.5.0"
|
"@swc/helpers": "^0.5.0"
|
||||||
@ -1881,9 +1878,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-darwin-arm64": {
|
"node_modules/@swc/core-darwin-arm64": {
|
||||||
"version": "1.3.70",
|
"version": "1.3.71",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.70.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.71.tgz",
|
||||||
"integrity": "sha512-31+mcl0dgdRHvZRjhLOK9V6B+qJ7nxDZYINr9pBlqGWxknz37Vld5KK19Kpr79r0dXUZvaaelLjCnJk9dA2PcQ==",
|
"integrity": "sha512-xOm0hDbcO2ShwQu1CjLtq3fwrG9AvhuE0s8vtBc8AsamYExHmR8bo6GQHJUtfPG1FVPk5a8xoQSd1fs09FQjLg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -1897,9 +1894,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-darwin-x64": {
|
"node_modules/@swc/core-darwin-x64": {
|
||||||
"version": "1.3.70",
|
"version": "1.3.71",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.70.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.71.tgz",
|
||||||
"integrity": "sha512-GMFJ65E18zQC80t0os+TZvI+8lbRuitncWVge/RXmXbVLPRcdykP4EJ87cqzcG5Ah0z18/E0T+ixD6jHRisrYQ==",
|
"integrity": "sha512-9sbDXBWgM22w/3Ll5kPhXMPkOiHRoqwMOyxLJBfGtIMnFlh5O+NRN3umRerK3pe4Q6/7hj2M5V+crEHYrXmuxg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -1913,9 +1910,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
||||||
"version": "1.3.70",
|
"version": "1.3.71",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.70.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.71.tgz",
|
||||||
"integrity": "sha512-wjhCwS8LCiAq2VedF1b4Bryyw68xZnfMED4pLRazAl8BaUlDFANfRBORNunxlfHQj4V3x39IaiLgCZRHMdzXBg==",
|
"integrity": "sha512-boKdMZsfKvhBs0FDeqH7KQj0lfYe0wCtrL1lv50oYMEeLajY9o4U5xSmc61Sg4HRXjlbR6dlM2cFfL84t7NpAA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -1929,9 +1926,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-arm64-gnu": {
|
"node_modules/@swc/core-linux-arm64-gnu": {
|
||||||
"version": "1.3.70",
|
"version": "1.3.71",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.70.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.71.tgz",
|
||||||
"integrity": "sha512-9D/Rx67cAOnMiexvCqARxvhj7coRajTp5HlJHuf+rfwMqI2hLhpO9/pBMQxBUAWxODO/ksQ/OF+GJRjmtWw/2A==",
|
"integrity": "sha512-yDatyHYMiOVwhyIA/LBwknPs2CUtLYWEMzPZjgLc+56PbgPs3oiEbNWeVUND5onPrfDQgK7NK1y8JeiXZqTgGQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -1945,9 +1942,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-arm64-musl": {
|
"node_modules/@swc/core-linux-arm64-musl": {
|
||||||
"version": "1.3.70",
|
"version": "1.3.71",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.70.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.71.tgz",
|
||||||
"integrity": "sha512-gkjxBio7XD+1GlQVVyPP/qeFkLu83VhRHXaUrkNYpr5UZG9zZurBERT9nkS6Y+ouYh+Q9xmw57aIyd2KvD2zqQ==",
|
"integrity": "sha512-xAdCA0L/hoa0ULL5SR4sMZCxkWk7C90DOU7wJalNVG9qNWYICfq3G7AR0E9Ohphzqyahfb5QJED/nA7N0+XwbQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -1961,9 +1958,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-x64-gnu": {
|
"node_modules/@swc/core-linux-x64-gnu": {
|
||||||
"version": "1.3.70",
|
"version": "1.3.71",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.70.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.71.tgz",
|
||||||
"integrity": "sha512-/nCly+V4xfMVwfEUoLLAukxUSot/RcSzsf6GdsGTjFcrp5sZIntAjokYRytm3VT1c2TK321AfBorsi9R5w8Y7Q==",
|
"integrity": "sha512-j94qLXP/yqhu2afnABAq/xrJIU8TEqcNkp1TlsAeO3R2nVLYL1w4XX8GW71SPnXmd2bwF102c3Cfv/2ilf2y2A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -1977,9 +1974,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-x64-musl": {
|
"node_modules/@swc/core-linux-x64-musl": {
|
||||||
"version": "1.3.70",
|
"version": "1.3.71",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.70.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.71.tgz",
|
||||||
"integrity": "sha512-HoOsPJbt361KGKaivAK0qIiYARkhzlxeAfvF5NlnKxkIMOZpQ46Lwj3tR0VWohKbrhS+cYKFlVuDi5XnDkx0XA==",
|
"integrity": "sha512-YiyU848ql6dLlmt0BHccGAaZ36Cf61VzCAMDKID/gd72snvzWcMCHrwSRW0gEFNXHsjBJrmNl+SLYZHfqoGwUA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -1993,9 +1990,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-win32-arm64-msvc": {
|
"node_modules/@swc/core-win32-arm64-msvc": {
|
||||||
"version": "1.3.70",
|
"version": "1.3.71",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.70.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.71.tgz",
|
||||||
"integrity": "sha512-hm4IBK/IaRil+aj1cWU6f0GyAdHpw/Jr5nyFYLM2c/tt7w2t5hgb8NjzM2iM84lOClrig1fG6edj2vCF1dFzNQ==",
|
"integrity": "sha512-1UsJ+6hnIRe/PVdgDPexvgGaN4KpBncT/bAOqlWc9XC7KeBXAWcGA08LrPUz2Ei00DJXzR622IGZVEYOHNkUOw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -2009,9 +2006,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-win32-ia32-msvc": {
|
"node_modules/@swc/core-win32-ia32-msvc": {
|
||||||
"version": "1.3.70",
|
"version": "1.3.71",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.70.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.71.tgz",
|
||||||
"integrity": "sha512-5cgKUKIT/9Fp5fCA+zIjYCQ4dSvjFYOeWGZR3QiTXGkC4bGa1Ji9SEPyeIAX0iruUnKjYaZB9RvHK2tNn7RLrQ==",
|
"integrity": "sha512-KnuI89+zojR9lDFELdQYZpxzPZ6pBfLwJfWTSGatnpL1ZHhIsV3tK1jwqIdJK1zkRxpBwc6p6FzSZdZwCSpnJw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@ -2025,9 +2022,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-win32-x64-msvc": {
|
"node_modules/@swc/core-win32-x64-msvc": {
|
||||||
"version": "1.3.70",
|
"version": "1.3.71",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.70.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.71.tgz",
|
||||||
"integrity": "sha512-LE8lW46+TQBzVkn2mHBlk8DIElPIZ2dO5P8AbJiARNBAnlqQWu67l9gWM89UiZ2l33J2cI37pHzON3tKnT8f9g==",
|
"integrity": "sha512-Pcw7fFirpaBOZsU8fhO48ZCb7NxIjuLnLRPrHqWQ4Mapx1+w9ZNdGya2DKP9n8EAiUrJO20WDsrBNMT2MQSWkA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -2652,9 +2649,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.467",
|
"version": "1.4.473",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.467.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.473.tgz",
|
||||||
"integrity": "sha512-2qI70O+rR4poYeF2grcuS/bCps5KJh6y1jtZMDDEteyKJQrzLOEhFyXCLcHW6DTBjKjWkk26JhWoAi+Ux9A0fg==",
|
"integrity": "sha512-aVfC8+440vGfl06l8HKKn8/PD5jRfSnLkTTD65EFvU46igbpQRri1gxSzW9/+TeUlwYzrXk1sw867T96zlyECA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/entities": {
|
"node_modules/entities": {
|
||||||
@ -2697,9 +2694,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fast-glob": {
|
"node_modules/fast-glob": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
|
||||||
"integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==",
|
"integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nodelib/fs.stat": "^2.0.2",
|
"@nodelib/fs.stat": "^2.0.2",
|
||||||
@ -2927,14 +2924,6 @@
|
|||||||
"entities": "^3.0.1"
|
"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": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
@ -3660,9 +3649,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.26",
|
"version": "8.4.27",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.26.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz",
|
||||||
"integrity": "sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==",
|
"integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -4208,9 +4197,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
"version": "2.6.0",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz",
|
||||||
"integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==",
|
"integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/type-fest": {
|
"node_modules/type-fest": {
|
||||||
|
@ -23,6 +23,5 @@
|
|||||||
"tailwindcss": "^3.3.3"
|
"tailwindcss": "^3.3.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"htmx.org": "^1.9.3"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user