Skip to content

Commit

Permalink
resolve merge conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
eatyourpeas committed Jun 2, 2024
2 parents 8e5f5f5 + 1ec4af8 commit a331029
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 68 deletions.
5 changes: 3 additions & 2 deletions project/npda/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,16 @@
<link href="{% static 'tailwind.css' %}" rel="stylesheet">
<link href="https://fonts.cdnfonts.com/css/arial-2" rel="stylesheet">
<script src="https://unpkg.com/[email protected]/dist/htmx.js" integrity="sha384-l9bYT9SL4CAW0Hl7pAOpfRc18mys1b0wK4U8UtGnWOxPVbVMgrOdB+jyz/WY8Jue" crossorigin="anonymous"></script>
<script src="https://unpkg.com/[email protected]"></script>
<!-- not for production -->
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/full.min.css" rel="stylesheet" type="text/css" />
<style type="text/tailwindcss">
@layer components {
.rcpch-light-blue-btn {
@apply bg-rcpch_light_blue text-white font-semibold hover:text-white py-2 px-3 mt-20 border border-rcpch_light_blue hover:bg-rcpch_strong_blue hover:border-rcpch_strong_blue rounded-none;
@apply bg-rcpch_light_blue text-white font-semibold hover:text-white py-2 px-3 border border-rcpch_light_blue hover:bg-rcpch_strong_blue hover:border-rcpch_strong_blue rounded-none;
}
.rcpch-btn {
@apply text-white font-semibold hover:text-white py-2.5 px-3 mt-20 border border-rcpch_yellow_light_tint1 hover:bg-rcpch_yellow hover:border-rcpch_yellow rounded-none;
@apply text-white font-semibold hover:text-white py-2.5 px-3 border border-rcpch_yellow_light_tint1 hover:bg-rcpch_yellow hover:border-rcpch_yellow rounded-none;
}
.rcpch-input-text {
@apply shadow appearance-none border border-rcpch_light_blue border-4 focus:border-rcpch_pink hover:border-rcpch_pink w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none rounded-none;
Expand Down
32 changes: 18 additions & 14 deletions project/npda/templates/npda/visit_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
{% load npda_tags %}
{% csrf_token %}
{% block content %}
<div class="bg-rcpch_light_blue py-8">
<div class="w-full max-w-3xl mx-auto px-2 py-4 m-2 shadow-md bg-white font-montserrat">
<div class="flex justify-center bg-white py-8">
<div class="w-full mx-96 px-2 py-4 m-2 shadow-md bg-white font-montserrat">
<strong>{{title}}</strong>
<form id="update-form" method="post" {% if form_method == "create" %} action="{% url 'visit-create' patient_id %}" {% else %} action="{% url 'visit-update' patient_id=patient_id pk=visit.pk %}" {% endif %}>
{% csrf_token %}
Expand All @@ -13,9 +13,10 @@
<div class="md:w-1/3">
<label for="{{ field.id_for_label }}" class="block text-gray-700 font-bold md:text-center mb-1 md:mb-0 pr-4"><small>{{ field.label }}</small></label>
</div>
<div class="md:w-2/3">
<div class="flex space-between md:w-2/3">
{% if field.id_for_label == "id_visit_date" %}
<input type="date" id="{{ field.id_for_label }}" name="{{ field.html_name }}" class="input rcpch-input-text" {% if field.value %} value={{ field.value|date:"Y-m-d" }} {% elif form_method == "create" %} value={% today_date %} {% endif %}>
<input type="date" id="{{ field.id_for_label }}" name="{{ field.html_name }}" class="input rcpch-input-text" {% if field.value %} value={{ field.value|date:"Y-m-d" }} {% endif %}>
<button type='button' _="on click set #{{ field.id_for_label}}'s value to '{% today_date %}'" class="btn rcpch-btn bg-rcpch_light_blue border border-rcpch_light_blue hover:bg-rcpch_strong_blue hover:border-rcpch_strong_blue">Today</button>
{% endif %}
{% for error in field.errors %}
<p>
Expand All @@ -29,10 +30,10 @@

<div role="tablist" class="tabs tabs-bordered">
<input type="radio" name="my_tabs_1" role="tab" class="tab" aria-label="Routine&nbsp;Measurements" checked/>
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-none p-6">
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-none">
{% for field_category in form.categories %}
{% with field_category|colour_for_category as background_colour %}
{% if field_category == "Measurements" or field_category == "HBA1c" or field_category == "Treatment" or field_category == "CGM" %}
{% if field_category == "Measurements" or field_category == "HBA1c" or field_category == "Treatment" or field_category == "CGM" or field_category == "BP"%}
<div class="flex flex-col mb-6 {% if background_colour %} bg-{{background_colour}} {% endif %}">
{% for field in form %}
{% if field.field.category == field_category %}
Expand All @@ -48,7 +49,8 @@
{% endfor %}
</select>
{% elif field.field.widget|is_dateinput %}
<input type="date" id="{{ field.id_for_label }}" name="{{ field.html_name }}" class="input rcpch-input-text" {% if field.value %} value={{ field.value|date:"Y-m-d" }} {% elif form_method == "create" %} value={% today_date %} {% endif %}>
<input type="date" id="{{ field.id_for_label }}" name="{{ field.html_name }}" class="input rcpch-input-text" {% if field.value %} value={{ field.value|date:"Y-m-d" }} {% endif %}>
<button type='button' _="on click set #{{ field.id_for_label}}'s value to '{% today_date %}'" class="btn rcpch-btn bg-rcpch_light_blue border border-rcpch_light_blue hover:bg-rcpch_strong_blue hover:border-rcpch_strong_blue">Today</button>
{% else %}
<input type="text" id="{{ field.id_for_label }}" name="{{ field.html_name }}" class="input rcpch-input-text rounded-none" {% if field.field.required %} placeholder="Required" {% endif %} {% if field.value %} value={{ field.value }} {% elif field.value == 0 %} value="0.0" {% endif %}>
{% endif %}
Expand All @@ -71,18 +73,18 @@
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-none p-6">
{% for field_category in form.categories %}
{% with field_category|colour_for_category as background_colour %}
{% if field_category == "BP" or field_category == "Foot Care" or field_category == "DECS" or field_category == "ACR" or field_category == "Cholesterol" or field_category == "Thyroid" or field_category == "Coeliac" or field_category == "Psychology" or field_category == "Smoking" or field_category == "Dietician" or field_category == "Sick Day Rules" or field_category == "Immunisation (flu)" %}
<div class="collapse collapse-arrow my-2 bg-base-200">
{% if field_category == "Foot Care" or field_category == "DECS" or field_category == "ACR" or field_category == "Cholesterol" or field_category == "Thyroid" or field_category == "Coeliac" or field_category == "Psychology" or field_category == "Smoking" or field_category == "Dietician" or field_category == "Sick Day Rules" or field_category == "Immunisation (flu)" %}
<div class="collapse collapse-arrow my-2 bg-base-200 rounded-none {% if field_category in categories_with_errors %} bg-rcpch_red {% elif field_category in categories_without_errors %} bg-rcpch_pink {% else %} bg-rcpch_dark_blue {% endif %}">
<input type="radio" name="my-accordion-3" checked="checked" />
<div class="collapse-title text-xl font-medium">
<div class="collapse-title text-xl font-medium text-white">
<strong>{{field_category}}</strong>
</div>
<div class="collapse-content flex flex-col mb-6 {% if background_colour %} bg-{{background_colour}} {% endif %}">
<div class="collapse-content flex flex-col mb-6 text-lg {% if field_category in categories_with_errors %} bg-rcpch_red {% elif field_category in categories_without_errors %} bg-rcpch_pink {% else %} bg-rcpch_dark_blue {% endif %}">
{% for field in form %}
{% if field.field.category == field_category %}
<div class="flex flex-row my-2 mx-2">
<div class="flex items-center justify-center md:w-1/3">
<label for="{{ field.id_for_label }}" class="{% if background_colour == "rcpch_dark_blue" %} text-white-700 {% else %} text-gray-700 {% endif %} block font-bold md:text-center mb-1 md:mb-0 pr-4"><small>{{ field.label }}</small></label>
<label for="{{ field.id_for_label }}" class="text-white block font-bold mb-1 md:mb-0 pr-4 text-left"><small>{{ field.label }}</small></label>
</div>
<div class="flex space-between md:w-2/3">
{% if field.field.widget|is_select %}
Expand All @@ -92,7 +94,8 @@
{% endfor %}
</select>
{% elif field.field.widget|is_dateinput %}
<input type="date" id="{{ field.id_for_label }}" name="{{ field.html_name }}" class="input rcpch-input-text" {% if field.value %} value={{ field.value|date:"Y-m-d" }} {% elif form_method == "create" %} value={% today_date %} {% endif %}>
<input type="date" id="{{ field.id_for_label }}" name="{{ field.html_name }}" class="input rcpch-input-text" {% if field.value %} value={{ field.value|date:"Y-m-d" }} {% endif %}>
<button type='button' _="on click set #{{ field.id_for_label}}'s value to '{% today_date %}'" class="btn rcpch-btn bg-rcpch_light_blue border border-rcpch_light_blue hover:bg-rcpch_strong_blue hover:border-rcpch_strong_blue">Today</button>
{% else %}
<input type="text" id="{{ field.id_for_label }}" name="{{ field.html_name }}" class="input rcpch-input-text rounded-none" {% if field.field.required %} placeholder="Required" {% endif %} {% if field.value %} value={{ field.value }} {% elif field.value == 0 %} value="0.0" {% endif %}>
{% endif %}
Expand Down Expand Up @@ -133,6 +136,7 @@
</select>
{% elif field.field.widget|is_dateinput %}
<input type="date" id="{{ field.id_for_label }}" name="{{ field.html_name }}" class="input rcpch-input-text" {% if field.value %} value={{ field.value|date:"Y-m-d" }} {% endif %}>
<button type='button' _="on click set #{{ field.id_for_label}}'s value to '{% today_date %}'" class="btn rcpch-btn bg-rcpch_light_blue border border-rcpch_light_blue hover:bg-rcpch_strong_blue hover:border-rcpch_strong_blue">Today</button>
{% else %}
<input type="text" id="{{ field.id_for_label }}" name="{{ field.html_name }}" class="input rcpch-input-text rounded-none" {% if field.field.required %} placeholder="Required" {% endif %} {% if field.value %} value={{ field.value }} {% elif field.value == 0 %} value="0.0" {% endif %}>
{% endif %}
Expand Down Expand Up @@ -160,7 +164,7 @@
<button type="submit" value="Submit" class="btn rcpch-btn bg-rcpch_light_blue border border-rcpch_light_blue hover:bg-rcpch_strong_blue hover:border-rcpch_strong_blue">{{button_title}}</button>
<a class="btn rcpch-btn bg-rcpch_yellow_light_tint1 border border-rcpch_yellow_light_tint1 hover:bg-rcpch_yellow hover:border-rcpch_yellow" href="{% url 'patient_visits' patient_id=patient_id %}">Cancel</a>
{% if form_method == 'update' %}
<a class="btn rcpch-btn bg-rcpch_red text-white font-semibold hover:text-white py-2.5 px-3 mt-20 border border-rcpch_red hover:bg-rcpch_red_dark_tint hover:border-rcpch_red_dark_tint" href="{% url 'visit-delete' patient_id visit.pk %}">Delete</a>
<a class="btn rcpch-btn bg-rcpch_red text-white font-semibold hover:text-white py-2.5 px-3 border border-rcpch_red hover:bg-rcpch_red_dark_tint hover:border-rcpch_red_dark_tint" href="{% url 'visit-delete' patient_id visit.pk %}">Delete</a>
{% endif %}
</div>
</form>
Expand Down
43 changes: 22 additions & 21 deletions project/npda/templates/two_factor/_wizard_actions.html
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
{% load i18n %}

<div class="flex flex-col justify-center items-center">

<button type="submit" class="bg-rcpch_light_blue text-white font-semibold hover:text-white py-2.5 px-3 mt-5 border border-rcpch_light_blue hover:bg-rcpch_strong_blue hover:border-rcpch_strong_blue">
{% trans "Next" %}
</button>


<button type="submit"
class="bg-rcpch_light_blue text-white font-semibold hover:text-white py-2.5 px-3 mt-5 border border-rcpch_light_blue hover:bg-rcpch_strong_blue hover:border-rcpch_strong_blue">
{% trans "Next" %}
</button>

{% if wizard.steps.prev %}
<!-- <div> -->
<button
name="wizard_goto_step"
type="submit"
value="{{ wizard.steps.prev }}"
class="justify-center items-center bg-rcpch_light_blue text-white font-semibold hover:text-white py-2.5 px-3 mt-5 border border-rcpch_light_blue hover:bg-rcpch_strong_blue hover:border-rcpch_strong_blue"
>
Back
</button>
</div>
{% endif %}
<!-- </div> -->
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}"
class="justify-center items-center bg-rcpch_light_blue text-white font-semibold hover:text-white py-2.5 px-3 mt-5 border border-rcpch_light_blue hover:bg-rcpch_strong_blue hover:border-rcpch_strong_blue">
Back
</button>
</div>
{% endif %}

<!--IF NO PREV BUTTON -> MUST BE AT FIRST SIGNIN PAGE -->
{% if not wizard.steps.prev %}
<div class="flex flex-col justify-center items-center">
<a href="{% url 'password_reset' %}" class="bg-rcpch_light_blue text-white font-semibold hover:text-white py-2.5 px-3 mt-5 border border-rcpch_light_blue hover:bg-rcpch_strong_blue hover:border-rcpch_strong_blue">Reset Password</a>
<a href="{% url 'password_reset' %}"
class="bg-rcpch_light_blue text-white font-semibold hover:text-white py-2.5 px-3 mt-5 border border-rcpch_light_blue hover:bg-rcpch_strong_blue hover:border-rcpch_strong_blue">Reset
Password</a>
<div class="mt-5">
<div class="mt-2 bg-gray-100 border border-gray-200 text-sm text-gray-800 rounded-lg p-4 dark:bg-white/10 dark:border-white/20 dark:text-white font-montserrat" role="alert">
<span class="font-bold">NOTE</span> For users of the old platform, you will require a new username and password to access this new platform. Please contact your Lead Clinician or <a href="mailto:[email protected]">[email protected]</a> for assistance.
<div
class="mt-2 bg-gray-100 border border-gray-200 text-sm text-gray-800 rounded-lg p-4 dark:bg-white/10 dark:border-white/20 dark:text-white font-montserrat"
role="alert">
<span class="font-bold">NOTE</span> For users of the old platform, you will require a new username and password to
access this new platform. Please contact your Lead Clinician or <a
href="mailto:[email protected]">[email protected]</a> for assistance.
</div>
</div>
</div>
{% endif %}
{% endif %}
8 changes: 2 additions & 6 deletions project/npda/urls.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.urls import path, include
from django.contrib.auth.views import PasswordResetConfirmView, LogoutView
from django.contrib.auth.views import PasswordResetConfirmView
from django.contrib.auth import urls as auth_urls
from project.npda.views import (
VisitCreateView,
Expand All @@ -13,8 +13,6 @@
from .views import *

urlpatterns = [
path("captcha/", include("captcha.urls")),
path("account/", include(auth_urls)),
path("home", view=home, name="home"),
# Patient views
path("patients", view=PatientListView.as_view(), name="patients"),
Expand Down Expand Up @@ -68,7 +66,7 @@
view=NPDAUserDeleteView.as_view(),
name="npdauser-delete",
),
# authentication
# Authentication -> NOTE: 2FA is implemented in project-level URLS with tf_urls
path("captcha/", include("captcha.urls")),
path("account/", include(auth_urls)),
path(
Expand All @@ -84,6 +82,4 @@
),
name="password_reset_confirm",
),
path("account/login", view=RCPCHLoginView.as_view(), name="login"),
path("account/logout", view=LogoutView.as_view(), name="logout"),
]
50 changes: 50 additions & 0 deletions project/npda/views/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# python imports
import logging

from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.contrib.auth.decorators import login_required

# Logging setup
logger = logging.getLogger(__name__)


def login_and_otp_required():
"""
Must have verified via 2FA
"""

def decorator(view):
# First use login_required on decorator
login_required(view)

def wrapper(request, *args, **kwargs):
# Then, ensure 2fa verified
user = request.user

# Bypass 2fa if local dev, with warning message
if settings.DEBUG:
logger.warning(
"User %s has bypassed 2FA for %s as settings.DEBUG is %s",
user,
view,
settings.DEBUG,
)
return view(request, *args, **kwargs)

# Prevent unverified users
if not user.is_verified():
user_list = user.__dict__
epilepsy12_user = user_list["_wrapped"]
logger.info(
"User %s is unverified. Tried accessing %s",
epilepsy12_user,
view.__qualname__,
)
raise PermissionDenied("Unverified user")

return view(request, *args, **kwargs)

return wrapper

return decorator
13 changes: 9 additions & 4 deletions project/npda/views/home.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from django_otp.decorators import otp_required
from django.contrib import messages

from ..general_functions import csv_upload
from ..forms.upload import UploadFileForm
from ..models import Patient, Visit
from .decorators import login_and_otp_required


@login_required
@login_and_otp_required()
def home(request):

if request.user.is_verified():
file_uploaded = False
if request.method == "POST":
Expand All @@ -25,5 +26,9 @@ def home(request):
context = {"file_uploaded": file_uploaded, "form": form}
template = "home.html"
return render(request=request, template_name=template, context=context)

else:
return redirect(reverse("two_factor:profile"))
form = UploadFileForm()
context = {"file_uploaded": file_uploaded, "form": form}
template = "home.html"
return render(request=request, template_name=template, context=context)
44 changes: 44 additions & 0 deletions project/npda/views/mixins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Defines custom mixins used throughout our Class Based Views"""

import logging

from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.contrib.auth.mixins import AccessMixin

logger = logging.getLogger(__name__)


class LoginAndOTPRequiredMixin(AccessMixin):
"""
Mixin that ensures the user is logged in and has verified via OTP.
Bypassed in local development is user.is_superuser AND settings.DEBUG==True.
"""

def dispatch(self, request, *args, **kwargs):

# Check if the user is authenticated
if not request.user.is_authenticated:
return self.handle_no_permission()

# Check if the user is superuser and bypass 2FA in debug mode
if settings.DEBUG and request.user.is_superuser:
logger.warning(
"User %s has bypassed 2FA for %s as settings.DEBUG is %s and user is superuser",
request.user,
self.__class__.__name__,
settings.DEBUG,
)
return super().dispatch(request, *args, **kwargs)

# Check if the user is verified
if not request.user.is_verified():
logger.info(
"User %s is unverified. Tried accessing %s",
request.user,
self.__class__.__name__,
)
raise PermissionDenied()

return super().dispatch(request, *args, **kwargs)
Loading

0 comments on commit a331029

Please sign in to comment.