Skip to content

Commit

Permalink
Merge pull request #1677 from digitalfabrik/POICategory
Browse files Browse the repository at this point in the history
Add Model POI Category
  • Loading branch information
MizukiTemma committed Oct 10, 2022
2 parents 5c3265e + b42c950 commit a10d0b8
Show file tree
Hide file tree
Showing 45 changed files with 1,388 additions and 76 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
UNRELEASED
----------

* [ [#951](https://github.com/digitalfabrik/integreat-cms/issues/951) ] Add possibility to create categories for POIs


2022.10.0
---------
Expand Down
2 changes: 2 additions & 0 deletions integreat_cms/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
push_page_translation_content,
)
from .v3.pdf_export import pdf_export
from .v3.location_categories import location_categories
from .v3.push_notifications import sent_push_notifications
from .v3.regions import regions, liveregions, hiddenregions
from .v3.offers import offers
Expand All @@ -39,6 +40,7 @@
content_api_urlpatterns = [
path("pages/", pages, name="pages"),
path("locations/", locations, name="locations"),
path("location-categories/", location_categories, name="location_categories"),
path("events/", events, name="events"),
path("page/", single_page, name="single_page"),
path("post/", single_page, name="single_page"),
Expand Down
61 changes: 61 additions & 0 deletions integreat_cms/api/v3/location_categories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""
This module includes the POI category API endpoint.
"""
from django.http import JsonResponse

from ...cms.models import POICategory
from ..decorators import json_response


def transform_location_category(location_category, language_slug):
"""
Function to create a JSON from a single location category object.
:param location_category: The location category object which should be converted
:type location_category: ~integreat_cms.cms.models.poi_categories.poi_category.POICategory
:param language_slug: The slug of the requested language
:type language_slug: str
:return: Data necessary for API
:rtype: dict
"""
if not location_category:
return None
category_translation = location_category.get_translation(language_slug)
return {
"id": location_category.id,
"name": category_translation.name
if category_translation
else location_category.name,
}


@json_response
# pylint: disable=unused-argument
def location_categories(request, region_slug, language_slug):
"""
Function to return all POI categories as JSON.
:param request: The current request
:type request: ~django.http.HttpRequest
:param region_slug: The slug of the requested region
:type region_slug: str
:param language_slug: The slug of the requested language
:type language_slug: str
:return: JSON object of all POI categories
:rtype: ~django.http.JsonResponse
"""
region = request.region
# Throw a 404 error when the language does not exist or is disabled
region.get_language_or_404(language_slug, only_active=True)
result = [
transform_location_category(location_category, language_slug)
for location_category in POICategory.objects.all()
]
return JsonResponse(
result, safe=False
) # Turn off Safe-Mode to allow serializing arrays
5 changes: 5 additions & 0 deletions integreat_cms/api/v3/locations.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
from django.conf import settings
from django.http import JsonResponse
from django.utils import timezone

from ..decorators import json_response
from .location_categories import transform_location_category


def transform_poi(poi):
Expand Down Expand Up @@ -72,6 +74,9 @@ def transform_poi_translation(poi_translation):
"website": poi.website if poi.website else None,
"email": poi.email if poi.email else None,
"phone_number": poi.phone_number if poi.phone_number else None,
"category": transform_location_category(
poi.category, poi_translation.language.slug
),
"location": transform_poi(poi),
"hash": None,
}
Expand Down
3 changes: 3 additions & 0 deletions integreat_cms/cms/constants/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
"view_organization",
"view_page",
"view_poi",
"view_poicategory",
"view_pushnotification",
"view_region",
"view_user",
Expand Down Expand Up @@ -132,6 +133,7 @@
"change_languagetreenode",
"change_offertemplate",
"change_organization",
"change_poicategory",
"change_user",
"delete_chatmessage",
"delete_directory",
Expand All @@ -142,6 +144,7 @@
"delete_offertemplate",
"delete_page",
"delete_poi",
"delete_poicategory",
"delete_pushnotification",
"delete_region",
"delete_user",
Expand Down
72 changes: 71 additions & 1 deletion integreat_cms/cms/fixtures/test_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -2660,7 +2660,8 @@
"country": "Deutschland",
"latitude": 1.0,
"longitude": 1.0,
"archived": false
"archived": false,
"category": 2
}
},
{
Expand Down Expand Up @@ -2779,6 +2780,75 @@
"creator": 1
}
},
{
"model": "cms.poicategory",
"pk": 1,
"fields": {}
},
{
"model": "cms.poicategory",
"pk": 2,
"fields": {}
},
{
"model": "cms.poicategory",
"pk": 3,
"fields": {}
},
{
"model": "cms.poicategorytranslation",
"pk": 1,
"fields": {
"category": 1,
"language": 1,
"name": "Beratungsstelle"
}
},
{
"model": "cms.poicategorytranslation",
"pk": 2,
"fields": {
"category": 1,
"language": 2,
"name": "Advice Center"
}
},
{
"model": "cms.poicategorytranslation",
"pk": 3,
"fields": {
"category": 2,
"language": 1,
"name": "Behörde/Amt"
}
},
{
"model": "cms.poicategorytranslation",
"pk": 4,
"fields": {
"category": 2,
"language": 2,
"name": "Authority/Office"
}
},
{
"model": "cms.poicategorytranslation",
"pk": 5,
"fields": {
"category": 3,
"language": 1,
"name": "Gastronomie"
}
},
{
"model": "cms.poicategorytranslation",
"pk": 6,
"fields": {
"category": 3,
"language": 2,
"name": "Gastronomy"
}
},
{
"model": "linkcheck.url",
"pk": 1,
Expand Down
5 changes: 5 additions & 0 deletions integreat_cms/cms/forms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,8 @@
from .users.password_reset_form import CustomPasswordResetForm

from .object_search_form import ObjectSearchForm

from .poi_categories.poi_category_translation_form import (
POICategoryTranslationForm,
poi_category_translation_formset_factory,
)
3 changes: 3 additions & 0 deletions integreat_cms/cms/forms/poi_categories/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
Forms for creating and modifying POI Category and POI Category Translation objects
"""
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import logging

from django.core.exceptions import ValidationError
from django.forms import inlineformset_factory
from django.forms.models import BaseInlineFormSet
from django.forms.formsets import DELETION_FIELD_NAME
from django.utils.translation import gettext_lazy as _

from ...models import Language, POICategory, POICategoryTranslation
from ..custom_model_form import CustomModelForm

logger = logging.getLogger(__name__)


class POICategoryTranslationForm(CustomModelForm):
"""
Form for creating and modifying POI category translation objects
"""

def __init__(self, **kwargs):
r"""
Initialize POI category translation form
:param \**kwargs: The supplied keyword arguments
:type \**kwargs: dict
"""

# Instantiate CustomModelForm
super().__init__(**kwargs)

# Do not require category and name fields
self.fields["category"].required = False
self.fields["name"].required = False

# Set custom language labels
language_name = self.instance.language.translated_name
self.fields["name"].widget.attrs.update(
{"placeholder": _("Enter name in {} here").format(language_name)}
)
self.fields["name"].label = _("Translation in {}").format(language_name)

def clean(self):
"""
This method extends the ``clean()``-method to delete translations with an empty name.
:return: The cleaned data (see :ref:`overriding-modelform-clean-method`)
:rtype: dict
"""
cleaned_data = super().clean()
# If the name field is empty, delete the form
if not cleaned_data.get("name") and "name" not in self.errors:
cleaned_data[DELETION_FIELD_NAME] = True
return cleaned_data

class Meta:
"""
This class contains additional meta configuration of the form class, see the :class:`django.forms.ModelForm`
for more information.
"""

#: The model of this :class:`django.forms.ModelForm`
model = POICategoryTranslation
#: The fields of the model which should be handled by this form
fields = ["category", "language", "name"]


class BaseInlinePOICategoryTranslationFormSet(BaseInlineFormSet):
"""
A formset for translations of POI categories
"""

def get_form_kwargs(self, index):
"""
Return additional keyword arguments for each individual formset form.
(see :meth:`~django.views.generic.edit.ModelFormMixin.get_form_kwargs` and
:ref:`django:custom-formset-form-kwargs`)
:param index: The index of the initialized form
(will be ``None`` if the form being constructed is a new empty form)
:type index: int
:return: The form kwargs
:rtype: dict
"""
kwargs = super().get_form_kwargs(index)
# Only add the additional instances for extra forms which do not have the initial data
if index >= self.initial_form_count():
# Get the relative index of all extra forms
rel_index = index - self.initial_form_count()
# Get all remaining languages
languages = Language.objects.exclude(
id__in=self.instance.translations.values_list("language__id", flat=True)
)
# Assign the language to the form with this index
kwargs["additional_instance_attributes"] = {
"language": languages[rel_index]
}
return kwargs

def clean(self):
"""
Make sure that at least one translation is given
:raises ~django.core.exceptions.ValidationError: When not a single form contains a valid text
"""
super().clean()
if not any(form.cleaned_data.get("name") for form in self):
raise ValidationError(_("At least one translation is required."))


def poi_category_translation_formset_factory():
"""
Build the formset class
:returns: The POICategoryTranslationFormset class
:rtype: type
"""
num_languages = Language.objects.count()
return inlineformset_factory(
parent_model=POICategory,
model=POICategoryTranslation,
form=POICategoryTranslationForm,
formset=BaseInlinePOICategoryTranslationFormSet,
min_num=num_languages,
max_num=num_languages,
)
1 change: 1 addition & 0 deletions integreat_cms/cms/forms/pois/poi_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Meta:
"website",
"email",
"phone_number",
"category",
]
#: The widgets which are used in this form
widgets = {
Expand Down

0 comments on commit a10d0b8

Please sign in to comment.