Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add contact model #2970

Merged
merged 1 commit into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading