Skip to content

Commit 3d408d5

Browse files
authored
Merge pull request #158 from NHSDigital/add-notifications-app
[DTOSS-9632] Add notifications app, initial models and migrations
2 parents e6490e9 + 4b0b3b3 commit 3d408d5

9 files changed

Lines changed: 200 additions & 0 deletions

File tree

manage_breast_screening/config/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ def boolean_env(key, default=None):
5959
"django.contrib.staticfiles",
6060
"manage_breast_screening.core",
6161
"manage_breast_screening.clinics",
62+
"manage_breast_screening.notifications",
6263
"manage_breast_screening.participants",
6364
"manage_breast_screening.mammograms",
6465
]

manage_breast_screening/notifications/__init__.py

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Register your models here.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.apps import AppConfig
2+
3+
4+
class NotificationsConfig(AppConfig):
5+
default_auto_field = "django.db.models.BigAutoField"
6+
name = "manage_breast_screening.notifications"
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Generated by Django 5.2.3 on 2025-06-26 14:04
2+
3+
import django.db.models.deletion
4+
import uuid
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
initial = True
11+
12+
dependencies = [
13+
]
14+
15+
operations = [
16+
migrations.CreateModel(
17+
name='Clinic',
18+
fields=[
19+
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
20+
('code', models.CharField(max_length=50)),
21+
('name', models.CharField(max_length=50)),
22+
('alt_name', models.CharField(max_length=50)),
23+
('holding_clinic', models.BooleanField()),
24+
('location_code', models.CharField(max_length=50)),
25+
('address_line_1', models.CharField(max_length=50)),
26+
('address_line_2', models.CharField(max_length=50)),
27+
('address_line_3', models.CharField(max_length=50)),
28+
('address_line_4', models.CharField(max_length=50)),
29+
('address_line_5', models.CharField(max_length=50)),
30+
('postcode', models.CharField(max_length=50)),
31+
('created_at', models.DateTimeField()),
32+
('updated_at', models.DateTimeField()),
33+
],
34+
),
35+
migrations.CreateModel(
36+
name='MessageBatch',
37+
fields=[
38+
('updated_at', models.DateTimeField(auto_now=True)),
39+
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
40+
('notify_id', models.CharField(blank=True, max_length=50)),
41+
('created_at', models.DateTimeField(auto_now_add=True)),
42+
('scheduled_at', models.DateTimeField(blank=True, null=True)),
43+
('sent_at', models.DateTimeField(blank=True, null=True)),
44+
('status', models.CharField(choices=[('unscheduled', 'Unscheduled'), ('scheduled', 'Scheduled'), ('sent', 'Sent'), ('failed', 'Failed')], default='unscheduled', max_length=50)),
45+
],
46+
options={
47+
'abstract': False,
48+
},
49+
),
50+
migrations.CreateModel(
51+
name='Appointment',
52+
fields=[
53+
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
54+
('nbss_id', models.CharField(max_length=30)),
55+
('nhs_number', models.IntegerField()),
56+
('status', models.CharField(max_length=50)),
57+
('booked_by', models.CharField(max_length=50)),
58+
('cancelled_by', models.CharField(max_length=50)),
59+
('number', models.IntegerField(default=1, null=True)),
60+
('starts_at', models.DateTimeField()),
61+
('created_at', models.DateTimeField()),
62+
('clinic', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='notifications.clinic')),
63+
],
64+
),
65+
migrations.CreateModel(
66+
name='Message',
67+
fields=[
68+
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
69+
('notify_id', models.CharField(blank=True, max_length=50)),
70+
('created_at', models.DateTimeField(blank=True, null=True)),
71+
('sent_at', models.DateTimeField(blank=True, null=True)),
72+
('status', models.CharField(choices=[('pending_enrichment', 'Pending enrichment'), ('enriched', 'Enriched'), ('sending', 'Sending'), ('delivered', 'Delivered'), ('failed', 'Failed')], default='pending_enrichment', max_length=50)),
73+
('appointment', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='notifications.appointment')),
74+
('batch', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='notifications.messagebatch')),
75+
],
76+
),
77+
]

manage_breast_screening/notifications/migrations/__init__.py

Whitespace-only changes.
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import uuid
2+
3+
from django.db import models
4+
5+
from ..core.models import BaseModel
6+
7+
BATCH_STATUSES = [
8+
("unscheduled", "Unscheduled"),
9+
("scheduled", "Scheduled"),
10+
("sent", "Sent"),
11+
("failed", "Failed"),
12+
]
13+
14+
MESSAGE_STATUSES = [
15+
("pending_enrichment", "Pending enrichment"),
16+
("enriched", "Enriched"),
17+
("sending", "Sending"),
18+
("delivered", "Delivered"),
19+
("failed", "Failed"),
20+
]
21+
22+
23+
class MessageBatch(BaseModel):
24+
"""
25+
Multiple messages sent as a batch
26+
"""
27+
28+
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
29+
notify_id = models.CharField(max_length=50, blank=True)
30+
created_at = models.DateTimeField(auto_now_add=True)
31+
scheduled_at = models.DateTimeField(null=True, blank=True)
32+
sent_at = models.DateTimeField(null=True, blank=True)
33+
status = models.CharField(
34+
max_length=50, choices=BATCH_STATUSES, default="unscheduled"
35+
)
36+
37+
def __str__(self):
38+
return f"MessageBatch {self.id} - Status: {self.status}"
39+
40+
41+
class Message(models.Model):
42+
"""
43+
A message sent to a participant.
44+
This is usually linked to a MessageBatch but can be a standalone message.
45+
"""
46+
47+
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
48+
notify_id = models.CharField(max_length=50, blank=True)
49+
batch = models.ForeignKey(
50+
MessageBatch,
51+
related_name="messages",
52+
on_delete=models.CASCADE,
53+
null=True,
54+
blank=True,
55+
)
56+
created_at = models.DateTimeField(null=True, blank=True)
57+
sent_at = models.DateTimeField(null=True, blank=True)
58+
status = models.CharField(
59+
max_length=50, choices=MESSAGE_STATUSES, default="pending_enrichment"
60+
)
61+
62+
appointment = models.ForeignKey(
63+
"notifications.Appointment", on_delete=models.PROTECT
64+
)
65+
66+
def __str__(self):
67+
return f"Message about {self.appointment} - Sent at: {self.sent_at}"
68+
69+
70+
class Appointment(models.Model):
71+
"""
72+
The screening appointment used to build the message.
73+
"""
74+
75+
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
76+
nbss_id = models.CharField(max_length=30)
77+
nhs_number = models.IntegerField(null=False)
78+
status = models.CharField(max_length=50)
79+
booked_by = models.CharField(max_length=50)
80+
cancelled_by = models.CharField(max_length=50)
81+
number = models.IntegerField(null=True, default=1)
82+
starts_at = models.DateTimeField(null=False)
83+
created_at = models.DateTimeField(null=False)
84+
85+
clinic = models.ForeignKey("notifications.Clinic", on_delete=models.PROTECT)
86+
87+
def __str__(self):
88+
return f"Appointment for {self.starts_at} at {self.clinic}"
89+
90+
91+
class Clinic(models.Model):
92+
"""
93+
A clinic where an appointment is held.
94+
"""
95+
96+
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
97+
code = models.CharField(max_length=50)
98+
name = models.CharField(max_length=50)
99+
alt_name = models.CharField(max_length=50)
100+
holding_clinic = models.BooleanField()
101+
location_code = models.CharField(max_length=50)
102+
address_line_1 = models.CharField(max_length=50)
103+
address_line_2 = models.CharField(max_length=50)
104+
address_line_3 = models.CharField(max_length=50)
105+
address_line_4 = models.CharField(max_length=50)
106+
address_line_5 = models.CharField(max_length=50)
107+
address_line_5 = models.CharField(max_length=50)
108+
postcode = models.CharField(max_length=50)
109+
created_at = models.DateTimeField(null=False)
110+
updated_at = models.DateTimeField(null=False)
111+
112+
def __str__(self):
113+
return f"Clinic {self.name} ({self.code})"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Create your tests here.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Create your views here.

0 commit comments

Comments
 (0)