Skip to content

Commit

Permalink
Merge pull request #2970 from digitalfabrik/feature/add-contact-model
Browse files Browse the repository at this point in the history
Add contact model
  • Loading branch information
JoeyStk authored Aug 9, 2024
2 parents ed2f7a1 + 486c6a7 commit 149a846
Show file tree
Hide file tree
Showing 7 changed files with 271 additions and 38 deletions.
3 changes: 3 additions & 0 deletions integreat_cms/cms/constants/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@
"delete_user",
"grant_page_permissions",
"manage_translations",
"change_contact",
"delete_contact",
"view_contact",
]

#: The permissions of the cms team
Expand Down
30 changes: 30 additions & 0 deletions integreat_cms/cms/fixtures/test_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -5779,5 +5779,35 @@
"poi": 4,
"meta_description": "Meta description of the test location"
}
},
{
"model": "cms.contact",
"pk": 1,
"fields": {
"title": "Integrationsbeauftragte",
"name": "Martina Musterfrau",
"poi": 6,
"email": "[email protected]",
"phone_number": "0123456789",
"website": "",
"archived": false,
"created_date": "2024-08-06T13:23:45.256Z",
"last_updated": "2024-08-06T13:23:45.256Z"
}
},
{
"model": "cms.contact",
"pk": 2,
"fields": {
"title": "Integrationsberaterin",
"name": "Martina Musterfrau",
"poi": 6,
"email": "[email protected]",
"phone_number": "0987654321",
"website": "www.random-page.com",
"archived": true,
"created_date": "2024-08-06T13:23:45.256Z",
"last_updated": "2024-08-06T13:23:45.256Z"
}
}
]
112 changes: 112 additions & 0 deletions integreat_cms/cms/migrations/0098_add_contact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import django.db.models.deletion
import django.utils.timezone
from django.apps.registry import Apps
from django.core.management.sql import emit_post_migrate_signal
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor

from integreat_cms.cms.constants import roles


# pylint: disable=unused-argument
def update_roles(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None:
"""
Add permissions for managing external calendars
:param apps: The configuration of installed applications
:param schema_editor: The database abstraction layer that creates actual SQL code
"""
Group = apps.get_model("auth", "Group")
Permission = apps.get_model("auth", "Permission")

# Emit post-migrate signal to make sure the Permission objects are created before they can be assigned
emit_post_migrate_signal(2, False, "default")

# Clear and update permissions according to new constants
for role_name in dict(roles.CHOICES):
group, _ = Group.objects.get_or_create(name=role_name)
# Clear permissions
group.permissions.clear()
# Set permissions
group.permissions.add(
*Permission.objects.filter(codename__in=roles.PERMISSIONS[role_name])
)


class Migration(migrations.Migration):
"""
Initial migration file for contact
"""

dependencies = [
("cms", "0097_alter_pushnotificationtranslation_text"),
]

operations = [
migrations.CreateModel(
name="Contact",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("title", models.CharField(max_length=200, verbose_name="title")),
("name", models.CharField(max_length=200, verbose_name="name")),
(
"email",
models.EmailField(
blank=True, max_length=254, verbose_name="email address"
),
),
(
"phone_number",
models.CharField(
blank=True, max_length=250, verbose_name="phone number"
),
),
(
"website",
models.URLField(blank=True, max_length=250, verbose_name="website"),
),
(
"archived",
models.BooleanField(
default=False,
help_text="Whether or not the location is read-only and hidden in the API.",
verbose_name="archived",
),
),
(
"last_updated",
models.DateTimeField(
auto_now=True, verbose_name="modification date"
),
),
(
"created_date",
models.DateTimeField(
default=django.utils.timezone.now, verbose_name="creation date"
),
),
(
"poi",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
to="cms.poi",
verbose_name="POI",
),
),
],
options={
"verbose_name": "contact",
"verbose_name_plural": "contacts",
"ordering": ["name"],
"default_permissions": ("change", "delete", "view"),
"default_related_name": "contact",
},
),
]
1 change: 1 addition & 0 deletions integreat_cms/cms/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .chat.attachment_map import AttachmentMap
from .chat.chat_message import ChatMessage
from .chat.user_chat import ABTester, UserChat
from .contact.contact import Contact
from .events.event import Event
from .events.event_translation import EventTranslation
from .events.recurrence_rule import RecurrenceRule
Expand Down
72 changes: 72 additions & 0 deletions integreat_cms/cms/models/contact/contact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from django.db import models
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _

from ..abstract_base_model import AbstractBaseModel
from ..pois.poi import POI
from ..regions.region import Region


class Contact(AbstractBaseModel):
"""
Data model representing a contact
"""

title = models.CharField(max_length=200, verbose_name=_("title"))
name = models.CharField(max_length=200, verbose_name=_("name"))
poi = models.ForeignKey(POI, on_delete=models.PROTECT, verbose_name=_("POI"))
email = models.EmailField(
blank=True,
verbose_name=_("email address"),
)
phone_number = models.CharField(
max_length=250, blank=True, verbose_name=_("phone number")
)
website = models.URLField(blank=True, max_length=250, verbose_name=_("website"))
archived = models.BooleanField(
default=False,
verbose_name=_("archived"),
help_text=_("Whether or not the location is read-only and hidden in the API."),
)
last_updated = models.DateTimeField(
auto_now=True,
verbose_name=_("modification date"),
)
created_date = models.DateTimeField(
default=timezone.now, verbose_name=_("creation date")
)

@cached_property
def region(self) -> Region:
"""
Returns the region this contact belongs to
:return: Region this contact belongs to
"""
return self.poi.region

def __str__(self) -> str:
"""
This overwrites the default Django :meth:`~django.db.models.Model.__str__` method which would return ``Contact object (id)``.
It is used in the Django admin backend and as label for ModelChoiceFields.
:return: A readable string representation of the contact
"""
return f"{self.title} {self.name}"

def get_repr(self) -> str:
"""
This overwrites the default Django ``__repr__()`` method which would return ``<Contact: Contact object (id)>``.
It is used for logging.
:return: The canonical string representation of the contact
"""
return f"<Contact (id: {self.id}, title: {self.title}, name: {self.name}, region: {self.region.slug})>"

class Meta:
verbose_name = _("contact")
default_related_name = "contact"
verbose_name_plural = _("contacts")
default_permissions = ("change", "delete", "view")
ordering = ["name"]
4 changes: 3 additions & 1 deletion integreat_cms/cms/views/regions/region_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from linkcheck.listeners import disable_listeners

from ...decorators import permission_required
from ...models import Page, PushNotification, Region
from ...models import Contact, Page, PushNotification, Region

if TYPE_CHECKING:
from typing import Any
Expand Down Expand Up @@ -96,6 +96,8 @@ def delete_region(
region.events.update(location=None)
# Prevent ProtectedError when media files get deleted before their usages as organization logo
region.organizations.all().delete()
# Prevent ProtectedError when location gets deleted before their contacts
Contact.objects.filter(poi__region=region).delete()
# Prevent IntegrityError when multiple feedback objects exist
region.feedback.all().delete()
# Disable linkchecking while deleting this region
Expand Down
Loading

0 comments on commit 149a846

Please sign in to comment.