Skip to content

Commit

Permalink
Simplify email metadata storage and access (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelkornblum authored Jan 3, 2025
1 parent fb41971 commit dcc610c
Show file tree
Hide file tree
Showing 15 changed files with 334 additions and 187 deletions.
20 changes: 9 additions & 11 deletions core/services.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
from typing import TYPE_CHECKING

from django.contrib.auth import get_user_model

from profiles import services as profile_services
from profiles.models.combined import Profile
from user import services as user_services
Expand All @@ -11,8 +7,9 @@ def create_identity(
id: str,
first_name: str,
last_name: str,
emails: list[dict],
preferred_email: str | None = None,
all_emails: list[str],
primary_email: str | None = None,
contact_email: str | None = None,
) -> Profile:
"""
Entrypoint for new user creation. Triggers the creation of User record,
Expand All @@ -23,9 +20,10 @@ def create_identity(

user_services.create(sso_email_id=id)
return profile_services.create_from_sso(
id,
first_name,
last_name,
emails,
preferred_email,
sso_email_id=id,
first_name=first_name,
last_name=last_name,
all_emails=all_emails,
contact_email=contact_email,
primary_email=primary_email,
)
4 changes: 2 additions & 2 deletions core/tests/test_core_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def test_create_identity(self):
id="[email protected]",
first_name="Billy",
last_name="Bob",
emails=[{"address": "[email protected]"}],
all_emails=["[email protected]"],
)
self.assertEqual(user.sso_email_id, "[email protected]")
self.assertEqual(user.is_active, True)
Expand All @@ -28,5 +28,5 @@ def test_create_identity(self):
id="[email protected]",
first_name="Billy",
last_name="Bob",
emails=[{"address": "[email protected]"}],
all_emails=["[email protected]"],
)
12 changes: 2 additions & 10 deletions core/tests/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,15 @@ def test_existing_user(self) -> None:
sso_email_id,
"Billy",
"Bob",
[
{
"address": "[email protected]",
"type": "",
"preferred": False,
},
],
["[email protected]"],
)

def test_new_user(self) -> None:
profile = services.create_identity(
"[email protected]",
"Billy",
"Bob",
[
{"address": "[email protected]", "type": "", "preferred": False},
],
["[email protected]"],
)
self.assertTrue(isinstance(profile, Profile))
self.assertTrue(profile.pk)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Generated by Django 5.1.4 on 2025-01-02 13:10

from django.db import migrations, models


def forward(apps, schema_editor):
StaffSSOProfileEmail = apps.get_model("profiles", "StaffSSOProfileEmail")
records_to_update = StaffSSOProfileEmail.objects.filter(type="work")
records_to_update.update(type="verified")


def backward(apps, scheme_editor):
StaffSSOProfileEmail = apps.get_model("profiles", "StaffSSOProfileEmail")
records_to_update = StaffSSOProfileEmail.objects.filter(type="verified")
records_to_update.update(type="work")


class Migration(migrations.Migration):

dependencies = [
("profiles", "0002_historicalprofile_is_active_profile_is_active"),
]

operations = [
migrations.AlterUniqueTogether(
name="staffssoprofileemail",
unique_together=set(),
),
migrations.AlterField(
model_name="historicalstaffssoprofileemail",
name="type",
field=models.CharField(
choices=[
("VERIFIED", "verified"),
("CONTACT", "contact"),
("USER_ADDED", "user-added"),
],
max_length=50,
),
),
migrations.AlterField(
model_name="staffssoprofileemail",
name="type",
field=models.CharField(
choices=[
("VERIFIED", "verified"),
("CONTACT", "contact"),
("USER_ADDED", "user-added"),
],
max_length=50,
),
),
migrations.AlterUniqueTogether(
name="staffssoprofileemail",
unique_together={("profile", "email", "type", "preferred")},
),
migrations.RunPython(forward, backward),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Generated by Django 5.1.4 on 2025-01-02 17:04

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("profiles", "0003_alter_staffssoprofileemail_unique_together_and_more"),
]

operations = [
migrations.RenameField(
model_name="historicalprofile",
old_name="preferred_email",
new_name="primary_email",
),
migrations.RenameField(
model_name="historicalstaffssoprofileemail",
old_name="preferred",
new_name="is_primary",
),
migrations.RenameField(
model_name="profile",
old_name="preferred_email",
new_name="primary_email",
),
migrations.RenameField(
model_name="staffssoprofileemail",
old_name="preferred",
new_name="is_primary",
),
migrations.RemoveField(
model_name="historicalstaffssoprofileemail",
name="type",
),
migrations.AlterUniqueTogether(
name="staffssoprofileemail",
unique_together={("profile", "email")},
),
migrations.AddField(
model_name="historicalprofile",
name="contact_email",
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name="historicalstaffssoprofileemail",
name="is_contact",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="profile",
name="contact_email",
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name="staffssoprofileemail",
name="is_contact",
field=models.BooleanField(default=False),
),
migrations.RemoveField(
model_name="staffssoprofileemail",
name="type",
),
]
3 changes: 2 additions & 1 deletion profiles/models/combined.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ class Profile(AbstractHistoricalModel):
sso_email_id = models.CharField(primary_key=True, unique=True)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
preferred_email = models.CharField(max_length=100)
primary_email = models.CharField(max_length=100)
contact_email = models.CharField(max_length=100, null=True, blank=True)
emails = ArrayField(models.CharField(max_length=100), default=list)
is_active = models.BooleanField(
default=True,
Expand Down
5 changes: 0 additions & 5 deletions profiles/models/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@
from .abstract import AbstractHistoricalModel


class EmailTypes(models.TextChoices):
WORK = "work", "Work"
CONTACT = "contact", "Contact"


class Email(AbstractHistoricalModel):
address = models.EmailField(validators=[EmailValidator()], unique=True)

Expand Down
26 changes: 22 additions & 4 deletions profiles/models/staff_sso.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
from django.db import models

from .abstract import AbstractHistoricalModel, AbstractProfile
from .generic import Email, EmailTypes
from .generic import Email


class StaffSSOProfile(AbstractProfile):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)

@property
def contact_email(self):
try:
return self.emails.get(is_contact=True)
except StaffSSOProfileEmail.DoesNotExist:
return None

@property
def primary_email(self):
try:
return self.emails.get(is_primary=True)
except StaffSSOProfileEmail.DoesNotExist:
return None

@property
def email_addresses(self):
return [e["email__address"] for e in self.emails.values("email__address")]

def __str__(self):
return f"StaffSSOProfile: {self.first_name} {self.last_name}"

Expand All @@ -17,11 +35,11 @@ class StaffSSOProfileEmail(AbstractHistoricalModel):
"StaffSSOProfile", on_delete=models.CASCADE, related_name="emails"
)
email = models.ForeignKey(Email, on_delete=models.CASCADE)
type = models.CharField(max_length=50, choices=EmailTypes.choices)
preferred = models.BooleanField(default=False)
is_primary = models.BooleanField(default=False)
is_contact = models.BooleanField(default=False)

class Meta:
unique_together = ("profile", "email", "type")
unique_together = ("profile", "email")

def __str__(self):
return f"StaffSSOProfileEmail: {self.profile.first_name} {self.profile.last_name}: {self.email.address}"
38 changes: 26 additions & 12 deletions profiles/services/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# This is the entrypoint service that coordinates between the sub-services
# If in doubt about what to use, you should probably be using this
from typing import Optional

from profiles.models.combined import Profile
from profiles.models.staff_sso import StaffSSOProfileEmail
from profiles.services import combined, staff_sso

from .combined import get_by_id as get_combined_by_id
Expand All @@ -18,29 +18,42 @@ def generate_combined_profile_data(sso_email_id: str):
field hierarchy per data type to generate the values for the combined.
create method
"""
sso_profile = staff_sso.get_by_user_id(sso_email_id)
sso_profile = staff_sso.get_by_id(sso_email_id)
user = sso_profile.user

primary_email = sso_profile.primary_email
if primary_email is None:
primary_email = sso_profile.email_addresses[0]
contact_email = sso_profile.contact_email
if contact_email is None:
contact_email = primary_email

return {
"first_name": sso_profile.first_name,
"last_name": sso_profile.last_name,
"preferred_email": "",
"emails": [],
"primary_email": primary_email,
"contact_email": contact_email,
"emails": sso_profile.email_addresses,
"is_active": user.is_active,
}


def create_from_sso(
sso_email_id: str,
first_name: str,
last_name: str,
emails: list[dict],
preferred_email: str | None = None,
all_emails: list[str],
primary_email: Optional[str] = None,
contact_email: Optional[str] = None,
) -> Profile:
"""A central function to create all relevant profile details"""
staff_sso.create(
sso_email_id,
first_name,
last_name,
emails,
sso_email_id=sso_email_id,
first_name=first_name,
last_name=last_name,
all_emails=all_emails,
primary_email=primary_email,
contact_email=contact_email,
)
combined_profile_data = generate_combined_profile_data(sso_email_id)

Expand All @@ -50,6 +63,7 @@ def create_from_sso(
sso_email_id=sso_email_id,
first_name=combined_profile_data["first_name"],
last_name=combined_profile_data["last_name"],
preferred_email=combined_profile_data["preferred_email"],
emails=combined_profile_data["emails"],
primary_email=combined_profile_data["primary_email"],
contact_email=combined_profile_data["contact_email"],
all_emails=combined_profile_data["emails"],
)
Loading

0 comments on commit dcc610c

Please sign in to comment.