diff --git a/actions/recurring_reservation.py b/actions/recurring_reservation.py
index 562d2d0af..85477efa2 100644
--- a/actions/recurring_reservation.py
+++ b/actions/recurring_reservation.py
@@ -6,7 +6,6 @@
from typing import TYPE_CHECKING, Any, TypedDict
from common.date_utils import DEFAULT_TIMEZONE, combine, get_periods_between
-from opening_hours.utils.time_span_element import TimeSpanElement
from reservations.enums import RejectionReadinessChoice, ReservationTypeChoice, ReservationTypeStaffChoice
from reservations.models import (
AffectingTimeSpan,
@@ -15,6 +14,7 @@
Reservation,
ReservationPurpose,
)
+from tilavarauspalvelu.utils.opening_hours.time_span_element import TimeSpanElement
if TYPE_CHECKING:
from collections.abc import Collection, Iterable
@@ -22,9 +22,8 @@
from django.db import models
from applications.models import City
- from opening_hours.models import ReservableTimeSpan
from reservations.enums import CustomerTypeChoice, ReservationStateChoice
- from tilavarauspalvelu.models import User
+ from tilavarauspalvelu.models import ReservableTimeSpan, User
class ReservationPeriod(TypedDict):
diff --git a/actions/reservation_unit.py b/actions/reservation_unit.py
index e833dec51..54c928a45 100644
--- a/actions/reservation_unit.py
+++ b/actions/reservation_unit.py
@@ -4,11 +4,11 @@
from typing import TYPE_CHECKING, Any
from common.date_utils import DEFAULT_TIMEZONE, time_as_timedelta
-from opening_hours.errors import HaukiAPIError
-from opening_hours.models import OriginHaukiResource, ReservableTimeSpan
-from opening_hours.utils.hauki_api_client import HaukiAPIClient
-from opening_hours.utils.hauki_api_types import HaukiAPIResource, HaukiTranslatedField
from reservation_units.enums import ReservationStartInterval
+from tilavarauspalvelu.exceptions import HaukiAPIError
+from tilavarauspalvelu.models import OriginHaukiResource
+from tilavarauspalvelu.utils.opening_hours.hauki_api_client import HaukiAPIClient
+from tilavarauspalvelu.utils.opening_hours.hauki_api_types import HaukiAPIResource, HaukiTranslatedField
from utils.external_service.errors import ExternalServiceError
if TYPE_CHECKING:
@@ -18,7 +18,7 @@
from reservation_units.models import ReservationUnit
from reservations.models import Reservation
- from tilavarauspalvelu.models import Building, Location
+ from tilavarauspalvelu.models import Building, Location, ReservableTimeSpan
__all__ = [
"ReservationUnitActions",
diff --git a/applications/tasks.py b/applications/tasks.py
index 1b9c7d08e..5fd738f3a 100644
--- a/applications/tasks.py
+++ b/applications/tasks.py
@@ -10,12 +10,12 @@
from common.date_utils import local_end_of_day, local_start_of_day
from common.utils import translate_for_user
from config.celery import app
-from opening_hours.enums import HaukiResourceState
-from opening_hours.utils.hauki_api_client import HaukiAPIClient
-from opening_hours.utils.time_span_element import TimeSpanElement
from reservations.enums import CustomerTypeChoice, ReservationStateChoice, ReservationTypeChoice
from reservations.models import RecurringReservation
from reservations.tasks import create_or_update_reservation_statistics, update_affecting_time_spans_task
+from tilavarauspalvelu.enums import HaukiResourceState
+from tilavarauspalvelu.utils.opening_hours.hauki_api_client import HaukiAPIClient
+from tilavarauspalvelu.utils.opening_hours.time_span_element import TimeSpanElement
from utils.sentry import SentryLogger
diff --git a/common/date_utils.py b/common/date_utils.py
index b28ec6bc4..17dc0feca 100644
--- a/common/date_utils.py
+++ b/common/date_utils.py
@@ -465,3 +465,10 @@ def get_periods_between(
for delta in range(0, (end_date - start_date).days + 1, interval):
yield start_datetime + datetime.timedelta(days=delta), end_datetime + datetime.timedelta(days=delta)
+
+
+def normalize_as_datetime(value: datetime.date | datetime.datetime, *, timedelta_days: int = 0) -> datetime.datetime:
+ if isinstance(value, datetime.datetime):
+ return value
+ # Convert dates to datetimes to include timezone information
+ return combine(value, datetime.time.min, tzinfo=DEFAULT_TIMEZONE) + datetime.timedelta(days=timedelta_days)
diff --git a/common/management/commands/data_creation/create_caisa.py b/common/management/commands/data_creation/create_caisa.py
index a8a22ca07..52fbed79a 100644
--- a/common/management/commands/data_creation/create_caisa.py
+++ b/common/management/commands/data_creation/create_caisa.py
@@ -1,6 +1,5 @@
from datetime import date, timedelta
-from opening_hours.models import OriginHaukiResource
from reservation_units.enums import (
AuthenticationType,
PriceUnit,
@@ -22,7 +21,15 @@
)
from reservations.models import ReservationMetadataSet
from tilavarauspalvelu.enums import TermsOfUseTypeChoices
-from tilavarauspalvelu.models import PaymentAccounting, PaymentMerchant, PaymentProduct, Space, TermsOfUse, Unit
+from tilavarauspalvelu.models import (
+ OriginHaukiResource,
+ PaymentAccounting,
+ PaymentMerchant,
+ PaymentProduct,
+ Space,
+ TermsOfUse,
+ Unit,
+)
from .utils import SetName, with_logs
diff --git a/common/management/commands/data_creation/create_misc.py b/common/management/commands/data_creation/create_misc.py
index 4ced750a4..243a2a434 100644
--- a/common/management/commands/data_creation/create_misc.py
+++ b/common/management/commands/data_creation/create_misc.py
@@ -5,9 +5,9 @@
from django.utils.timezone import localtime
from django_celery_beat.models import CrontabSchedule, PeriodicTask
+from common.date_utils import DEFAULT_TIMEZONE
from common.enums import BannerNotificationLevel, BannerNotificationTarget
from common.models import BannerNotification
-from opening_hours.models import DEFAULT_TIMEZONE
from reservations.tasks import prune_reservations_task, update_affecting_time_spans_task, update_expired_orders_task
from .utils import faker_en, faker_fi, faker_sv, with_logs
diff --git a/common/management/commands/data_creation/create_reservation_units.py b/common/management/commands/data_creation/create_reservation_units.py
index 78412fe30..190abce37 100644
--- a/common/management/commands/data_creation/create_reservation_units.py
+++ b/common/management/commands/data_creation/create_reservation_units.py
@@ -7,7 +7,6 @@
from decimal import Decimal
from itertools import cycle
-from opening_hours.models import OriginHaukiResource, ReservableTimeSpan
from reservation_units.enums import (
AuthenticationType,
PriceUnit,
@@ -29,7 +28,7 @@
)
from reservations.models import ReservationMetadataSet
from tilavarauspalvelu.enums import TermsOfUseTypeChoices
-from tilavarauspalvelu.models import Resource, Service, TermsOfUse, Unit
+from tilavarauspalvelu.models import OriginHaukiResource, ReservableTimeSpan, Resource, Service, TermsOfUse, Unit
from .create_seasonal_booking import _create_application_round_time_slots
from .utils import (
diff --git a/locale/fi/LC_MESSAGES/django.po b/locale/fi/LC_MESSAGES/django.po
index 4765de88c..3d22bd2e4 100644
--- a/locale/fi/LC_MESSAGES/django.po
+++ b/locale/fi/LC_MESSAGES/django.po
@@ -1346,106 +1346,6 @@ msgstr "Englanti"
msgid "Swedish"
msgstr "Ruotsi"
-#: opening_hours/admin/origin_hauki_resource.py
-msgid ""
-"OriginHaukiResources with this specific hash never have any opening hours."
-msgstr ""
-"OriginHaukiResources, joilla on tämä tietty tarkiste, eivät koskaan sisällä "
-"aukioloaikoja."
-
-#: opening_hours/admin/origin_hauki_resource.py
-msgid "ID of the resource in Hauki."
-msgstr "Resurssin tunniste Aukiolosovelluksesta."
-
-#: opening_hours/admin/origin_hauki_resource.py
-msgid ""
-"Hash of the opening hours. Used to determine if the opening hours have "
-"changed."
-msgstr ""
-"Aukioloaikojen tarkiste. Käytetään määrittämään, ovatko aukioloajat "
-"muuttuneet."
-
-#: opening_hours/admin/origin_hauki_resource.py
-msgid ""
-"All opening hours have been fetched from Hauki up until this date. Opening "
-"hours are fetched until the last day of the month two years from now."
-msgstr ""
-"Kaikki aukioloajat on haettu Aukiolosovelluksesta tähän päivämäärään asti. "
-"Aukioloajat haetaan kaksi vuotta eteenpäin."
-
-#: opening_hours/admin/origin_hauki_resource.py
-msgid "Reservable Time Spans updated."
-msgstr "Varattavat aikavälit päivitetty."
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Open"
-msgstr "Auki"
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Closed"
-msgstr "Suljettu"
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Undefined"
-msgstr "Määrittelemätön"
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Self service"
-msgstr "Itsepalvelu"
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "With key"
-msgstr "Avaimella"
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "With reservation"
-msgstr "Varauksella"
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Open and reservable"
-msgstr "Auki ja varattavissa"
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "With key and reservation"
-msgstr "Avaimella ja varauksella"
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Enter only"
-msgstr "Vain sisäänkäynti"
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Exit only"
-msgstr "Vain uloskäynti"
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Weather permitting"
-msgstr "Sään salliessa"
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Not in use"
-msgstr "Ei vuoroa käytössä"
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Maintenance"
-msgstr "Siivoustauko/huoltotauko"
-
-#: opening_hours/models.py
-msgid "`start_datetime` must be before `end_datetime`."
-msgstr "`start_datetime` täytyy olla ennen `end_datetime`."
-
#: reservation_units/admin/reservation_unit/admin.py
msgid "Search by ID, name, unit name, or service sector name"
msgstr "Etsi ID:llä, nimellä, toimipisteen nimellä tai palvelualueen nimellä"
@@ -2804,6 +2704,37 @@ msgstr "Testisähköposti '%s' lähetetty onnistuneesti."
msgid "Search by username, email, first name or last name"
msgstr "Etsi käyttäjänimellä, sähköpostilla, etu- tai sukunimellä"
+#: tilavarauspalvelu/admin/origin_hauki_resource/admin.py
+msgid ""
+"OriginHaukiResources with this specific hash never have any opening hours."
+msgstr ""
+"OriginHaukiResources, joilla on tämä tietty tarkiste, eivät koskaan sisällä "
+"aukioloaikoja."
+
+#: tilavarauspalvelu/admin/origin_hauki_resource/admin.py
+msgid "ID of the resource in Hauki."
+msgstr "Resurssin tunniste Aukiolosovelluksesta."
+
+#: tilavarauspalvelu/admin/origin_hauki_resource/admin.py
+msgid ""
+"Hash of the opening hours. Used to determine if the opening hours have "
+"changed."
+msgstr ""
+"Aukioloaikojen tarkiste. Käytetään määrittämään, ovatko aukioloajat "
+"muuttuneet."
+
+#: tilavarauspalvelu/admin/origin_hauki_resource/admin.py
+msgid ""
+"All opening hours have been fetched from Hauki up until this date. Opening "
+"hours are fetched until the last day of the month two years from now."
+msgstr ""
+"Kaikki aukioloajat on haettu Aukiolosovelluksesta tähän päivämäärään asti. "
+"Aukioloajat haetaan kaksi vuotta eteenpäin."
+
+#: tilavarauspalvelu/admin/origin_hauki_resource/admin.py
+msgid "Reservable Time Spans updated."
+msgstr "Varattavat aikavälit päivitetty."
+
#: tilavarauspalvelu/admin/payment_merchant/admin.py
msgid "The Paytrail Merchant ID should be a six-digit number."
msgstr "Kaupan paytrail-tunnuksen tulisi olla kuusinumeroinen."
@@ -3061,6 +2992,71 @@ msgstr "Varaaja"
msgid "Notification manager"
msgstr "Ilmoituksen hallitsija."
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Open"
+msgstr "Auki"
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Closed"
+msgstr "Suljettu"
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Undefined"
+msgstr "Määrittelemätön"
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Self service"
+msgstr "Itsepalvelu"
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "With key"
+msgstr "Avaimella"
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "With reservation"
+msgstr "Varauksella"
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Open and reservable"
+msgstr "Auki ja varattavissa"
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "With key and reservation"
+msgstr "Avaimella ja varauksella"
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Enter only"
+msgstr "Vain sisäänkäynti"
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Exit only"
+msgstr "Vain uloskäynti"
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Weather permitting"
+msgstr "Sään salliessa"
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Not in use"
+msgstr "Ei vuoroa käytössä"
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Maintenance"
+msgstr "Siivoustauko/huoltotauko"
+
#: tilavarauspalvelu/models/email_template/model.py
msgid "Email type"
msgstr "Sähköpostin tyyppi"
@@ -3125,6 +3121,10 @@ msgstr "Täytyy olla suurempi kuin 0"
msgid "Must be the sum of net and vat amounts"
msgstr "Täytyy olla netto- ja ALV-määrien summa"
+#: tilavarauspalvelu/models/reservable_time_span/model.py
+msgid "`start_datetime` must be before `end_datetime`."
+msgstr "`start_datetime` täytyy olla ennen `end_datetime`."
+
#: tilavarauspalvelu/models/terms_of_use/model.py
msgctxt "singular"
msgid "terms of use"
diff --git a/locale/sv/LC_MESSAGES/django.po b/locale/sv/LC_MESSAGES/django.po
index 5687731ff..efe62559b 100644
--- a/locale/sv/LC_MESSAGES/django.po
+++ b/locale/sv/LC_MESSAGES/django.po
@@ -1306,100 +1306,6 @@ msgstr ""
msgid "Swedish"
msgstr ""
-#: opening_hours/admin/origin_hauki_resource.py
-msgid ""
-"OriginHaukiResources with this specific hash never have any opening hours."
-msgstr ""
-
-#: opening_hours/admin/origin_hauki_resource.py
-msgid "ID of the resource in Hauki."
-msgstr ""
-
-#: opening_hours/admin/origin_hauki_resource.py
-msgid ""
-"Hash of the opening hours. Used to determine if the opening hours have "
-"changed."
-msgstr ""
-
-#: opening_hours/admin/origin_hauki_resource.py
-msgid ""
-"All opening hours have been fetched from Hauki up until this date. Opening "
-"hours are fetched until the last day of the month two years from now."
-msgstr ""
-
-#: opening_hours/admin/origin_hauki_resource.py
-msgid "Reservable Time Spans updated."
-msgstr ""
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Open"
-msgstr ""
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Closed"
-msgstr ""
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Undefined"
-msgstr ""
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Self service"
-msgstr ""
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "With key"
-msgstr ""
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "With reservation"
-msgstr ""
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Open and reservable"
-msgstr ""
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "With key and reservation"
-msgstr ""
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Enter only"
-msgstr ""
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Exit only"
-msgstr ""
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Weather permitting"
-msgstr ""
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Not in use"
-msgstr ""
-
-#: opening_hours/enums.py
-msgctxt "HaukiResourceState"
-msgid "Maintenance"
-msgstr ""
-
-#: opening_hours/models.py
-msgid "`start_datetime` must be before `end_datetime`."
-msgstr ""
-
#: reservation_units/admin/reservation_unit/admin.py
msgid "Search by ID, name, unit name, or service sector name"
msgstr ""
@@ -2772,6 +2678,31 @@ msgstr ""
msgid "Search by username, email, first name or last name"
msgstr ""
+#: tilavarauspalvelu/admin/origin_hauki_resource/admin.py
+msgid ""
+"OriginHaukiResources with this specific hash never have any opening hours."
+msgstr ""
+
+#: tilavarauspalvelu/admin/origin_hauki_resource/admin.py
+msgid "ID of the resource in Hauki."
+msgstr ""
+
+#: tilavarauspalvelu/admin/origin_hauki_resource/admin.py
+msgid ""
+"Hash of the opening hours. Used to determine if the opening hours have "
+"changed."
+msgstr ""
+
+#: tilavarauspalvelu/admin/origin_hauki_resource/admin.py
+msgid ""
+"All opening hours have been fetched from Hauki up until this date. Opening "
+"hours are fetched until the last day of the month two years from now."
+msgstr ""
+
+#: tilavarauspalvelu/admin/origin_hauki_resource/admin.py
+msgid "Reservable Time Spans updated."
+msgstr ""
+
#: tilavarauspalvelu/admin/payment_merchant/admin.py
msgid "The Paytrail Merchant ID should be a six-digit number."
msgstr ""
@@ -3027,6 +2958,71 @@ msgstr ""
msgid "Notification manager"
msgstr ""
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Open"
+msgstr ""
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Closed"
+msgstr ""
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Undefined"
+msgstr ""
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Self service"
+msgstr ""
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "With key"
+msgstr ""
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "With reservation"
+msgstr ""
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Open and reservable"
+msgstr ""
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "With key and reservation"
+msgstr ""
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Enter only"
+msgstr ""
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Exit only"
+msgstr ""
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Weather permitting"
+msgstr ""
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Not in use"
+msgstr ""
+
+#: tilavarauspalvelu/enums.py
+msgctxt "HaukiResourceState"
+msgid "Maintenance"
+msgstr ""
+
#: tilavarauspalvelu/models/email_template/model.py
msgid "Email type"
msgstr ""
@@ -3085,6 +3081,10 @@ msgstr ""
msgid "Must be the sum of net and vat amounts"
msgstr ""
+#: tilavarauspalvelu/models/reservable_time_span/model.py
+msgid "`start_datetime` must be before `end_datetime`."
+msgstr ""
+
#: tilavarauspalvelu/models/terms_of_use/model.py
msgctxt "singular"
msgid "terms of use"
diff --git a/opening_hours/admin/__init__.py b/opening_hours/admin/__init__.py
deleted file mode 100644
index 6fe5b400f..000000000
--- a/opening_hours/admin/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from .origin_hauki_resource import OriginHaukiResourceAdmin
-
-__all__ = [
- "OriginHaukiResourceAdmin",
-]
diff --git a/opening_hours/apps.py b/opening_hours/apps.py
deleted file mode 100644
index 07fdbf7da..000000000
--- a/opening_hours/apps.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from django.apps import AppConfig
-
-
-class OpeningHoursConfig(AppConfig):
- name = "opening_hours"
diff --git a/opening_hours/enums.py b/opening_hours/enums.py
deleted file mode 100644
index 7caf1d240..000000000
--- a/opening_hours/enums.py
+++ /dev/null
@@ -1,91 +0,0 @@
-from types import DynamicClassAttribute
-
-from django.utils.translation import pgettext_lazy
-from enumfields import Enum
-
-
-class HaukiResourceState(Enum):
- OPEN = "open"
- CLOSED = "closed"
- UNDEFINED = "undefined"
- SELF_SERVICE = "self_service"
- WITH_KEY = "with_key"
- WITH_RESERVATION = "with_reservation"
- OPEN_AND_RESERVABLE = "open_and_reservable"
- WITH_KEY_AND_RESERVATION = "with_key_and_reservation"
- ENTER_ONLY = "enter_only"
- EXIT_ONLY = "exit_only"
- WEATHER_PERMITTING = "weather_permitting"
- NOT_IN_USE = "not_in_use"
- MAINTENANCE = "maintenance"
-
- class Labels:
- OPEN = pgettext_lazy("HaukiResourceState", "Open")
- CLOSED = pgettext_lazy("HaukiResourceState", "Closed")
- UNDEFINED = pgettext_lazy("HaukiResourceState", "Undefined")
- SELF_SERVICE = pgettext_lazy("HaukiResourceState", "Self service")
- WITH_KEY = pgettext_lazy("HaukiResourceState", "With key")
- WITH_RESERVATION = pgettext_lazy("HaukiResourceState", "With reservation")
- OPEN_AND_RESERVABLE = pgettext_lazy("HaukiResourceState", "Open and reservable")
- WITH_KEY_AND_RESERVATION = pgettext_lazy("HaukiResourceState", "With key and reservation")
- ENTER_ONLY = pgettext_lazy("HaukiResourceState", "Enter only")
- EXIT_ONLY = pgettext_lazy("HaukiResourceState", "Exit only")
- WEATHER_PERMITTING = pgettext_lazy("HaukiResourceState", "Weather permitting")
- NOT_IN_USE = pgettext_lazy("HaukiResourceState", "Not in use")
- MAINTENANCE = pgettext_lazy("HaukiResourceState", "Maintenance")
-
- @classmethod
- def accessible_states(cls):
- """
- States indicating the space can be accessed in some way,
- whether the access is restricted (e.g. via key or reservation)
- or not.
- """
- return [
- cls.ENTER_ONLY,
- cls.OPEN,
- cls.OPEN_AND_RESERVABLE,
- cls.SELF_SERVICE,
- cls.WITH_KEY,
- cls.WITH_KEY_AND_RESERVATION,
- cls.WITH_RESERVATION,
- ]
-
- @classmethod
- def reservable_states(cls):
- """States indicating the space can be reserved in some way."""
- return [
- cls.OPEN_AND_RESERVABLE,
- cls.WITH_KEY_AND_RESERVATION,
- cls.WITH_RESERVATION,
- ]
-
- @classmethod
- def closed_states(cls):
- """States indicating the space is closed and inaccessible."""
- return [
- None,
- cls.CLOSED,
- cls.MAINTENANCE,
- cls.NOT_IN_USE,
- cls.UNDEFINED,
- ]
-
- @DynamicClassAttribute
- def is_accessible(self) -> bool:
- return self in HaukiResourceState.accessible_states()
-
- @DynamicClassAttribute
- def is_reservable(self) -> bool:
- return self in HaukiResourceState.reservable_states()
-
- @DynamicClassAttribute
- def is_closed(self) -> bool:
- return self in HaukiResourceState.closed_states()
-
- @classmethod
- def get(cls, state):
- try:
- return HaukiResourceState(state)
- except ValueError:
- return HaukiResourceState.UNDEFINED
diff --git a/opening_hours/errors.py b/opening_hours/errors.py
deleted file mode 100644
index bae8afb96..000000000
--- a/opening_hours/errors.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from utils.external_service.errors import ExternalServiceError
-
-
-class HaukiAPIError(ExternalServiceError):
- """Request succeeded but Hauki API returned an error"""
-
-
-class HaukiConfigurationError(ExternalServiceError):
- """Hauki API settings are not configured correctly"""
-
-
-class ReservableTimeSpanClientError(Exception):
- pass
-
-
-class ReservableTimeSpanClientValueError(ReservableTimeSpanClientError):
- pass
-
-
-class ReservableTimeSpanClientNothingToDoError(ReservableTimeSpanClientError):
- pass
diff --git a/opening_hours/migrations/0005_remove_reservabletimespan_resource_and_more.py b/opening_hours/migrations/0005_remove_reservabletimespan_resource_and_more.py
new file mode 100644
index 000000000..f82807213
--- /dev/null
+++ b/opening_hours/migrations/0005_remove_reservabletimespan_resource_and_more.py
@@ -0,0 +1,29 @@
+# Generated by Django 5.1.1 on 2024-09-19 11:15
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("opening_hours", "0004_alter_originhaukiresource_options"),
+ ("reservation_units", "0110_alter_reservationunit_origin_hauki_resource"),
+ ("tilavarauspalvelu", "0010_migrate_opening_hours"),
+ ]
+
+ operations = [
+ migrations.SeparateDatabaseAndState(
+ state_operations=[
+ migrations.RemoveField(
+ model_name="reservabletimespan",
+ name="resource",
+ ),
+ migrations.DeleteModel(
+ name="OriginHaukiResource",
+ ),
+ migrations.DeleteModel(
+ name="ReservableTimeSpan",
+ ),
+ ],
+ database_operations=[],
+ ),
+ ]
diff --git a/opening_hours/tasks.py b/opening_hours/tasks.py
deleted file mode 100644
index 76670bae8..000000000
--- a/opening_hours/tasks.py
+++ /dev/null
@@ -1,12 +0,0 @@
-import logging
-
-from config.celery import app
-from opening_hours.utils.hauki_resource_hash_updater import HaukiResourceHashUpdater
-
-logger = logging.getLogger(__name__)
-
-
-@app.task(name="update_origin_hauki_resource_reservable_time_spans")
-def update_origin_hauki_resource_reservable_time_spans() -> None:
- logger.info("Updating OriginHaukiResource reservable time spans...")
- HaukiResourceHashUpdater().run()
diff --git a/reservation_units/admin/reservation_unit/admin.py b/reservation_units/admin/reservation_unit/admin.py
index a332c2e5f..be45fe376 100644
--- a/reservation_units/admin/reservation_unit/admin.py
+++ b/reservation_units/admin/reservation_unit/admin.py
@@ -10,11 +10,11 @@
from applications.models import ApplicationRoundTimeSlot
from common.typing import WSGIRequest
-from opening_hours.utils.hauki_resource_hash_updater import HaukiResourceHashUpdater
from reservation_units.admin.reservation_unit.form import ApplicationRoundTimeSlotForm, ReservationUnitAdminForm
from reservation_units.enums import ReservationKind
from reservation_units.models import ReservationUnit, ReservationUnitImage, ReservationUnitPricing
from reservation_units.utils.export_data import ReservationUnitExporter
+from tilavarauspalvelu.utils.opening_hours.hauki_resource_hash_updater import HaukiResourceHashUpdater
__all__ = [
"ReservationUnitAdmin",
diff --git a/reservation_units/migrations/0110_alter_reservationunit_origin_hauki_resource.py b/reservation_units/migrations/0110_alter_reservationunit_origin_hauki_resource.py
new file mode 100644
index 000000000..d8d97ddcf
--- /dev/null
+++ b/reservation_units/migrations/0110_alter_reservationunit_origin_hauki_resource.py
@@ -0,0 +1,30 @@
+# Generated by Django 5.1.1 on 2024-09-19 11:15
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("reservation_units", "0109_alter_reservationunit_spaces_and_more"),
+ ("tilavarauspalvelu", "0010_migrate_opening_hours"),
+ ]
+
+ operations = [
+ migrations.SeparateDatabaseAndState(
+ state_operations=[
+ migrations.AlterField(
+ model_name="reservationunit",
+ name="origin_hauki_resource",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ related_name="reservation_units",
+ to="tilavarauspalvelu.originhaukiresource",
+ ),
+ ),
+ ],
+ database_operations=[],
+ ),
+ ]
diff --git a/reservation_units/models/reservation_unit.py b/reservation_units/models/reservation_unit.py
index 6f96c955e..4f2a3f510 100644
--- a/reservation_units/models/reservation_unit.py
+++ b/reservation_units/models/reservation_unit.py
@@ -25,10 +25,16 @@
from reservation_units.querysets import ReservationUnitQuerySet
if TYPE_CHECKING:
- from opening_hours.models import OriginHaukiResource
from reservation_units.models import ReservationUnitCancellationRule, ReservationUnitType
from reservations.models import ReservationMetadataSet
- from tilavarauspalvelu.models import PaymentAccounting, PaymentMerchant, PaymentProduct, TermsOfUse, Unit
+ from tilavarauspalvelu.models import (
+ OriginHaukiResource,
+ PaymentAccounting,
+ PaymentMerchant,
+ PaymentProduct,
+ TermsOfUse,
+ Unit,
+ )
__all__ = [
"ReservationUnit",
@@ -120,7 +126,7 @@ class ReservationUnit(SearchDocumentMixin, models.Model):
on_delete=models.SET_NULL,
)
origin_hauki_resource: OriginHaukiResource | None = models.ForeignKey(
- "opening_hours.OriginHaukiResource",
+ "tilavarauspalvelu.OriginHaukiResource",
related_name="reservation_units",
on_delete=models.SET_NULL,
blank=True,
diff --git a/reservation_units/utils/first_reservable_time_helper/first_reservable_time_helper.py b/reservation_units/utils/first_reservable_time_helper/first_reservable_time_helper.py
index 58dd64c91..35e887f00 100644
--- a/reservation_units/utils/first_reservable_time_helper/first_reservable_time_helper.py
+++ b/reservation_units/utils/first_reservable_time_helper/first_reservable_time_helper.py
@@ -14,13 +14,13 @@
from applications.enums import ApplicationRoundStatusChoice
from applications.models import ApplicationRound
from common.date_utils import local_datetime, local_datetime_max, local_datetime_min, local_start_of_day
-from opening_hours.models import ReservableTimeSpan
-from opening_hours.utils.time_span_element import TimeSpanElement
-from opening_hours.utils.time_span_element_utils import merge_overlapping_time_span_elements
from reservation_units.utils.first_reservable_time_helper.first_reservable_time_reservation_unit_helper import (
ReservationUnitFirstReservableTimeHelper,
)
from reservations.models import AffectingTimeSpan
+from tilavarauspalvelu.models import ReservableTimeSpan
+from tilavarauspalvelu.utils.opening_hours.time_span_element import TimeSpanElement
+from tilavarauspalvelu.utils.opening_hours.time_span_element_utils import merge_overlapping_time_span_elements
if TYPE_CHECKING:
from decimal import Decimal
diff --git a/reservation_units/utils/first_reservable_time_helper/first_reservable_time_reservable_time_span_helper.py b/reservation_units/utils/first_reservable_time_helper/first_reservable_time_reservable_time_span_helper.py
index 9967656f1..64dcf98e7 100644
--- a/reservation_units/utils/first_reservable_time_helper/first_reservable_time_reservable_time_span_helper.py
+++ b/reservation_units/utils/first_reservable_time_helper/first_reservable_time_reservable_time_span_helper.py
@@ -4,16 +4,16 @@
from datetime import datetime, timedelta
from typing import TYPE_CHECKING
-from opening_hours.utils.time_span_element_utils import override_reservable_with_closed_time_spans
from reservation_units.enums import ReservationStartInterval
from reservation_units.utils.first_reservable_time_helper.utils import ReservableTimeOutput
+from tilavarauspalvelu.utils.opening_hours.time_span_element_utils import override_reservable_with_closed_time_spans
if TYPE_CHECKING:
- from opening_hours.models import ReservableTimeSpan
- from opening_hours.utils.time_span_element import TimeSpanElement
from reservation_units.utils.first_reservable_time_helper.first_reservable_time_reservation_unit_helper import (
ReservationUnitFirstReservableTimeHelper,
)
+ from tilavarauspalvelu.models import ReservableTimeSpan
+ from tilavarauspalvelu.utils.opening_hours.time_span_element import TimeSpanElement
class ReservableTimeSpanFirstReservableTimeHelper:
diff --git a/reservation_units/utils/first_reservable_time_helper/first_reservable_time_reservation_unit_helper.py b/reservation_units/utils/first_reservable_time_helper/first_reservable_time_reservation_unit_helper.py
index 8e18f53d3..1df86ec44 100644
--- a/reservation_units/utils/first_reservable_time_helper/first_reservable_time_reservation_unit_helper.py
+++ b/reservation_units/utils/first_reservable_time_helper/first_reservable_time_reservation_unit_helper.py
@@ -4,13 +4,13 @@
from typing import TYPE_CHECKING
from common.date_utils import local_datetime, local_datetime_max, local_datetime_min, local_start_of_day
-from opening_hours.utils.time_span_element import TimeSpanElement
-from opening_hours.utils.time_span_element_utils import merge_overlapping_time_span_elements
from reservation_units.enums import ReservationStartInterval
from reservation_units.utils.first_reservable_time_helper.first_reservable_time_reservable_time_span_helper import (
ReservableTimeSpanFirstReservableTimeHelper,
)
from reservation_units.utils.first_reservable_time_helper.utils import ReservableTimeOutput
+from tilavarauspalvelu.utils.opening_hours.time_span_element import TimeSpanElement
+from tilavarauspalvelu.utils.opening_hours.time_span_element_utils import merge_overlapping_time_span_elements
if TYPE_CHECKING:
from reservation_units.models.reservation_unit import ReservationUnit
diff --git a/reservations/models/affecting_time_span.py b/reservations/models/affecting_time_span.py
index 3ed83e584..241872288 100644
--- a/reservations/models/affecting_time_span.py
+++ b/reservations/models/affecting_time_span.py
@@ -12,8 +12,8 @@
from django.utils.translation import gettext_lazy as _
from common.date_utils import DEFAULT_TIMEZONE, local_datetime, timedelta_to_json
-from opening_hours.utils.time_span_element import TimeSpanElement
from reservations.querysets import AffectingTimeSpanQuerySet
+from tilavarauspalvelu.utils.opening_hours.time_span_element import TimeSpanElement
from utils.sentry import SentryLogger
if TYPE_CHECKING:
diff --git a/tests/factories/opening_hours.py b/tests/factories/opening_hours.py
index a9ececbd9..42f2ac030 100644
--- a/tests/factories/opening_hours.py
+++ b/tests/factories/opening_hours.py
@@ -1,6 +1,6 @@
import factory
-from opening_hours.models import OriginHaukiResource, ReservableTimeSpan
+from tilavarauspalvelu.models import OriginHaukiResource, ReservableTimeSpan
from ._base import GenericDjangoModelFactory
diff --git a/tests/test_actions/test_reservation_unit_actions_hauki_exporter.py b/tests/test_actions/test_reservation_unit_actions_hauki_exporter.py
index 0b1d75ce5..d78b58d3f 100644
--- a/tests/test_actions/test_reservation_unit_actions_hauki_exporter.py
+++ b/tests/test_actions/test_reservation_unit_actions_hauki_exporter.py
@@ -1,10 +1,10 @@
import pytest
-from opening_hours.errors import HaukiAPIError
-from opening_hours.utils.hauki_api_client import HaukiAPIClient
from reservation_units.models import ReservationUnit
from tests.factories import OriginHaukiResourceFactory, ReservationUnitFactory
from tests.helpers import patch_method
+from tilavarauspalvelu.exceptions import HaukiAPIError
+from tilavarauspalvelu.utils.opening_hours.hauki_api_client import HaukiAPIClient
# Applied to all tests
pytestmark = [
diff --git a/tests/test_external_services/test_hauki/conftest.py b/tests/test_external_services/test_hauki/conftest.py
index 2b56fae18..f6c9e140b 100644
--- a/tests/test_external_services/test_hauki/conftest.py
+++ b/tests/test_external_services/test_hauki/conftest.py
@@ -9,10 +9,11 @@
@pytest.fixture(autouse=True)
def _force_HaukiAPIClient_to_be_mocked():
"""Force 'HaukiAPIClient.generic' to be mocked in all tests."""
- with mock.patch(
- "opening_hours.utils.hauki_api_client.HaukiAPIClient.generic",
- side_effect=NotImplementedError("'HaukiAPIClient.generic' must be mocked!"),
- ):
+ from tilavarauspalvelu.utils.opening_hours.hauki_api_client import HaukiAPIClient
+
+ path = HaukiAPIClient.__module__ + "." + HaukiAPIClient.__qualname__ + ".generic"
+ exception = NotImplementedError("'HaukiAPIClient.generic' must be mocked!")
+ with mock.patch(path, side_effect=exception):
yield
diff --git a/tests/test_external_services/test_hauki/test_hauki_link_generator.py b/tests/test_external_services/test_hauki/test_hauki_link_generator.py
index 8761e079a..2f88f314d 100644
--- a/tests/test_external_services/test_hauki/test_hauki_link_generator.py
+++ b/tests/test_external_services/test_hauki/test_hauki_link_generator.py
@@ -5,7 +5,7 @@
from django.conf import settings
from freezegun import freeze_time
-from opening_hours.utils.hauki_link_generator import generate_hauki_link
+from tilavarauspalvelu.utils.opening_hours.hauki_link_generator import generate_hauki_link
VALID_SIGNATURE = "87cbd7cc4f1730cb71094d2648e79045d0dde31e1025695b69f4d76c301e4f20"
ORGANIZATION = settings.HAUKI_ORGANISATION_ID
diff --git a/tests/test_external_services/test_hauki/test_hauki_resource_hash_updater.py b/tests/test_external_services/test_hauki/test_hauki_resource_hash_updater.py
index dae71dceb..16d85e2de 100644
--- a/tests/test_external_services/test_hauki/test_hauki_resource_hash_updater.py
+++ b/tests/test_external_services/test_hauki/test_hauki_resource_hash_updater.py
@@ -1,24 +1,27 @@
import datetime
+from typing import TYPE_CHECKING
import freezegun
import pytest
-from django.utils.timezone import get_default_timezone
-from opening_hours.models import OriginHaukiResource, ReservableTimeSpan
-from opening_hours.utils.hauki_api_client import HaukiAPIClient
-from opening_hours.utils.hauki_resource_hash_updater import HaukiResourceHashUpdater
-from opening_hours.utils.reservable_time_span_client import NEVER_ANY_OPENING_HOURS_HASH, ReservableTimeSpanClient
+from common.date_utils import DEFAULT_TIMEZONE
from tests.factories.opening_hours import OriginHaukiResourceFactory, ReservableTimeSpanFactory
from tests.helpers import patch_method
from tests.mocks import MockResponse
+from tilavarauspalvelu.constants import NEVER_ANY_OPENING_HOURS_HASH
+from tilavarauspalvelu.models import ReservableTimeSpan
+from tilavarauspalvelu.utils.opening_hours.hauki_api_client import HaukiAPIClient
+from tilavarauspalvelu.utils.opening_hours.hauki_resource_hash_updater import HaukiResourceHashUpdater
+from tilavarauspalvelu.utils.opening_hours.reservable_time_span_client import ReservableTimeSpanClient
+
+if TYPE_CHECKING:
+ from tilavarauspalvelu.models import OriginHaukiResource
# Applied to all tests
pytestmark = [
pytest.mark.django_db,
]
-DEFAULT_TIMEZONE = get_default_timezone()
-
############
# __init__ #
############
diff --git a/tests/test_external_services/test_hauki/test_merge_overlapping_time_span_elements.py b/tests/test_external_services/test_hauki/test_merge_overlapping_time_span_elements.py
index a9d3016f1..b743c4aee 100644
--- a/tests/test_external_services/test_hauki/test_merge_overlapping_time_span_elements.py
+++ b/tests/test_external_services/test_hauki/test_merge_overlapping_time_span_elements.py
@@ -1,8 +1,8 @@
from datetime import timedelta
-from opening_hours.utils.time_span_element import TimeSpanElement
-from opening_hours.utils.time_span_element_utils import merge_overlapping_time_span_elements
from tests.test_external_services.test_hauki.test_reservable_time_spans_client import _get_date
+from tilavarauspalvelu.utils.opening_hours.time_span_element import TimeSpanElement
+from tilavarauspalvelu.utils.opening_hours.time_span_element_utils import merge_overlapping_time_span_elements
# No buffers
diff --git a/tests/test_external_services/test_hauki/test_override_reservable_with_closed_time_spans.py b/tests/test_external_services/test_hauki/test_override_reservable_with_closed_time_spans.py
index 15135cc87..64975fab3 100644
--- a/tests/test_external_services/test_hauki/test_override_reservable_with_closed_time_spans.py
+++ b/tests/test_external_services/test_hauki/test_override_reservable_with_closed_time_spans.py
@@ -1,10 +1,10 @@
-from opening_hours.utils.time_span_element import TimeSpanElement
-from opening_hours.utils.time_span_element_utils import override_reservable_with_closed_time_spans
from tests.test_external_services.test_hauki.test_reservable_time_spans_client import (
_get_date,
_get_normalised_time_spans,
_get_reservable_and_closed_time_spans,
)
+from tilavarauspalvelu.utils.opening_hours.time_span_element import TimeSpanElement
+from tilavarauspalvelu.utils.opening_hours.time_span_element_utils import override_reservable_with_closed_time_spans
def test__override_reservable_with_closed_time_spans():
diff --git a/tests/test_external_services/test_hauki/test_reservable_time_spans_client.py b/tests/test_external_services/test_hauki/test_reservable_time_spans_client.py
index f613a95d9..5c077ca39 100644
--- a/tests/test_external_services/test_hauki/test_reservable_time_spans_client.py
+++ b/tests/test_external_services/test_hauki/test_reservable_time_spans_client.py
@@ -6,21 +6,21 @@
from freezegun import freeze_time
from common.date_utils import DEFAULT_TIMEZONE
-from opening_hours.enums import HaukiResourceState
-from opening_hours.errors import ReservableTimeSpanClientNothingToDoError
-from opening_hours.models import ReservableTimeSpan
-from opening_hours.utils.hauki_api_client import HaukiAPIClient
-from opening_hours.utils.hauki_api_types import (
+from tests.helpers import patch_method
+from tests.mocks import MockResponse
+from tilavarauspalvelu.enums import HaukiResourceState
+from tilavarauspalvelu.exceptions import ReservableTimeSpanClientNothingToDoError
+from tilavarauspalvelu.models import ReservableTimeSpan
+from tilavarauspalvelu.utils.opening_hours.hauki_api_client import HaukiAPIClient
+from tilavarauspalvelu.utils.opening_hours.hauki_api_types import (
HaukiAPIOpeningHoursResponseDate,
HaukiAPIOpeningHoursResponseItem,
HaukiAPIOpeningHoursResponseResource,
HaukiAPIOpeningHoursResponseTime,
HaukiTranslatedField,
)
-from opening_hours.utils.reservable_time_span_client import ReservableTimeSpanClient
-from opening_hours.utils.time_span_element import TimeSpanElement
-from tests.helpers import patch_method
-from tests.mocks import MockResponse
+from tilavarauspalvelu.utils.opening_hours.reservable_time_span_client import ReservableTimeSpanClient
+from tilavarauspalvelu.utils.opening_hours.time_span_element import TimeSpanElement
# Applied to all tests
pytestmark = [
diff --git a/tests/test_external_services/test_hauki/test_summaries.py b/tests/test_external_services/test_hauki/test_summaries.py
index 4819e7a2f..8305abccb 100644
--- a/tests/test_external_services/test_hauki/test_summaries.py
+++ b/tests/test_external_services/test_hauki/test_summaries.py
@@ -3,8 +3,8 @@
import pytest
from django.utils.timezone import get_default_timezone
-from opening_hours.utils.summaries import get_resources_total_hours_per_resource
from tests.factories import OriginHaukiResourceFactory, ReservableTimeSpanFactory
+from tilavarauspalvelu.utils.opening_hours.summaries import get_resources_total_hours_per_resource
# Applied to all tests
pytestmark = [
diff --git a/tests/test_external_services/test_hauki/test_time_span_element.py b/tests/test_external_services/test_hauki/test_time_span_element.py
index af4f9352f..23d8afdae 100644
--- a/tests/test_external_services/test_hauki/test_time_span_element.py
+++ b/tests/test_external_services/test_hauki/test_time_span_element.py
@@ -5,10 +5,10 @@
from django.utils.timezone import get_default_timezone
from graphene_django_extensions.testing.utils import parametrize_helper
-from opening_hours.enums import HaukiResourceState
-from opening_hours.utils.hauki_api_types import HaukiAPIOpeningHoursResponseTime
-from opening_hours.utils.time_span_element import TimeSpanElement
from tests.test_external_services.test_hauki.test_reservable_time_spans_client import _get_date
+from tilavarauspalvelu.enums import HaukiResourceState
+from tilavarauspalvelu.utils.opening_hours.hauki_api_types import HaukiAPIOpeningHoursResponseTime
+from tilavarauspalvelu.utils.opening_hours.time_span_element import TimeSpanElement
DEFAULT_TIMEZONE = get_default_timezone()
diff --git a/tests/test_external_services/test_tprek/test_tprek_unit_hauki_resource_importer.py b/tests/test_external_services/test_tprek/test_tprek_unit_hauki_resource_importer.py
index debc06a77..584e7e2e5 100644
--- a/tests/test_external_services/test_tprek/test_tprek_unit_hauki_resource_importer.py
+++ b/tests/test_external_services/test_tprek/test_tprek_unit_hauki_resource_importer.py
@@ -1,10 +1,10 @@
import pytest
-from opening_hours.utils.hauki_api_client import HaukiAPIClient
from tests.factories import OriginHaukiResourceFactory, UnitFactory
from tests.helpers import patch_method
from tests.mocks import MockResponse
from tilavarauspalvelu.utils.importers.tprek_unit_importer import TprekUnitHaukiResourceIdImporter
+from tilavarauspalvelu.utils.opening_hours.hauki_api_client import HaukiAPIClient
pytestmark = [
pytest.mark.django_db,
diff --git a/tests/test_graphql_api/test_reservation_unit/test_create.py b/tests/test_graphql_api/test_reservation_unit/test_create.py
index ed95e5981..e08778092 100644
--- a/tests/test_graphql_api/test_reservation_unit/test_create.py
+++ b/tests/test_graphql_api/test_reservation_unit/test_create.py
@@ -3,13 +3,13 @@
import pytest
from applications.enums import WeekdayChoice
-from opening_hours.errors import HaukiAPIError
-from opening_hours.utils.hauki_api_client import HaukiAPIClient
-from opening_hours.utils.hauki_api_types import HaukiAPIResource, HaukiTranslatedField
from reservation_units.enums import ReservationKind
from reservation_units.models import ReservationUnit
from tests.factories import UnitFactory
from tests.helpers import patch_method
+from tilavarauspalvelu.exceptions import HaukiAPIError
+from tilavarauspalvelu.utils.opening_hours.hauki_api_client import HaukiAPIClient
+from tilavarauspalvelu.utils.opening_hours.hauki_api_types import HaukiAPIResource, HaukiTranslatedField
from .helpers import CREATE_MUTATION, get_create_non_draft_input_data
diff --git a/tests/test_graphql_api/test_reservation_unit/test_hauki_integration.py b/tests/test_graphql_api/test_reservation_unit/test_hauki_integration.py
index 378359669..ff8e1ddfb 100644
--- a/tests/test_graphql_api/test_reservation_unit/test_hauki_integration.py
+++ b/tests/test_graphql_api/test_reservation_unit/test_hauki_integration.py
@@ -5,10 +5,10 @@
from graphql_relay import to_global_id
from actions.reservation_unit import ReservationUnitHaukiExporter
-from opening_hours.errors import HaukiAPIError
-from opening_hours.utils.hauki_resource_hash_updater import HaukiResourceHashUpdater
from tests.factories import OriginHaukiResourceFactory, ReservationUnitFactory
from tests.helpers import patch_method
+from tilavarauspalvelu.exceptions import HaukiAPIError
+from tilavarauspalvelu.utils.opening_hours.hauki_resource_hash_updater import HaukiResourceHashUpdater
from .helpers import UPDATE_MUTATION, get_draft_update_input_data, reservation_unit_query
diff --git a/tests/test_querysets/test_reservable_time_span.py b/tests/test_querysets/test_reservable_time_span.py
index 5848e0260..c651b358f 100644
--- a/tests/test_querysets/test_reservable_time_span.py
+++ b/tests/test_querysets/test_reservable_time_span.py
@@ -4,8 +4,8 @@
import pytest
from graphene_django_extensions.testing.utils import parametrize_helper
-from opening_hours.models import ReservableTimeSpan
from tests.factories import OriginHaukiResourceFactory, ReservableTimeSpanFactory
+from tilavarauspalvelu.models import ReservableTimeSpan
# Applied to all tests
pytestmark = [
diff --git a/tests/test_querysets/test_reservation_querysets.py b/tests/test_querysets/test_reservation_querysets.py
index 8932d2fb9..5645309b3 100644
--- a/tests/test_querysets/test_reservation_querysets.py
+++ b/tests/test_querysets/test_reservation_querysets.py
@@ -4,11 +4,11 @@
import pytest
from common.date_utils import DEFAULT_TIMEZONE, local_date
-from opening_hours.utils.time_span_element import TimeSpanElement
from reservation_units.models import ReservationUnit, ReservationUnitHierarchy
from reservations.enums import ReservationStateChoice, ReservationTypeChoice
from reservations.models import AffectingTimeSpan, Reservation
from tests.factories import ReservationFactory, ReservationUnitFactory, ResourceFactory, SpaceFactory, UnitFactory
+from tilavarauspalvelu.utils.opening_hours.time_span_element import TimeSpanElement
# Applied to all tests
pytestmark = [
diff --git a/tests/test_utils/test_first_reservable_time_helper.py b/tests/test_utils/test_first_reservable_time_helper.py
index 30ceb04c4..6824f665a 100644
--- a/tests/test_utils/test_first_reservable_time_helper.py
+++ b/tests/test_utils/test_first_reservable_time_helper.py
@@ -7,7 +7,6 @@
from django.utils.timezone import get_default_timezone
from graphene_django_extensions.testing.utils import parametrize_helper
-from opening_hours.utils.time_span_element import TimeSpanElement
from reservation_units.enums import ReservationStartInterval
from reservation_units.models import ReservationUnit
from reservation_units.utils.first_reservable_time_helper.first_reservable_time_helper import FirstReservableTimeHelper
@@ -18,6 +17,7 @@
ReservationUnitFirstReservableTimeHelper,
)
from tests.factories import OriginHaukiResourceFactory, ReservableTimeSpanFactory, ReservationUnitFactory
+from tilavarauspalvelu.utils.opening_hours.time_span_element import TimeSpanElement
DEFAULT_TIMEZONE = get_default_timezone()
diff --git a/tests/test_utils/test_generate_reservations_from_allocations.py b/tests/test_utils/test_generate_reservations_from_allocations.py
index 7a2430ebc..b225d474b 100644
--- a/tests/test_utils/test_generate_reservations_from_allocations.py
+++ b/tests/test_utils/test_generate_reservations_from_allocations.py
@@ -7,9 +7,6 @@
from applications.enums import ApplicantTypeChoice, Weekday
from applications.tasks import generate_reservation_series_from_allocations
from common.date_utils import DEFAULT_TIMEZONE, combine, local_date, local_datetime, local_iso_format
-from opening_hours.enums import HaukiResourceState
-from opening_hours.utils.hauki_api_client import HaukiAPIClient
-from opening_hours.utils.hauki_api_types import HaukiAPIDatePeriod
from reservation_units.models import ReservationUnitHierarchy
from reservations.enums import (
CustomerTypeChoice,
@@ -20,6 +17,9 @@
from reservations.models import AffectingTimeSpan, RecurringReservation, RejectedOccurrence, Reservation
from tests.factories import AllocatedTimeSlotFactory, ReservationFactory
from tests.helpers import patch_method
+from tilavarauspalvelu.enums import HaukiResourceState
+from tilavarauspalvelu.utils.opening_hours.hauki_api_client import HaukiAPIClient
+from tilavarauspalvelu.utils.opening_hours.hauki_api_types import HaukiAPIDatePeriod
from utils.sentry import SentryLogger
pytestmark = [
diff --git a/tilavarauspalvelu/admin/__init__.py b/tilavarauspalvelu/admin/__init__.py
index b2f3f30a0..6cc1b760c 100644
--- a/tilavarauspalvelu/admin/__init__.py
+++ b/tilavarauspalvelu/admin/__init__.py
@@ -1,5 +1,6 @@
from .email_template.admin import EmailTemplateAdmin
from .general_role.admin import GeneralRoleAdmin
+from .origin_hauki_resource.admin import OriginHaukiResourceAdmin
from .payment_accounting.admin import PaymentAccountingAdmin
from .payment_merchant.admin import PaymentMerchantAdmin
from .payment_order.admin import PaymentOrderAdmin
@@ -16,6 +17,7 @@
__all__ = [
"EmailTemplateAdmin",
"GeneralRoleAdmin",
+ "OriginHaukiResourceAdmin",
"PaymentAccountingAdmin",
"PaymentMerchantAdmin",
"PaymentOrderAdmin",
diff --git a/tilavarauspalvelu/admin/hauki_resource/admin.py b/tilavarauspalvelu/admin/hauki_resource/admin.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/opening_hours/management/__init__.py b/tilavarauspalvelu/admin/origin_hauki_resource/__init__.py
similarity index 100%
rename from opening_hours/management/__init__.py
rename to tilavarauspalvelu/admin/origin_hauki_resource/__init__.py
diff --git a/opening_hours/admin/origin_hauki_resource.py b/tilavarauspalvelu/admin/origin_hauki_resource/admin.py
similarity index 75%
rename from opening_hours/admin/origin_hauki_resource.py
rename to tilavarauspalvelu/admin/origin_hauki_resource/admin.py
index 38ba1bb50..c9324746b 100644
--- a/opening_hours/admin/origin_hauki_resource.py
+++ b/tilavarauspalvelu/admin/origin_hauki_resource/admin.py
@@ -3,51 +3,20 @@
from django import forms
from django.contrib import admin
from django.db.models import Count, QuerySet
-from django.urls import reverse
-from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
from common.typing import WSGIRequest
-from opening_hours.models import OriginHaukiResource, ReservableTimeSpan
-from opening_hours.utils.hauki_resource_hash_updater import HaukiResourceHashUpdater
-from opening_hours.utils.reservable_time_span_client import NEVER_ANY_OPENING_HOURS_HASH
-from reservation_units.models import ReservationUnit
+from tilavarauspalvelu.admin.reservable_time_span.admin import ReservableTimeSpanInline
+from tilavarauspalvelu.admin.reservation_unit.admin import ReservationUnitInline
+from tilavarauspalvelu.constants import NEVER_ANY_OPENING_HOURS_HASH
+from tilavarauspalvelu.models import OriginHaukiResource
+from tilavarauspalvelu.utils.opening_hours.hauki_resource_hash_updater import HaukiResourceHashUpdater
__all__ = [
"OriginHaukiResourceAdmin",
]
-class ReservationUnitInline(admin.TabularInline):
- model = ReservationUnit
- fields = ["id", "reservation_unit_link"]
- readonly_fields = fields
- can_delete = False
- extra = 0
-
- def has_add_permission(self, request, obj=None) -> bool:
- return False
-
- def reservation_unit_link(self, obj):
- url = reverse("admin:reservation_units_reservationunit_change", args=(obj.pk,))
-
- return format_html(f"{obj.name_fi}")
-
-
-class ReservableTimeSpanInline(admin.TabularInline):
- model = ReservableTimeSpan
- fields = ["time_span_str"]
- readonly_fields = fields
- can_delete = False
- extra = 0
-
- def has_add_permission(self, request, obj=None) -> bool:
- return False
-
- def time_span_str(self, obj: ReservableTimeSpan) -> str:
- return obj.get_datetime_str()
-
-
class OriginHaukiResourceAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
diff --git a/opening_hours/management/commands/__init__.py b/tilavarauspalvelu/admin/reservable_time_span/__init__.py
similarity index 100%
rename from opening_hours/management/commands/__init__.py
rename to tilavarauspalvelu/admin/reservable_time_span/__init__.py
diff --git a/tilavarauspalvelu/admin/reservable_time_span/admin.py b/tilavarauspalvelu/admin/reservable_time_span/admin.py
new file mode 100644
index 000000000..0e74d9000
--- /dev/null
+++ b/tilavarauspalvelu/admin/reservable_time_span/admin.py
@@ -0,0 +1,17 @@
+from django.contrib import admin
+
+from tilavarauspalvelu.models import ReservableTimeSpan
+
+
+class ReservableTimeSpanInline(admin.TabularInline):
+ model = ReservableTimeSpan
+ fields = ["time_span_str"]
+ readonly_fields = fields
+ can_delete = False
+ extra = 0
+
+ def has_add_permission(self, request, obj=None) -> bool:
+ return False
+
+ def time_span_str(self, obj: ReservableTimeSpan) -> str:
+ return obj.get_datetime_str()
diff --git a/tilavarauspalvelu/admin/reservable_timespan/admin.py b/tilavarauspalvelu/admin/reservable_timespan/admin.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tilavarauspalvelu/admin/reservation_unit/admin.py b/tilavarauspalvelu/admin/reservation_unit/admin.py
index e69de29bb..9909ebdaf 100644
--- a/tilavarauspalvelu/admin/reservation_unit/admin.py
+++ b/tilavarauspalvelu/admin/reservation_unit/admin.py
@@ -0,0 +1,21 @@
+from django.contrib import admin
+from django.urls import reverse
+from django.utils.html import format_html
+
+from reservation_units.models import ReservationUnit
+
+
+class ReservationUnitInline(admin.TabularInline):
+ model = ReservationUnit
+ fields = ["id", "reservation_unit_link"]
+ readonly_fields = fields
+ can_delete = False
+ extra = 0
+
+ def has_add_permission(self, request, obj=None) -> bool:
+ return False
+
+ def reservation_unit_link(self, obj):
+ url = reverse("admin:reservation_units_reservationunit_change", args=(obj.pk,))
+
+ return format_html(f"{obj.name_fi}")
diff --git a/tilavarauspalvelu/api/graphql/types/recurring_reservation/serializers.py b/tilavarauspalvelu/api/graphql/types/recurring_reservation/serializers.py
index b81e02258..95e77cb6c 100644
--- a/tilavarauspalvelu/api/graphql/types/recurring_reservation/serializers.py
+++ b/tilavarauspalvelu/api/graphql/types/recurring_reservation/serializers.py
@@ -13,7 +13,6 @@
from applications.enums import WeekdayChoice
from common.date_utils import local_date
from common.fields.serializer import CurrentUserDefaultNullable, input_only_field
-from opening_hours.utils.reservable_time_span_client import ReservableTimeSpanClient
from reservation_units.enums import ReservationStartInterval
from reservation_units.models import ReservationUnit
from reservations.enums import ReservationStateChoice, ReservationTypeStaffChoice
@@ -25,6 +24,8 @@
"RecurringReservationCreateSerializer",
]
+from tilavarauspalvelu.utils.opening_hours.reservable_time_span_client import ReservableTimeSpanClient
+
class RecurringReservationCreateSerializer(NestingModelSerializer):
instance: None
diff --git a/tilavarauspalvelu/api/graphql/types/reservation_unit/serializers.py b/tilavarauspalvelu/api/graphql/types/reservation_unit/serializers.py
index 9e710196d..f305321a0 100644
--- a/tilavarauspalvelu/api/graphql/types/reservation_unit/serializers.py
+++ b/tilavarauspalvelu/api/graphql/types/reservation_unit/serializers.py
@@ -8,7 +8,6 @@
from rest_framework.exceptions import ValidationError
from applications.enums import WeekdayChoice
-from opening_hours.utils.hauki_resource_hash_updater import HaukiResourceHashUpdater
from reservation_units.enums import PricingStatus, ReservationStartInterval
from reservation_units.models import ReservationUnit, ReservationUnitPricing
from reservation_units.utils.reservation_unit_pricing_helper import ReservationUnitPricingHelper
@@ -18,6 +17,7 @@
)
from tilavarauspalvelu.api.graphql.types.reservation_unit_image.serializers import ReservationUnitImageFieldSerializer
from tilavarauspalvelu.api.graphql.types.reservation_unit_pricing.serializers import ReservationUnitPricingSerializer
+from tilavarauspalvelu.utils.opening_hours.hauki_resource_hash_updater import HaukiResourceHashUpdater
from utils.external_service.errors import ExternalServiceError
if TYPE_CHECKING:
diff --git a/tilavarauspalvelu/api/graphql/types/reservation_unit/types.py b/tilavarauspalvelu/api/graphql/types/reservation_unit/types.py
index 2791a205d..2607bc674 100644
--- a/tilavarauspalvelu/api/graphql/types/reservation_unit/types.py
+++ b/tilavarauspalvelu/api/graphql/types/reservation_unit/types.py
@@ -13,15 +13,14 @@
from common.db import SubqueryCount
from common.typing import GQLInfo
-from opening_hours.models import OriginHaukiResource
-from opening_hours.utils.hauki_link_generator import generate_hauki_link
from reservation_units.enums import ReservationUnitPublishingState, ReservationUnitReservationState
from reservation_units.models import ReservationUnit
from reservations.enums import ReservationTypeChoice
from reservations.models import Reservation
from tilavarauspalvelu.api.graphql.types.location.types import LocationNode
from tilavarauspalvelu.api.graphql.types.reservation.types import ReservationNode
-from tilavarauspalvelu.models import Location, PaymentMerchant, Space, Unit
+from tilavarauspalvelu.models import Location, OriginHaukiResource, PaymentMerchant, Space, Unit
+from tilavarauspalvelu.utils.opening_hours.hauki_link_generator import generate_hauki_link
from .filtersets import ReservationUnitFilterSet
from .permissions import ReservationUnitPermission
diff --git a/tilavarauspalvelu/constants.py b/tilavarauspalvelu/constants.py
index 3a8951fcb..1b21f6991 100644
--- a/tilavarauspalvelu/constants.py
+++ b/tilavarauspalvelu/constants.py
@@ -10,3 +10,8 @@
# The coordinates in this coordinate system are numbers in the range of
# -90.0000 to 90.0000 for latitude and -180.0000 to 180.0000 for longitude.
COORDINATE_SYSTEM_ID: int = 4326
+
+
+# Hash value for when there are never any opening hours
+# See https://github.com/City-of-Helsinki/hauki `hours.models.Resource._get_date_periods_as_hash`
+NEVER_ANY_OPENING_HOURS_HASH = "d41d8cd98f00b204e9800998ecf8427e" # md5(b"").hexdigest()
diff --git a/tilavarauspalvelu/enums.py b/tilavarauspalvelu/enums.py
index acfc486c3..153e4738a 100644
--- a/tilavarauspalvelu/enums.py
+++ b/tilavarauspalvelu/enums.py
@@ -2,11 +2,13 @@
import enum
from inspect import cleandoc
+from types import DynamicClassAttribute
from django.db import models
from django.utils.functional import classproperty
from django.utils.translation import gettext_lazy as _
from django.utils.translation import pgettext_lazy
+from enumfields import Enum
from tilavarauspalvelu.typing import permission
@@ -227,3 +229,90 @@ class EmailType(models.TextChoices):
RESERVATION_WITH_PIN_CONFIRMED = "reservation_with_pin_confirmed"
STAFF_NOTIFICATION_RESERVATION_MADE = "staff_notification_reservation_made"
STAFF_NOTIFICATION_RESERVATION_REQUIRES_HANDLING = "staff_notification_reservation_requires_handling"
+
+
+class HaukiResourceState(Enum):
+ OPEN = "open"
+ CLOSED = "closed"
+ UNDEFINED = "undefined"
+ SELF_SERVICE = "self_service"
+ WITH_KEY = "with_key"
+ WITH_RESERVATION = "with_reservation"
+ OPEN_AND_RESERVABLE = "open_and_reservable"
+ WITH_KEY_AND_RESERVATION = "with_key_and_reservation"
+ ENTER_ONLY = "enter_only"
+ EXIT_ONLY = "exit_only"
+ WEATHER_PERMITTING = "weather_permitting"
+ NOT_IN_USE = "not_in_use"
+ MAINTENANCE = "maintenance"
+
+ class Labels:
+ OPEN = pgettext_lazy("HaukiResourceState", "Open")
+ CLOSED = pgettext_lazy("HaukiResourceState", "Closed")
+ UNDEFINED = pgettext_lazy("HaukiResourceState", "Undefined")
+ SELF_SERVICE = pgettext_lazy("HaukiResourceState", "Self service")
+ WITH_KEY = pgettext_lazy("HaukiResourceState", "With key")
+ WITH_RESERVATION = pgettext_lazy("HaukiResourceState", "With reservation")
+ OPEN_AND_RESERVABLE = pgettext_lazy("HaukiResourceState", "Open and reservable")
+ WITH_KEY_AND_RESERVATION = pgettext_lazy("HaukiResourceState", "With key and reservation")
+ ENTER_ONLY = pgettext_lazy("HaukiResourceState", "Enter only")
+ EXIT_ONLY = pgettext_lazy("HaukiResourceState", "Exit only")
+ WEATHER_PERMITTING = pgettext_lazy("HaukiResourceState", "Weather permitting")
+ NOT_IN_USE = pgettext_lazy("HaukiResourceState", "Not in use")
+ MAINTENANCE = pgettext_lazy("HaukiResourceState", "Maintenance")
+
+ @classmethod
+ def accessible_states(cls):
+ """
+ States indicating the space can be accessed in some way,
+ whether the access is restricted (e.g. via key or reservation)
+ or not.
+ """
+ return [
+ cls.ENTER_ONLY,
+ cls.OPEN,
+ cls.OPEN_AND_RESERVABLE,
+ cls.SELF_SERVICE,
+ cls.WITH_KEY,
+ cls.WITH_KEY_AND_RESERVATION,
+ cls.WITH_RESERVATION,
+ ]
+
+ @classmethod
+ def reservable_states(cls):
+ """States indicating the space can be reserved in some way."""
+ return [
+ cls.OPEN_AND_RESERVABLE,
+ cls.WITH_KEY_AND_RESERVATION,
+ cls.WITH_RESERVATION,
+ ]
+
+ @classmethod
+ def closed_states(cls):
+ """States indicating the space is closed and inaccessible."""
+ return [
+ None,
+ cls.CLOSED,
+ cls.MAINTENANCE,
+ cls.NOT_IN_USE,
+ cls.UNDEFINED,
+ ]
+
+ @DynamicClassAttribute
+ def is_accessible(self) -> bool:
+ return self in HaukiResourceState.accessible_states()
+
+ @DynamicClassAttribute
+ def is_reservable(self) -> bool:
+ return self in HaukiResourceState.reservable_states()
+
+ @DynamicClassAttribute
+ def is_closed(self) -> bool:
+ return self in HaukiResourceState.closed_states()
+
+ @classmethod
+ def get(cls, state):
+ try:
+ return HaukiResourceState(state)
+ except ValueError:
+ return HaukiResourceState.UNDEFINED
diff --git a/tilavarauspalvelu/exceptions.py b/tilavarauspalvelu/exceptions.py
index 49c5870e8..cb40e64d2 100644
--- a/tilavarauspalvelu/exceptions.py
+++ b/tilavarauspalvelu/exceptions.py
@@ -1,3 +1,6 @@
+from utils.external_service.errors import ExternalServiceError
+
+
class SendEmailNotificationError(Exception):
pass
@@ -10,3 +13,23 @@ class EmailTemplateValidationError(Exception):
def __init__(self, *args, **kwargs) -> None:
if len(args) > 0:
self.message = args[0]
+
+
+class HaukiAPIError(ExternalServiceError):
+ """Request succeeded but Hauki API returned an error"""
+
+
+class HaukiConfigurationError(ExternalServiceError):
+ """Hauki API settings are not configured correctly"""
+
+
+class ReservableTimeSpanClientError(Exception):
+ pass
+
+
+class ReservableTimeSpanClientValueError(ReservableTimeSpanClientError):
+ pass
+
+
+class ReservableTimeSpanClientNothingToDoError(ReservableTimeSpanClientError):
+ pass
diff --git a/opening_hours/management/commands/update_hauki_hashes.py b/tilavarauspalvelu/management/commands/update_hauki_hashes.py
similarity index 76%
rename from opening_hours/management/commands/update_hauki_hashes.py
rename to tilavarauspalvelu/management/commands/update_hauki_hashes.py
index e1a97541e..7a1f909fd 100644
--- a/opening_hours/management/commands/update_hauki_hashes.py
+++ b/tilavarauspalvelu/management/commands/update_hauki_hashes.py
@@ -3,7 +3,7 @@
from django.core.management.base import BaseCommand
-from opening_hours.utils.hauki_resource_hash_updater import HaukiResourceHashUpdater
+from tilavarauspalvelu.utils.opening_hours.hauki_resource_hash_updater import HaukiResourceHashUpdater
logger = logging.getLogger(__name__)
diff --git a/tilavarauspalvelu/migrations/0010_migrate_opening_hours.py b/tilavarauspalvelu/migrations/0010_migrate_opening_hours.py
new file mode 100644
index 000000000..d2863c466
--- /dev/null
+++ b/tilavarauspalvelu/migrations/0010_migrate_opening_hours.py
@@ -0,0 +1,94 @@
+# Generated by Django 5.1.1 on 2024-09-19 11:15
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+from utils.migration import TestOnlyRunBefore
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("tilavarauspalvelu", "0009_migrate_email_template"),
+ ("applications", "0094_alter_applicationround_terms_of_use"),
+ ("common", "0012_sqllog_stack_info"),
+ ("email_notification", "0010_delete_emailtemplate"),
+ ("merchants", "0019_delete_paymentaccounting_and_more"),
+ ("opening_hours", "0004_alter_originhaukiresource_options"),
+ ("permissions", "0035_remove_unitrole_assigner_remove_unitrole_unit_groups_and_more"),
+ ("reservation_units", "0109_alter_reservationunit_spaces_and_more"),
+ ("reservations", "0082_remove_net_prices"),
+ ("resources", "0011_delete_resource"),
+ ("services", "0006_delete_service"),
+ ("spaces", "0042_delete_space_delete_unit_delete_unitgroup"),
+ ("terms_of_use", "0006_delete_termsofuse"),
+ ("users", "0017_remove_personalinfoviewlog_user_and_more"),
+ ]
+
+ run_before = TestOnlyRunBefore(
+ run_before=[
+ ("social_django", "0013_migrate_extra_data"),
+ ],
+ )
+
+ operations = [
+ migrations.SeparateDatabaseAndState(
+ state_operations=[
+ migrations.CreateModel(
+ name="OriginHaukiResource",
+ fields=[
+ ("id", models.IntegerField(primary_key=True, serialize=False, unique=True)),
+ ("opening_hours_hash", models.CharField(blank=True, max_length=64)),
+ ("latest_fetched_date", models.DateField(blank=True, null=True)),
+ ],
+ options={
+ "db_table": "origin_hauki_resource",
+ "ordering": ["pk"],
+ "base_manager_name": "objects",
+ },
+ ),
+ migrations.AlterField(
+ model_name="unit",
+ name="origin_hauki_resource",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ related_name="units",
+ to="tilavarauspalvelu.originhaukiresource",
+ ),
+ ),
+ migrations.CreateModel(
+ name="ReservableTimeSpan",
+ fields=[
+ (
+ "id",
+ models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"),
+ ),
+ ("start_datetime", models.DateTimeField()),
+ ("end_datetime", models.DateTimeField()),
+ (
+ "resource",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="reservable_time_spans",
+ to="tilavarauspalvelu.originhaukiresource",
+ ),
+ ),
+ ],
+ options={
+ "db_table": "reservable_time_span",
+ "ordering": ["resource", "start_datetime", "end_datetime"],
+ "base_manager_name": "objects",
+ "constraints": [
+ models.CheckConstraint(
+ condition=models.Q(("start_datetime__lt", models.F("end_datetime"))),
+ name="reservable_time_span_start_before_end",
+ violation_error_message="`start_datetime` must be before `end_datetime`.",
+ )
+ ],
+ },
+ ),
+ ],
+ database_operations=[],
+ ),
+ ]
diff --git a/tilavarauspalvelu/models/__init__.py b/tilavarauspalvelu/models/__init__.py
index 6ad43cdc4..0c68bfa8e 100644
--- a/tilavarauspalvelu/models/__init__.py
+++ b/tilavarauspalvelu/models/__init__.py
@@ -2,12 +2,14 @@
from .email_template.model import EmailTemplate
from .general_role.model import GeneralRole
from .location.model import Location
+from .origin_hauki_resource.model import OriginHaukiResource
from .payment_accounting.model import PaymentAccounting
from .payment_merchant.model import PaymentMerchant
from .payment_order.model import PaymentOrder
from .payment_product.model import PaymentProduct
from .personal_info_view_log.model import PersonalInfoViewLog
from .real_estate.model import RealEstate
+from .reservable_time_span.model import ReservableTimeSpan
from .resource.model import Resource
from .service.model import Service
from .service_sector.model import ServiceSector
@@ -23,6 +25,7 @@
"EmailTemplate",
"GeneralRole",
"Location",
+ "OriginHaukiResource",
"PaymentAccounting",
"PaymentMerchant",
"PaymentOrder",
@@ -30,6 +33,7 @@
"PersonalInfoViewLog",
"ProfileUser",
"RealEstate",
+ "ReservableTimeSpan",
"Resource",
"Service",
"ServiceSector",
diff --git a/tilavarauspalvelu/models/hauki_resource/__init__.py b/tilavarauspalvelu/models/hauki_resource/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tilavarauspalvelu/models/hauki_resource/actions.py b/tilavarauspalvelu/models/hauki_resource/actions.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tilavarauspalvelu/models/hauki_resource/model.py b/tilavarauspalvelu/models/hauki_resource/model.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tilavarauspalvelu/models/hauki_resource/queryset.py b/tilavarauspalvelu/models/hauki_resource/queryset.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/opening_hours/utils/__init__.py b/tilavarauspalvelu/models/origin_hauki_resource/__init__.py
similarity index 100%
rename from opening_hours/utils/__init__.py
rename to tilavarauspalvelu/models/origin_hauki_resource/__init__.py
diff --git a/tilavarauspalvelu/models/origin_hauki_resource/actions.py b/tilavarauspalvelu/models/origin_hauki_resource/actions.py
new file mode 100644
index 000000000..cbe923d1c
--- /dev/null
+++ b/tilavarauspalvelu/models/origin_hauki_resource/actions.py
@@ -0,0 +1,9 @@
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from .model import OriginHaukiResource
+
+
+class OriginHaukiResourceActions:
+ def __init__(self, origin_hauki_resource: "OriginHaukiResource") -> None:
+ self.origin_hauki_resource = origin_hauki_resource
diff --git a/tilavarauspalvelu/models/origin_hauki_resource/model.py b/tilavarauspalvelu/models/origin_hauki_resource/model.py
new file mode 100644
index 000000000..709fc959f
--- /dev/null
+++ b/tilavarauspalvelu/models/origin_hauki_resource/model.py
@@ -0,0 +1,48 @@
+from __future__ import annotations
+
+from functools import cached_property
+from typing import TYPE_CHECKING
+
+from django.db import models
+
+from .queryset import OriginHaukiResourceManager
+
+if TYPE_CHECKING:
+ from datetime import datetime
+
+ from .actions import OriginHaukiResourceActions
+
+
+__all__ = [
+ "OriginHaukiResource",
+]
+
+
+class OriginHaukiResource(models.Model):
+ # Resource id in Hauki API
+ id = models.IntegerField(unique=True, primary_key=True)
+ # Hauki API hash for opening hours, which is used to determine if the opening hours have changed
+ opening_hours_hash = models.CharField(max_length=64, blank=True)
+ # Latest date fetched from Hauki opening hours API
+ latest_fetched_date = models.DateField(blank=True, null=True)
+
+ objects = OriginHaukiResourceManager()
+
+ class Meta:
+ db_table = "origin_hauki_resource"
+ base_manager_name = "objects"
+ ordering = ["pk"]
+
+ def __str__(self) -> str:
+ return str(self.id)
+
+ @cached_property
+ def actions(self) -> OriginHaukiResourceActions:
+ # Import actions inline to defer loading them.
+ # This allows us to avoid circular imports.
+ from .actions import OriginHaukiResourceActions
+
+ return OriginHaukiResourceActions(self)
+
+ def is_reservable(self, start_datetime: datetime, end_datetime: datetime) -> bool:
+ return self.reservable_time_spans.fully_fill_period(start=start_datetime, end=end_datetime).exists()
diff --git a/tilavarauspalvelu/models/origin_hauki_resource/queryset.py b/tilavarauspalvelu/models/origin_hauki_resource/queryset.py
new file mode 100644
index 000000000..2e324d49f
--- /dev/null
+++ b/tilavarauspalvelu/models/origin_hauki_resource/queryset.py
@@ -0,0 +1,14 @@
+from __future__ import annotations
+
+from django.db import models
+
+__all__ = [
+ "OriginHaukiResourceManager",
+ "OriginHaukiResourceQuerySet",
+]
+
+
+class OriginHaukiResourceQuerySet(models.QuerySet): ...
+
+
+class OriginHaukiResourceManager(models.Manager.from_queryset(OriginHaukiResourceQuerySet)): ...
diff --git a/tilavarauspalvelu/admin/hauki_resource/__init__.py b/tilavarauspalvelu/models/reservable_time_span/__init__.py
similarity index 100%
rename from tilavarauspalvelu/admin/hauki_resource/__init__.py
rename to tilavarauspalvelu/models/reservable_time_span/__init__.py
diff --git a/tilavarauspalvelu/models/reservable_time_span/actions.py b/tilavarauspalvelu/models/reservable_time_span/actions.py
new file mode 100644
index 000000000..9f21a7d54
--- /dev/null
+++ b/tilavarauspalvelu/models/reservable_time_span/actions.py
@@ -0,0 +1,9 @@
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from .model import ReservableTimeSpan
+
+
+class ReservableTimeSpanActions:
+ def __init__(self, reservable_time_span: "ReservableTimeSpan") -> None:
+ self.reservable_time_span = reservable_time_span
diff --git a/opening_hours/models.py b/tilavarauspalvelu/models/reservable_time_span/model.py
similarity index 60%
rename from opening_hours/models.py
rename to tilavarauspalvelu/models/reservable_time_span/model.py
index 7331cf71a..565e2ab07 100644
--- a/opening_hours/models.py
+++ b/tilavarauspalvelu/models/reservable_time_span/model.py
@@ -1,56 +1,38 @@
-from datetime import datetime
+from __future__ import annotations
+
+from functools import cached_property
+from typing import TYPE_CHECKING
from django.db import models
from django.db.models import F, Q
-from django.utils.timezone import get_default_timezone
from django.utils.translation import gettext_lazy as _
-from opening_hours.querysets import ReservableTimeSpanQuerySet
-from opening_hours.utils.time_span_element import TimeSpanElement
+from common.date_utils import DEFAULT_TIMEZONE
+from tilavarauspalvelu.utils.opening_hours.time_span_element import TimeSpanElement
+
+from .queryset import ReservableTimeSpanManager
-DEFAULT_TIMEZONE = get_default_timezone()
+if TYPE_CHECKING:
+ from .actions import ReservableTimeSpanActions
__all__ = [
- "OriginHaukiResource",
"ReservableTimeSpan",
]
-class OriginHaukiResource(models.Model):
- # Resource id in Hauki API
- id = models.IntegerField(unique=True, primary_key=True)
- # Hauki API hash for opening hours, which is used to determine if the opening hours have changed
- opening_hours_hash = models.CharField(max_length=64, blank=True)
- # Latest date fetched from Hauki opening hours API
- latest_fetched_date = models.DateField(blank=True, null=True)
-
- class Meta:
- db_table = "origin_hauki_resource"
- base_manager_name = "objects"
- ordering = [
- "pk",
- ]
-
- def __str__(self) -> str:
- return str(self.id)
-
- def is_reservable(self, start_datetime: datetime, end_datetime: datetime) -> bool:
- return self.reservable_time_spans.fully_fill_period(start=start_datetime, end=end_datetime).exists()
-
-
class ReservableTimeSpan(models.Model):
"""A time period on which a ReservationUnit is reservable."""
resource = models.ForeignKey(
- OriginHaukiResource,
+ "tilavarauspalvelu.OriginHaukiResource",
related_name="reservable_time_spans",
on_delete=models.CASCADE,
)
start_datetime = models.DateTimeField(null=False, blank=False)
end_datetime = models.DateTimeField(null=False, blank=False)
- objects = ReservableTimeSpanQuerySet.as_manager()
+ objects = ReservableTimeSpanManager()
class Meta:
db_table = "reservable_time_span"
@@ -71,6 +53,14 @@ class Meta:
def __str__(self) -> str:
return f"{self.resource} {self.get_datetime_str()}"
+ @cached_property
+ def actions(self) -> ReservableTimeSpanActions:
+ # Import actions inline to defer loading them.
+ # This allows us to avoid circular imports.
+ from .actions import ReservableTimeSpanActions
+
+ return ReservableTimeSpanActions(self)
+
def get_datetime_str(self) -> str:
strformat = "%Y-%m-%d %H:%M"
diff --git a/opening_hours/querysets.py b/tilavarauspalvelu/models/reservable_time_span/queryset.py
similarity index 56%
rename from opening_hours/querysets.py
rename to tilavarauspalvelu/models/reservable_time_span/queryset.py
index 33ec93443..f3b81e3fa 100644
--- a/opening_hours/querysets.py
+++ b/tilavarauspalvelu/models/reservable_time_span/queryset.py
@@ -1,20 +1,22 @@
-from datetime import date, datetime, time, timedelta
+import datetime
+from typing import Self
-from django.db.models import Case, Q, QuerySet, Value, When
-from django.utils.timezone import get_default_timezone
+from django.db import models
-DEFAULT_TIMEZONE = get_default_timezone()
+from common.date_utils import normalize_as_datetime
+__all__ = [
+ "ReservableTimeSpanManager",
+ "ReservableTimeSpanQuerySet",
+]
-def _normalize_datetime(value: date | datetime, timedelta_days: int = 0) -> datetime:
- if isinstance(value, datetime):
- return value
- # Convert dates to datetimes to include timezone information
- return datetime.combine(value, time.min, tzinfo=DEFAULT_TIMEZONE) + timedelta(days=timedelta_days)
-
-class ReservableTimeSpanQuerySet(QuerySet):
- def overlapping_with_period(self, start: datetime | date, end: datetime | date):
+class ReservableTimeSpanQuerySet(models.QuerySet):
+ def overlapping_with_period(
+ self,
+ start: datetime.datetime | datetime.date,
+ end: datetime.datetime | datetime.date,
+ ) -> Self:
"""
Filter to reservable time spans that overlap with the given period.
@@ -33,11 +35,15 @@ def overlapping_with_period(self, start: datetime | date, end: datetime | date):
│ │----- # No
│ │ --- # No
"""
- start: datetime = _normalize_datetime(start)
- end: datetime = _normalize_datetime(end, timedelta_days=1)
+ start: datetime = normalize_as_datetime(start)
+ end: datetime = normalize_as_datetime(end, timedelta_days=1)
return self.filter(start_datetime__lt=end, end_datetime__gt=start)
- def fully_fill_period(self, start: datetime | date, end: datetime | date):
+ def fully_fill_period(
+ self,
+ start: datetime.datetime | datetime.date,
+ end: datetime.datetime | datetime.date,
+ ) -> Self:
"""
Filter to reservable time spans that can fully fill in the given period.
@@ -56,35 +62,42 @@ def fully_fill_period(self, start: datetime | date, end: datetime | date):
│ │----- # No
│ │ --- # No
"""
- start: datetime = _normalize_datetime(start)
- end: datetime = _normalize_datetime(end, timedelta_days=1)
+ start: datetime = normalize_as_datetime(start)
+ end: datetime = normalize_as_datetime(end, timedelta_days=1)
return self.filter(start_datetime__lte=start, end_datetime__gte=end)
- def truncated_start_and_end_datetimes_for_period(self, start: datetime | date, end: datetime | date):
+ def truncated_start_and_end_datetimes_for_period(
+ self,
+ start: datetime.datetime | datetime.date,
+ end: datetime.datetime | datetime.date,
+ ) -> Self:
"""
Annotate truncated start and end datetimes for reservable time spans that overlap with the given period.
If the time span starts before the period, the start time is set to the period start.
If the time span ends after the period, the end time is set to the period end (start of next day).
"""
- start = _normalize_datetime(start)
- end = _normalize_datetime(end, timedelta_days=1)
+ start = normalize_as_datetime(start)
+ end = normalize_as_datetime(end, timedelta_days=1)
return self.overlapping_with_period(
start=start,
end=end,
).annotate(
- truncated_start_datetime=Case(
- When(
- condition=Q(start_datetime__lt=start),
- then=Value(start),
+ truncated_start_datetime=models.Case(
+ models.When(
+ condition=models.Q(start_datetime__lt=start),
+ then=models.Value(start),
),
default="start_datetime",
),
- truncated_end_datetime=Case(
- When(
- condition=Q(end_datetime__gt=end),
- then=Value(end),
+ truncated_end_datetime=models.Case(
+ models.When(
+ condition=models.Q(end_datetime__gt=end),
+ then=models.Value(end),
),
default="end_datetime",
),
)
+
+
+class ReservableTimeSpanManager(models.Manager.from_queryset(ReservableTimeSpanQuerySet)): ...
diff --git a/tilavarauspalvelu/models/reservable_timespan/__init__.py b/tilavarauspalvelu/models/reservable_timespan/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tilavarauspalvelu/models/reservable_timespan/actions.py b/tilavarauspalvelu/models/reservable_timespan/actions.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tilavarauspalvelu/models/reservable_timespan/model.py b/tilavarauspalvelu/models/reservable_timespan/model.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tilavarauspalvelu/models/reservable_timespan/queryset.py b/tilavarauspalvelu/models/reservable_timespan/queryset.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tilavarauspalvelu/models/unit/model.py b/tilavarauspalvelu/models/unit/model.py
index fbd2a7333..b33d71d58 100644
--- a/tilavarauspalvelu/models/unit/model.py
+++ b/tilavarauspalvelu/models/unit/model.py
@@ -39,7 +39,7 @@ class Unit(models.Model):
rank: int | None = models.PositiveIntegerField(blank=True, null=True) # Used for ordering
origin_hauki_resource = models.ForeignKey(
- "opening_hours.OriginHaukiResource",
+ "tilavarauspalvelu.OriginHaukiResource",
related_name="units",
on_delete=models.SET_NULL,
blank=True,
diff --git a/tilavarauspalvelu/tasks.py b/tilavarauspalvelu/tasks.py
index e173c7e12..769108d7c 100644
--- a/tilavarauspalvelu/tasks.py
+++ b/tilavarauspalvelu/tasks.py
@@ -1,3 +1,5 @@
+import logging
+
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db.transaction import atomic
@@ -14,6 +16,8 @@
from tilavarauspalvelu.utils.email.email_sender import EmailNotificationSender
from utils.sentry import SentryLogger
+logger = logging.getLogger(__name__)
+
@app.task(name="rebuild_space_tree_hierarchy")
def rebuild_space_tree_hierarchy() -> None:
@@ -203,3 +207,11 @@ def send_application_handled_email_task() -> None:
email_sender.send_batch_application_emails(applications=applications)
applications.update(results_ready_notification_sent_date=local_datetime())
+
+
+@app.task(name="update_origin_hauki_resource_reservable_time_spans")
+def update_origin_hauki_resource_reservable_time_spans() -> None:
+ from tilavarauspalvelu.utils.opening_hours.hauki_resource_hash_updater import HaukiResourceHashUpdater
+
+ logger.info("Updating OriginHaukiResource reservable time spans...")
+ HaukiResourceHashUpdater().run()
diff --git a/opening_hours/decorators.py b/tilavarauspalvelu/utils/decorators.py
similarity index 100%
rename from opening_hours/decorators.py
rename to tilavarauspalvelu/utils/decorators.py
diff --git a/opening_hours/hours.py b/tilavarauspalvelu/utils/hours.py
similarity index 94%
rename from opening_hours/hours.py
rename to tilavarauspalvelu/utils/hours.py
index c76bdddec..6ac754655 100644
--- a/opening_hours/hours.py
+++ b/tilavarauspalvelu/utils/hours.py
@@ -1,6 +1,6 @@
import datetime
-from opening_hours.models import ReservableTimeSpan
+from tilavarauspalvelu.models import ReservableTimeSpan
def can_reserve_based_on_opening_hours(
diff --git a/tilavarauspalvelu/utils/importers/tprek_unit_importer.py b/tilavarauspalvelu/utils/importers/tprek_unit_importer.py
index 0fbfd96b5..49f5b3197 100644
--- a/tilavarauspalvelu/utils/importers/tprek_unit_importer.py
+++ b/tilavarauspalvelu/utils/importers/tprek_unit_importer.py
@@ -5,14 +5,13 @@
from django.db.models import QuerySet
from django.db.transaction import atomic
-from opening_hours.models import OriginHaukiResource
-from opening_hours.utils.hauki_api_client import HaukiAPIClient
-from tilavarauspalvelu.models import Location, Unit
+from tilavarauspalvelu.models import Location, OriginHaukiResource, Unit
from tilavarauspalvelu.utils.importers.tprek_api_client import TprekAPIClient, TprekLocationData, TprekUnitData
+from tilavarauspalvelu.utils.opening_hours.hauki_api_client import HaukiAPIClient
from utils.sentry import SentryLogger
if TYPE_CHECKING:
- from opening_hours.utils.hauki_api_types import HaukiAPIResource
+ from tilavarauspalvelu.utils.opening_hours import HaukiAPIResource
logger = logging.getLogger(__name__)
diff --git a/tilavarauspalvelu/admin/reservable_timespan/__init__.py b/tilavarauspalvelu/utils/opening_hours/__init__.py
similarity index 100%
rename from tilavarauspalvelu/admin/reservable_timespan/__init__.py
rename to tilavarauspalvelu/utils/opening_hours/__init__.py
diff --git a/opening_hours/utils/hauki_api_client.py b/tilavarauspalvelu/utils/opening_hours/hauki_api_client.py
similarity index 97%
rename from opening_hours/utils/hauki_api_client.py
rename to tilavarauspalvelu/utils/opening_hours/hauki_api_client.py
index dba9903fe..12aa8930d 100644
--- a/opening_hours/utils/hauki_api_client.py
+++ b/tilavarauspalvelu/utils/opening_hours/hauki_api_client.py
@@ -3,8 +3,8 @@
from django.conf import settings
-from opening_hours.errors import HaukiAPIError, HaukiConfigurationError
-from opening_hours.utils.hauki_api_types import (
+from tilavarauspalvelu.exceptions import HaukiAPIError, HaukiConfigurationError
+from tilavarauspalvelu.utils.opening_hours.hauki_api_types import (
HaukiAPIDatePeriod,
HaukiAPIOpeningHoursResponse,
HaukiAPIOpeningHoursResponseItem,
diff --git a/opening_hours/utils/hauki_api_types.py b/tilavarauspalvelu/utils/opening_hours/hauki_api_types.py
similarity index 98%
rename from opening_hours/utils/hauki_api_types.py
rename to tilavarauspalvelu/utils/opening_hours/hauki_api_types.py
index c8f18892c..3665ab5c5 100644
--- a/opening_hours/utils/hauki_api_types.py
+++ b/tilavarauspalvelu/utils/opening_hours/hauki_api_types.py
@@ -1,6 +1,6 @@
from typing import Any, Literal, TypedDict
-from opening_hours.enums import HaukiResourceState
+from tilavarauspalvelu.enums import HaukiResourceState
##########
# Common #
diff --git a/opening_hours/utils/hauki_link_generator.py b/tilavarauspalvelu/utils/opening_hours/hauki_link_generator.py
similarity index 100%
rename from opening_hours/utils/hauki_link_generator.py
rename to tilavarauspalvelu/utils/opening_hours/hauki_link_generator.py
diff --git a/opening_hours/utils/hauki_resource_hash_updater.py b/tilavarauspalvelu/utils/opening_hours/hauki_resource_hash_updater.py
similarity index 90%
rename from opening_hours/utils/hauki_resource_hash_updater.py
rename to tilavarauspalvelu/utils/opening_hours/hauki_resource_hash_updater.py
index cb5a58027..b4fdbc7da 100644
--- a/opening_hours/utils/hauki_resource_hash_updater.py
+++ b/tilavarauspalvelu/utils/opening_hours/hauki_resource_hash_updater.py
@@ -2,18 +2,16 @@
from datetime import datetime, time
from django.utils import timezone
-from django.utils.timezone import get_default_timezone
-from opening_hours.errors import ReservableTimeSpanClientNothingToDoError, ReservableTimeSpanClientValueError
-from opening_hours.models import OriginHaukiResource
-from opening_hours.utils.hauki_api_client import HaukiAPIClient
-from opening_hours.utils.hauki_api_types import HaukiAPIResource
-from opening_hours.utils.reservable_time_span_client import ReservableTimeSpanClient
+from common.date_utils import DEFAULT_TIMEZONE
+from tilavarauspalvelu.exceptions import ReservableTimeSpanClientNothingToDoError, ReservableTimeSpanClientValueError
+from tilavarauspalvelu.models import OriginHaukiResource
+from tilavarauspalvelu.utils.opening_hours.hauki_api_client import HaukiAPIClient
+from tilavarauspalvelu.utils.opening_hours.hauki_api_types import HaukiAPIResource
+from tilavarauspalvelu.utils.opening_hours.reservable_time_span_client import ReservableTimeSpanClient
logger = logging.getLogger(__name__)
-DEFAULT_TIMEZONE = get_default_timezone()
-
class HaukiResourceHashUpdater:
# List of resource ids that should be updated
diff --git a/opening_hours/utils/reservable_time_span_client.py b/tilavarauspalvelu/utils/opening_hours/reservable_time_span_client.py
similarity index 91%
rename from opening_hours/utils/reservable_time_span_client.py
rename to tilavarauspalvelu/utils/opening_hours/reservable_time_span_client.py
index fd8d696f0..0b52c8c94 100644
--- a/opening_hours/utils/reservable_time_span_client.py
+++ b/tilavarauspalvelu/utils/opening_hours/reservable_time_span_client.py
@@ -5,20 +5,17 @@
from django.conf import settings
from common.date_utils import local_date
-from opening_hours.errors import ReservableTimeSpanClientNothingToDoError, ReservableTimeSpanClientValueError
-from opening_hours.models import OriginHaukiResource, ReservableTimeSpan
-from opening_hours.utils.hauki_api_client import HaukiAPIClient
-from opening_hours.utils.hauki_api_types import HaukiAPIOpeningHoursResponseItem
-from opening_hours.utils.time_span_element import TimeSpanElement
-from opening_hours.utils.time_span_element_utils import (
+from tilavarauspalvelu.constants import NEVER_ANY_OPENING_HOURS_HASH
+from tilavarauspalvelu.exceptions import ReservableTimeSpanClientNothingToDoError, ReservableTimeSpanClientValueError
+from tilavarauspalvelu.models import OriginHaukiResource, ReservableTimeSpan
+from tilavarauspalvelu.utils.opening_hours.hauki_api_client import HaukiAPIClient
+from tilavarauspalvelu.utils.opening_hours.hauki_api_types import HaukiAPIOpeningHoursResponseItem
+from tilavarauspalvelu.utils.opening_hours.time_span_element import TimeSpanElement
+from tilavarauspalvelu.utils.opening_hours.time_span_element_utils import (
merge_overlapping_time_span_elements,
override_reservable_with_closed_time_spans,
)
-# Hash value for when there are never any opening hours
-# See https://github.com/City-of-Helsinki/hauki `hours.models.Resource._get_date_periods_as_hash`
-NEVER_ANY_OPENING_HOURS_HASH = "d41d8cd98f00b204e9800998ecf8427e" # md5(b"").hexdigest()
-
class ReservableTimeSpanClient:
DAYS_TO_FETCH = 730 # 2 years
diff --git a/opening_hours/utils/summaries.py b/tilavarauspalvelu/utils/opening_hours/summaries.py
similarity index 93%
rename from opening_hours/utils/summaries.py
rename to tilavarauspalvelu/utils/opening_hours/summaries.py
index 729d81e14..c8f373b1a 100644
--- a/opening_hours/utils/summaries.py
+++ b/tilavarauspalvelu/utils/opening_hours/summaries.py
@@ -5,7 +5,7 @@
from django.db.models.functions import Coalesce
from common.db import SubquerySum
-from opening_hours.models import OriginHaukiResource, ReservableTimeSpan
+from tilavarauspalvelu.models import OriginHaukiResource, ReservableTimeSpan
def get_resources_total_hours_per_resource(
diff --git a/opening_hours/utils/time_span_element.py b/tilavarauspalvelu/utils/opening_hours/time_span_element.py
similarity index 98%
rename from opening_hours/utils/time_span_element.py
rename to tilavarauspalvelu/utils/opening_hours/time_span_element.py
index a403153b7..ef8a0ce11 100644
--- a/opening_hours/utils/time_span_element.py
+++ b/tilavarauspalvelu/utils/opening_hours/time_span_element.py
@@ -4,8 +4,8 @@
from typing import Optional
from common.date_utils import DEFAULT_TIMEZONE, combine, local_start_of_day
-from opening_hours.enums import HaukiResourceState
-from opening_hours.utils.hauki_api_types import HaukiAPIOpeningHoursResponseTime
+from tilavarauspalvelu.enums import HaukiResourceState
+from tilavarauspalvelu.utils.opening_hours.hauki_api_types import HaukiAPIOpeningHoursResponseTime
@dataclass(order=True, frozen=False)
diff --git a/opening_hours/utils/time_span_element_utils.py b/tilavarauspalvelu/utils/opening_hours/time_span_element_utils.py
similarity index 99%
rename from opening_hours/utils/time_span_element_utils.py
rename to tilavarauspalvelu/utils/opening_hours/time_span_element_utils.py
index e20245603..d8c8eca46 100644
--- a/opening_hours/utils/time_span_element_utils.py
+++ b/tilavarauspalvelu/utils/opening_hours/time_span_element_utils.py
@@ -3,7 +3,7 @@
from itertools import chain
from common.utils import with_indices
-from opening_hours.utils.time_span_element import TimeSpanElement
+from tilavarauspalvelu.utils.opening_hours.time_span_element import TimeSpanElement
def merge_overlapping_time_span_elements(*time_span_lists: Iterable[TimeSpanElement]) -> list[TimeSpanElement]: