Skip to content

Commit

Permalink
✨(dashboard) add consent model
Browse files Browse the repository at this point in the history
add consent models
add basic content admin
create core app
add entity and delivery_point models
add basic entity and delivery_point admin
add unitest for models consent and core
 update CHANGELOG.md
  • Loading branch information
ssorin committed Nov 22, 2024
1 parent c3f2359 commit 773e47b
Show file tree
Hide file tree
Showing 17 changed files with 587 additions and 12 deletions.
3 changes: 2 additions & 1 deletion src/dashboard/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ and this project adheres to
- add internationalization and language switcher
- add authentication system
- introduce new custom user model
- add consent base app
- add consent app with Consent model
- add core app with Entity and DeliveryPoint models

[unreleased]: https://github.com/MTES-MCT/qualicharge/compare/main...bootstrap-dashboard-project

2 changes: 1 addition & 1 deletion src/dashboard/Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ name = "pypi"
Django = "==5.1.3"
django-dsfr = "==1.4.3"
django-environ = "==0.11.2"
django-extensions = "==3.2.3"
gunicorn = "==23.0.0"
psycopg = {extras = ["pool", "binary"], version = "==3.2.3"}
whitenoise = "==6.8.2"

[dev-packages]
black = "==24.10.0"
django-extensions = "==3.2.3"
django-stubs = {extras = ["compatible-mypy"], version = "==5.1.1"}
djlint = "==1.36.1"
honcho = "==2.0.0"
Expand Down
20 changes: 10 additions & 10 deletions src/dashboard/Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions src/dashboard/apps/consent/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""Dashboard consent admin."""

from django.contrib import admin

from .models import Consent


@admin.register(Consent)
class ConsentAdmin(admin.ModelAdmin):
"""Consent admin."""

pass
88 changes: 88 additions & 0 deletions src/dashboard/apps/consent/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Generated by Django 5.1.3 on 2024-11-21 11:26

import django.db.models.deletion
import django.utils.timezone
import uuid
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
("qcd_core", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name="Consent",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
(
"created_at",
models.DateTimeField(
default=django.utils.timezone.now,
editable=False,
verbose_name="created at",
),
),
(
"updated_at",
models.DateTimeField(
blank=True, null=True, verbose_name="updated at"
),
),
(
"status",
models.CharField(
choices=[
("AWAITING", "Awaiting"),
("VALIDATED", "Validated"),
("REVOKED", "Revoked"),
],
default="AWAITING",
max_length=20,
verbose_name="status",
),
),
("start", models.DateTimeField(verbose_name="start date")),
("end", models.DateTimeField(verbose_name="end date")),
(
"revoked_at",
models.DateTimeField(
blank=True, null=True, verbose_name="revoked at"
),
),
(
"created_by",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to=settings.AUTH_USER_MODEL,
verbose_name="created by",
),
),
(
"delivery_point",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="qcd_core.deliverypoint",
),
),
],
options={
"ordering": ["delivery_point"],
},
),
]
61 changes: 61 additions & 0 deletions src/dashboard/apps/consent/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""Dashboard consent app models."""

from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from apps.auth.models import DashboardUser as User
from apps.core.models import DashboardBase, DeliveryPoint


class Consent(DashboardBase):
"""Represents the consent status for a given delivery point and user.
Attributes:
- AWAITING: Status indicating that the consent is awaiting validation.
- VALIDATED: Status indicating that the consent has been validated.
- REVOKED: Status indicating that the consent has been revoked.
- delivery_point (ForeignKey): relation to the delivery point associated with the
consent.
- created_by (ForeignKey): relation to the user giving the consent.
- status (CharField): storing the status of the consent, with choices constrained
by CONSENT_STATUS_CHOICE.
- start (DateTimeField): representing the start date of the consent validity.
- end (DateTimeField): representing the end date of the consent validity.
- revoked_at (DateTimeField): recording the revoked date of the consent, if any.
"""

AWAITING = "AWAITING"
VALIDATED = "VALIDATED"
REVOKED = "REVOKED"
CONSENT_STATUS_CHOICE = [
(AWAITING, _("Awaiting")),
(VALIDATED, _("Validated")),
(REVOKED, _("Revoked")),
]

delivery_point = models.ForeignKey(DeliveryPoint, on_delete=models.CASCADE)
created_by = models.ForeignKey(
User, on_delete=models.SET_NULL, null=True, verbose_name=_("created by")
)
status = models.CharField(
_("status"), max_length=20, choices=CONSENT_STATUS_CHOICE, default=AWAITING
)

# Validity period
start = models.DateTimeField(_("start date"))
end = models.DateTimeField(_("end date"))
revoked_at = models.DateTimeField(_("revoked at"), null=True, blank=True)

class Meta: # noqa: D106
ordering = ["delivery_point"]

def __str__(self): # noqa: D105
return f"{self.delivery_point} - {self.updated_at}: {self.status}"

def save(self, *args, **kwargs):
"""Update the revoked_at timestamps if the consent is revoked."""
if self.status == self.REVOKED:
self.revoked_at = timezone.now()
return super(Consent, self).save(*args, **kwargs)
1 change: 1 addition & 0 deletions src/dashboard/apps/consent/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Dashboard consent app tests."""
80 changes: 80 additions & 0 deletions src/dashboard/apps/consent/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""Dashboard consent models tests."""

from datetime import timedelta

import pytest
from django.contrib.auth import get_user_model
from django.utils import formats, timezone

from apps.consent.models import Consent
from apps.core.models import DeliveryPoint


@pytest.mark.django_db
def test_create_consent():
"""Tests the creation of a consent."""
# create user
User = get_user_model()
user1 = User.objects.create_user(username="user1", password="foo") # noqa: S106

# create delivery point
delivery_point = DeliveryPoint.objects.create(provider_id="provider_1234")

# create consent
consent = Consent.objects.create(
delivery_point=delivery_point,
created_by=user1,
start=timezone.now(),
end=timezone.now() + timedelta(days=90),
)
assert consent.delivery_point == delivery_point
assert consent.created_by == user1
assert consent.status == Consent.AWAITING
assert consent.revoked_at is None
assert consent.start is not None
assert consent.end is not None

# test consent.end is 90 days later than the consent.start
end_date = consent.start + timedelta(days=90)
consent_start = formats.date_format(end_date, "Y/m/d")
consent_end = formats.date_format(consent.end, "Y/m/d")
assert consent_start == consent_end

# test created_at and updated_at have been updated.
assert consent.created_at is not None
assert consent.updated_at is not None


@pytest.mark.django_db
def test_update_consent_status():
"""Tests updating a consent status."""
# create user
User = get_user_model()
user1 = User.objects.create_user(username="user1", password="foo") # noqa: S106

# create delivery point
delivery_point = DeliveryPoint.objects.create(provider_id="provider_1234")

# create consent
consent = Consent.objects.create(
delivery_point=delivery_point,
created_by=user1,
start=timezone.now(),
end=timezone.now() + timedelta(days=90),
)
new_updated_at = consent.updated_at

# update status to VALIDATED
consent.status = Consent.VALIDATED
consent.save()
assert consent.status == Consent.VALIDATED
assert consent.updated_at > new_updated_at
assert consent.revoked_at is None
new_updated_at = consent.updated_at

# update status to REVOKED
consent.status = Consent.REVOKED
consent.save()
assert consent.status == Consent.REVOKED
assert consent.updated_at > new_updated_at
assert consent.revoked_at is not None
1 change: 1 addition & 0 deletions src/dashboard/apps/core/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Dashboard core app."""
19 changes: 19 additions & 0 deletions src/dashboard/apps/core/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Dashboard core admin."""

from django.contrib import admin

from .models import DeliveryPoint, Entity


@admin.register(Entity)
class EntityAdmin(admin.ModelAdmin):
"""Entity admin."""

pass


@admin.register(DeliveryPoint)
class DeliveryPointAdmin(admin.ModelAdmin):
"""Delivery point admin."""

pass
13 changes: 13 additions & 0 deletions src/dashboard/apps/core/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Dashboard core app base config."""

from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _


class CoreConfig(AppConfig):
"""Core app config."""

default_auto_field = "django.db.models.BigAutoField"
name = "apps.core"
label = "qcd_core"
verbose_name = _("Core")
Loading

0 comments on commit 773e47b

Please sign in to comment.