diff --git a/app/authentication/templates/authentication/login.html b/app/authentication/templates/authentication/login.html index f735a2e..bd3ba00 100644 --- a/app/authentication/templates/authentication/login.html +++ b/app/authentication/templates/authentication/login.html @@ -1,35 +1,58 @@ {% extends 'core/base.html' %} +{% load static %} +{% load widget_tweaks %} +{% block title %} + Medwings | Log In +{% endblock title %} + {% block content %}
- {% if form.errors %} -
-

Your username and password didn't match. Please try again.

-
- {% endif %} - - {% if next %} -
- {% if user.is_authenticated %} -

Your account doesn't have access to this page. To proceed, - please login with an account that has access.

- {% else %} -

Please log in to see this page.

- {% endif %} -
- {% endif %} - +

Log In

{% csrf_token %} -
+
Please enter your login details -
-
{{ form.username.label_tag }}
-
{{ form.username }}
-
{{ form.password.label_tag }}
-
{{ form.password }}
- + + {% if form.non_field_errors %} +
+ {% for error in form.non_field_errors %} +

{{ error }}

+ {% endfor %} +
+ {% endif %} + +
+
+ {% render_field form.username|add_error_class:"error" %} + + {% if form.username.errors %} +
+ {% for error in form.username.errors %} +

{{ error }}

+ {% endfor %} +
+ {% endif %} +
+ +
+ {% render_field form.password|add_error_class:"error" %} + + {% if form.password.errors %} +
+ {% for error in form.password.errors %} +

{{ error }}

+ {% endfor %} +
+ {% endif %} +
- + + +
diff --git a/app/core/settings.py b/app/core/settings.py index 8724143..ad5094a 100644 --- a/app/core/settings.py +++ b/app/core/settings.py @@ -29,6 +29,7 @@ ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ + 'widget_tweaks', 'core', 'authentication', 'medwings', diff --git a/app/gotify/migrations/0001_initial.py b/app/gotify/migrations/0001_initial.py index 28c0423..caaa452 100644 --- a/app/gotify/migrations/0001_initial.py +++ b/app/gotify/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.3 on 2023-07-29 18:28 +# Generated by Django 4.2.3 on 2023-07-30 21:15 from django.conf import settings from django.db import migrations, models diff --git a/app/medwings/forms.py b/app/medwings/forms.py index 9f18171..6c3d8d6 100644 --- a/app/medwings/forms.py +++ b/app/medwings/forms.py @@ -1,6 +1,6 @@ from django import forms -from medwings.models import Profile +from medwings.models import Profile, RespirationScoreRecord class ProfileForm(forms.ModelForm): @@ -10,3 +10,9 @@ class ProfileForm(forms.ModelForm): widgets = { 'date_of_birth': forms.DateInput(attrs={'type': 'date'}), } + + +class RespirationScoreForm(forms.ModelForm): + class Meta: + model = RespirationScoreRecord + fields = ['value_severity'] diff --git a/app/medwings/migrations/0001_initial.py b/app/medwings/migrations/0001_initial.py index 7a90529..5711d5c 100644 --- a/app/medwings/migrations/0001_initial.py +++ b/app/medwings/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.3 on 2023-07-29 18:28 +# Generated by Django 4.2.3 on 2023-07-30 21:15 from django.conf import settings from django.db import migrations, models @@ -79,7 +79,8 @@ class Migration(migrations.Migration): ('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')), + ('respiration_score_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='medwings.respirationscorerecord')), + ('spo2_level_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 b80c013..b362247 100644 --- a/app/medwings/models.py +++ b/app/medwings/models.py @@ -119,3 +119,5 @@ class MewsRecord(models.Model): mews_value += 0 else: mews_value += 2 + + return mews_value diff --git a/app/medwings/templates/medwings/mews-continue.html b/app/medwings/templates/medwings/mews-continue.html new file mode 100644 index 0000000..2bc9479 --- /dev/null +++ b/app/medwings/templates/medwings/mews-continue.html @@ -0,0 +1,191 @@ +{% extends 'core/base.html' %} +{% load static %} +{% load widget_tweaks %} +{% block title %} + Medwings | Take a measurement +{% endblock title %} + +{% block content %} +
+

Record your health status

+
+
+

Blood Pressure (systolic)

+
+
+ + +
+
+

Body Temperature

+
+
+ + +
+
+

Heart Rate

+
+
+ + +
+
+

Blood Oxygenation

+
+
+ + +
+
+

Respiration Score

+
+
+ + +
+
+

MEWS

+
+
+ +
+
+ +{% endblock content %} diff --git a/app/medwings/templates/medwings/mews-init.html b/app/medwings/templates/medwings/mews-init.html new file mode 100644 index 0000000..932c853 --- /dev/null +++ b/app/medwings/templates/medwings/mews-init.html @@ -0,0 +1,43 @@ +{% extends 'core/base.html' %} +{% load static %} +{% load widget_tweaks %} +{% block title %} + Medwings | Take a measurement +{% endblock title %} + +{% block content %} +
+

Record your health status

+
+ {% csrf_token %} +
+ To get started, please answer the following question: + + {% if respiration_score_form.non_field_errors %} +
+ {% for error in respiration_score_form.non_field_errors %} +

{{ error }}

+ {% endfor %} +
+ {% endif %} + + +
+ + {% render_field respiration_score_form.value_severity|add_error_class:"error" %} +
+ {% if respiration_score_form.value_severity.errors %} +
+ {% for error in respiration_score_form.value_severity.errors %} +

{{ error }}

+ {% endfor %} +
+ {% endif %} + + +
+
+
+{% endblock content %} diff --git a/app/medwings/urls.py b/app/medwings/urls.py index bdea7cd..7a2630a 100644 --- a/app/medwings/urls.py +++ b/app/medwings/urls.py @@ -5,5 +5,7 @@ from . import views urlpatterns = [ path("", views.index, name="home"), path("dashboard/", views.dashboard, name="dashboard"), + path("mews/init/", views.mews_init, name="mews-init"), + path("mews/continue/", views.mews_continue, name="mews-continue"), path("mews/status/", views.mews_status, name="mews-status"), ] diff --git a/app/medwings/views.py b/app/medwings/views.py index f945250..ad60a89 100644 --- a/app/medwings/views.py +++ b/app/medwings/views.py @@ -1,12 +1,13 @@ from datetime import timedelta -from django.shortcuts import render +from django.shortcuts import redirect, render from django.http import HttpResponse, JsonResponse from django.contrib.auth.decorators import login_required from django.views.decorators.http import require_http_methods from django.utils import timezone from . import models +from . import forms @require_http_methods(["GET"]) @@ -14,10 +15,41 @@ def index(request): return render(request, 'medwings/index.html') +@login_required @require_http_methods(["GET"]) def dashboard(request): return render(request, 'medwings/dashboard.html') + +@login_required +@require_http_methods(["GET", "POST"]) +def mews_init(request): + if request.method == 'POST': + respiration_score_form = forms.RespirationScoreForm(request.POST) + + if respiration_score_form.is_valid(): + respiration_score = respiration_score_form.save(commit=False) + respiration_score.recorded = timezone.now() + respiration_score.user = request.user + respiration_score.save() + + return redirect('mews-continue') + + else: + respiration_score_form = forms.RespirationScoreForm() + + context = { + 'respiration_score_form': respiration_score_form, + } + + return render(request, 'medwings/mews-init.html', context) + + +@login_required +def mews_continue(request): + return render(request, 'medwings/mews-continue.html') + + @require_http_methods(["GET"]) def mews_status(request): if not request.user.is_authenticated: @@ -60,6 +92,7 @@ def mews_status(request): body_temp_record=body_temp_record, heart_rate_record=heart_rate_record, respiration_score_record=respiration_score_record, + spo2_level_record=spo2_level_record ) mews_record.save() data['mews_value'] = mews_record.value_n diff --git a/app/requirements.txt b/app/requirements.txt index d4dd1f1..72d30a5 100644 --- a/app/requirements.txt +++ b/app/requirements.txt @@ -2,6 +2,8 @@ asgiref==3.7.2 certifi==2023.7.22 charset-normalizer==3.2.0 Django==4.2.3 +django-widget-tweaks==1.4.12 +djangorestframework==3.14.0 idna==3.4 psycopg==3.1.9 psycopg-binary==3.1.9 diff --git a/app/withings/migrations/0001_initial.py b/app/withings/migrations/0001_initial.py index 5073b30..c6e8861 100644 --- a/app/withings/migrations/0001_initial.py +++ b/app/withings/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.3 on 2023-07-29 18:35 +# Generated by Django 4.2.3 on 2023-07-30 21:15 from django.conf import settings from django.db import migrations, models diff --git a/assets/css/styles.css b/assets/css/styles.css index f4915d3..68871d8 100644 --- a/assets/css/styles.css +++ b/assets/css/styles.css @@ -47,7 +47,22 @@ input[type="submit"] { @apply hover:drop-shadow-xl; } -a.btn { +input.error { + @apply border border-failure; +} + +fieldset { + @apply border border-accent p-4; +} +legend { + @apply text-sm text-accent-600 px-2; +} + +a { + @apply underline text-primary-300; +} + +.btn { text-align: center; @apply rounded rounded-lg drop-shadow-md px-4 py-2; @apply bg-accent-600; @@ -55,7 +70,7 @@ a.btn { @apply hover:drop-shadow-xl; } -a.btn-outline { +.btn-outline { text-align: center; @apply rounded rounded-lg drop-shadow-md px-4 py-2; @apply text-accent-600 bg-accent-600/10; @@ -93,10 +108,68 @@ div.status-message { } div.status-message.error { - @apply bg-failure/50; + @apply font-semibold bg-failure/50 border border-failure; } div.call-to-action-box { @apply bg-gradient-to-r from-secondary-300/75 to-secondary-500/75; @apply rounded-md py-4 px-6; } + +.loader--loading { + animation: loading 2s infinite linear; +} +.loader--finished { + animation: finished 1.5s ease-in both; +} +.loader--value { + animation: loaded 1s ease-out both; +} + +@keyframes loading { + 0% { + transform: scaleX(0%) translateX(-100%); + transform-origin: left; + } + + 50% { + transform: scaleX(50%); + transform-origin: center; + } + + 100% { + transform: scaleX(0%) translateX(100%); + transform-origin: right; + } +} + +@keyframes finished { + 0% { + transform: scaleX(0%); + transform-origin: center; + } + + 50% { + transform: scaleX(100%); + transform-origin: center; + opacity: 1; + } + + 100% { + transform: scaleX(100%) translateX(1000%); + transform-origin: right; + opacity: 0; + } +} + +@keyframes loaded { + 0% { + transform: translateX(1000%); + opacity: 0; + } + + 100% { + transform: translateX(0%); + opacity: 1; + } +} diff --git a/assets/ts/index.ts b/assets/ts/index.ts index 877f85d..b833fd8 100644 --- a/assets/ts/index.ts +++ b/assets/ts/index.ts @@ -1,10 +1,10 @@ const navbarToggleButton = document.querySelector('#navbarToggleButton'); -button.addEventListener('click', () => { - if (button.textContent === 'Click me') { - button.textContent = 'Clicked'; - } else { - button.textContent = 'Click me'; - } -}); +//button.addEventListener('click', () => { + //if (button.textContent === 'Click me') { + //button.textContent = 'Clicked'; + //} else { + //button.textContent = 'Click me'; + //} +//});