diff --git a/.tbls.yml b/.tbls.yml
index a3a220b..9f218e2 100644
--- a/.tbls.yml
+++ b/.tbls.yml
@@ -19,7 +19,6 @@ viewpoints:
- hame.document
- hame.source_data
- hame.organisation
- - hame.lifecycle_date
- codes.administrative_region
- codes.lifecycle_status
- codes.plan_theme
@@ -56,7 +55,6 @@ viewpoints:
- hame.document
- hame.source_data
- hame.organisation
- - hame.lifecycle_date
-
name: Code tables
desc: These tables contain all national (koodistot.suomi.fi) and local codes used for classifying plan data.
diff --git a/database/codes.py b/database/codes.py
index c30c6a5..39fd89c 100644
--- a/database/codes.py
+++ b/database/codes.py
@@ -13,9 +13,7 @@
if TYPE_CHECKING:
from database.models import (
Document,
- EventDate,
LandUseArea,
- LifeCycleDate,
Line,
Organisation,
OtherArea,
@@ -124,9 +122,6 @@ class LifeCycleStatus(CodeBase):
__tablename__ = "lifecycle_status"
code_list_uri = "http://uri.suomi.fi/codelist/rytj/kaavaelinkaari"
- lifecycle_dates: Mapped[list[LifeCycleDate]] = relationship(
- back_populates="lifecycle_status"
- )
allowed_interaction_events: Mapped[list[TypeOfInteractionEvent]] = relationship(
secondary="codes.allowed_events", back_populates="allowed_statuses"
)
@@ -446,10 +441,6 @@ class TypeOfInteractionEvent(CodeBase):
overlaps="allowed_decisions,allowed_processing_events",
)
- event_dates: Mapped[list[EventDate]] = relationship(
- back_populates="interaction_event"
- )
-
class NameOfPlanCaseDecision(CodeBase):
"""Kaava-asian päätöksen nimi"""
@@ -463,8 +454,6 @@ class NameOfPlanCaseDecision(CodeBase):
overlaps="allowed_interaction_events,allowed_processing_events,allowed_statuses",
)
- event_dates: Mapped[list[EventDate]] = relationship(back_populates="decision")
-
class TypeOfProcessingEvent(CodeBase):
"""Käsittelytapahtuman laji"""
@@ -478,10 +467,6 @@ class TypeOfProcessingEvent(CodeBase):
overlaps="allowed_interaction_events,allowed_decisions,allowed_statuses",
)
- event_dates: Mapped[list[EventDate]] = relationship(
- back_populates="processing_event"
- )
-
class TypeOfDecisionMaker(CodeBase):
"""Päätöksentekijän laji"""
diff --git a/database/models.py b/database/models.py
index 3b864dd..26409e4 100644
--- a/database/models.py
+++ b/database/models.py
@@ -25,17 +25,14 @@
LegalEffectsOfMasterPlan,
LifeCycleStatus,
Municipality,
- NameOfPlanCaseDecision,
PersonalDataContent,
PlanTheme,
PlanType,
RetentionTime,
TypeOfAdditionalInformation,
TypeOfDocument,
- TypeOfInteractionEvent,
TypeOfPlanRegulation,
TypeOfPlanRegulationGroup,
- TypeOfProcessingEvent,
TypeOfSourceData,
TypeOfUnderground,
TypeOfVerbalPlanRegulation,
@@ -44,8 +41,8 @@
regulation_group_association = Table(
"regulation_group_association",
Base.metadata,
- Column("id", Uuid, primary_key=True, server_default=func.gen_random_uuid()),
- Column(
+ Column[UUID]("id", Uuid, primary_key=True, server_default=func.gen_random_uuid()),
+ Column[UUID](
"plan_regulation_group_id",
ForeignKey(
"hame.plan_regulation_group.id",
@@ -56,30 +53,30 @@
),
# General groups cannot actually be n2n but use this approach anyway
# to make the approach uniform with the plan objects
- Column(
+ Column[UUID](
"plan_id",
ForeignKey("hame.plan.id", name="plan_id_fkey", ondelete="CASCADE"),
index=True,
comment="A plan in which the regulation group is a general regulation group",
),
- Column(
+ Column[UUID](
"land_use_area_id",
ForeignKey(
"hame.land_use_area.id", name="land_use_area_id_fkey", ondelete="CASCADE"
),
index=True,
),
- Column(
+ Column[UUID](
"other_area_id",
ForeignKey("hame.other_area.id", name="other_area_id_fkey", ondelete="CASCADE"),
index=True,
),
- Column(
+ Column[UUID](
"line_id",
ForeignKey("hame.line.id", name="line_id_fkey", ondelete="CASCADE"),
index=True,
),
- Column(
+ Column[UUID](
"point_id",
ForeignKey("hame.point.id", name="point_id_fkey", ondelete="CASCADE"),
index=True,
@@ -91,13 +88,13 @@
legal_effects_association = Table(
"legal_effects_association",
Base.metadata,
- Column(
+ Column[UUID](
"plan_id",
ForeignKey("hame.plan.id", name="plan_id_fkey", ondelete="CASCADE"),
primary_key=True,
# indexed by the primary key
),
- Column(
+ Column[UUID](
"legal_effects_of_master_plan_id",
ForeignKey(
"codes.legal_effects_of_master_plan.id",
@@ -115,8 +112,8 @@
plan_theme_association = Table(
"plan_theme_association",
Base.metadata,
- Column("id", Uuid, primary_key=True, server_default=func.gen_random_uuid()),
- Column(
+ Column[UUID]("id", Uuid, primary_key=True, server_default=func.gen_random_uuid()),
+ Column[UUID](
"plan_regulation_id",
ForeignKey(
"hame.plan_regulation.id",
@@ -125,7 +122,7 @@
),
index=True,
),
- Column(
+ Column[UUID](
"plan_proposition_id",
ForeignKey(
"hame.plan_proposition.id",
@@ -134,7 +131,7 @@
),
index=True,
),
- Column(
+ Column[UUID](
"plan_theme_id",
ForeignKey(
"codes.plan_theme.id", name="plan_theme_id_fkey", ondelete="CASCADE"
@@ -209,19 +206,6 @@ def lifecycle_status(cls) -> Mapped[LifeCycleStatus]:
"LifeCycleStatus", back_populates=f"{cls.__tablename__}s", lazy="joined"
)
- # Let's add backreference to allow lazy loading from this side.
- @declared_attr
- @classmethod
- def lifecycle_dates(cls) -> Mapped[list[LifeCycleDate]]:
- return relationship(
- "LifeCycleDate",
- back_populates=f"{cls.__tablename__}",
- lazy="joined",
- cascade="all, delete-orphan",
- passive_deletes=True,
- order_by="LifeCycleDate.starting_at",
- )
-
class PeriodOfValidityMixin:
"""Mixin for period of validity fields."""
@@ -536,7 +520,7 @@ class AdditionalInformation(VersionedBase, AttributeValueMixin):
type_of_verbal_regulation_association = Table(
"type_of_verbal_regulation_association",
Base.metadata,
- Column(
+ Column[UUID](
"plan_regulation_id",
ForeignKey(
"hame.plan_regulation.id",
@@ -545,7 +529,7 @@ class AdditionalInformation(VersionedBase, AttributeValueMixin):
),
primary_key=True,
),
- Column(
+ Column[UUID](
"type_of_verbal_plan_regulation_id",
ForeignKey(
"codes.type_of_verbal_plan_regulation.id",
@@ -767,131 +751,3 @@ class Document(VersionedBase):
decision_date: Mapped[datetime | None]
document_date: Mapped[datetime]
url: Mapped[str | None]
-
-
-class LifeCycleDate(VersionedBase):
- """Elinkaaritilan päivämäärät"""
-
- __tablename__ = "lifecycle_date"
-
- lifecycle_status_id: Mapped[UUID] = mapped_column(
- ForeignKey("codes.lifecycle_status.id", name="plan_lifecycle_status_id_fkey"),
- index=True,
- )
- plan_id: Mapped[UUID | None] = mapped_column(
- ForeignKey("hame.plan.id", name="plan_id_fkey", ondelete="CASCADE"), index=True
- )
- land_use_area_id: Mapped[UUID | None] = mapped_column(
- ForeignKey(
- "hame.land_use_area.id", name="land_use_area_id_fkey", ondelete="CASCADE"
- ),
- index=True,
- )
- other_area_id: Mapped[UUID | None] = mapped_column(
- ForeignKey("hame.other_area.id", name="other_area_id_fkey", ondelete="CASCADE"),
- index=True,
- )
- line_id: Mapped[UUID | None] = mapped_column(
- ForeignKey("hame.line.id", name="line_id_fkey", ondelete="CASCADE"), index=True
- )
- point_id: Mapped[UUID | None] = mapped_column(
- ForeignKey("hame.point.id", name="point_id_fkey", ondelete="CASCADE"),
- index=True,
- )
- plan_regulation_id: Mapped[UUID | None] = mapped_column(
- ForeignKey(
- "hame.plan_regulation.id",
- name="plan_regulation_id_fkey",
- ondelete="CASCADE",
- ),
- index=True,
- )
- plan_proposition_id: Mapped[UUID | None] = mapped_column(
- ForeignKey(
- "hame.plan_proposition.id",
- name="plan_proposition_id_fkey",
- ondelete="CASCADE",
- ),
- index=True,
- )
-
- plan: Mapped[Plan | None] = relationship(back_populates="lifecycle_dates")
- land_use_area: Mapped[LandUseArea | None] = relationship(
- back_populates="lifecycle_dates"
- )
- other_area: Mapped[OtherArea | None] = relationship(
- back_populates="lifecycle_dates"
- )
- line: Mapped[Line | None] = relationship(back_populates="lifecycle_dates")
- point: Mapped[Point | None] = relationship(back_populates="lifecycle_dates")
- plan_regulation: Mapped[PlanRegulation | None] = relationship(
- back_populates="lifecycle_dates"
- )
- plan_proposition: Mapped[PlanProposition | None] = relationship(
- back_populates="lifecycle_dates"
- )
- # Let's load all the codes for objects joined.
- lifecycle_status: Mapped[LifeCycleStatus] = relationship(
- back_populates="lifecycle_dates", lazy="joined"
- )
- # Let's add backreference to allow lazy loading from this side.
- event_dates: Mapped[list[EventDate]] = relationship(
- back_populates="lifecycle_date",
- lazy="joined",
- cascade="all, delete-orphan",
- passive_deletes=True,
- )
-
- starting_at: Mapped[datetime]
- ending_at: Mapped[datetime | None]
-
-
-class EventDate(VersionedBase):
- """Tapahtuman päivämäärät
-
- Jokaisessa elinkaaritilassa voi olla tiettyjä tapahtumia. Liitetään tapahtuma
- sille sallittuun elinkaaritilaan. Tapahtuman päivämäärien tulee olla aina
- elinkaaritilan päivämäärien välissä.
- """
-
- __tablename__ = "event_date"
-
- lifecycle_date_id: Mapped[UUID] = mapped_column(
- ForeignKey(
- "hame.lifecycle_date.id", name="lifecycle_date_id_fkey", ondelete="CASCADE"
- ),
- index=True,
- )
- decision_id: Mapped[UUID | None] = mapped_column(
- ForeignKey(
- "codes.name_of_plan_case_decision.id",
- name="name_of_plan_case_decision_id_fkey",
- )
- )
- processing_event_id: Mapped[UUID | None] = mapped_column(
- ForeignKey(
- "codes.type_of_processing_event.id", name="type_of_processing_event_fkey"
- )
- )
- interaction_event_id: Mapped[UUID | None] = mapped_column(
- ForeignKey(
- "codes.type_of_interaction_event.id", name="type_of_interaction_event_fkey"
- )
- )
-
- # Let's load all the codes for objects joined.
- lifecycle_date: Mapped[LifeCycleDate] = relationship(
- back_populates="event_dates", lazy="joined"
- )
- decision: Mapped[NameOfPlanCaseDecision] = relationship(
- back_populates="event_dates", lazy="joined"
- )
- processing_event: Mapped[TypeOfProcessingEvent] = relationship(
- back_populates="event_dates", lazy="joined"
- )
- interaction_event: Mapped[TypeOfInteractionEvent] = relationship(
- back_populates="event_dates", lazy="joined"
- )
-
- starting_at: Mapped[datetime]
- ending_at: Mapped[datetime | None]
diff --git a/database/triggers.py b/database/triggers.py
index 96d0779..76846cc 100644
--- a/database/triggers.py
+++ b/database/triggers.py
@@ -25,14 +25,6 @@ def all_subclasses(cls: type) -> set[type]:
# and cls.__table__.name != "
"
]
-# All tables that inherit PlanBase
-tables_with_lifecycle_date = [
- klass.__tablename__
- for _, klass in inspect.getmembers(models, inspect.isclass)
- if inspect.getmodule(klass) == models
- and issubclass(klass, models.PlanBase)
- and hasattr(klass, "__tablename__") # Ignore classes without __tablename__
-]
# Regulations and propositions link to plan via plan regulation group
plan_regulation_tables = ["plan_regulation", "plan_proposition"]
@@ -158,103 +150,6 @@ def generate_modified_at_triggers() -> tuple[list[PGTrigger], list[PGFunction]]:
return trgs, [trgfunc]
-def generate_new_object_add_lifecycle_date_triggers() -> tuple[
- list[PGTrigger], list[PGFunction]
-]:
- trgs = []
- trgfunc_signature = "trgfunc_new_object_add_lifecycle_date()"
- trgfunc_definition = """
- RETURNS TRIGGER AS $$
- BEGIN
- EXECUTE format(
- $query$
- INSERT INTO hame.lifecycle_date
- (lifecycle_status_id, %I, starting_at)
- VALUES
- ($1, $2, CURRENT_TIMESTAMP)
- $query$,
- TG_TABLE_NAME || '_id'
- ) USING NEW.lifecycle_status_id, NEW.id;
- RETURN NEW;
- END;
- $$ language 'plpgsql'
- """
- trgfunc = PGFunction(
- schema="hame", signature=trgfunc_signature, definition=trgfunc_definition
- )
-
- for table in tables_with_lifecycle_date:
- trg_signature = f"trg_new_{table}_add_lifecycle_date"
- trg_definition = f"""
- AFTER INSERT ON {table}
- FOR EACH ROW
- EXECUTE FUNCTION hame.{trgfunc_signature}
- """
- trg = PGTrigger(
- schema="hame",
- signature=trg_signature,
- on_entity=f"hame.{table}",
- is_constraint=False,
- definition=trg_definition,
- )
- trgs.append(trg)
-
- return trgs, [trgfunc]
-
-
-def generate_new_lifecycle_date_triggers() -> tuple[list[PGTrigger], list[PGFunction]]:
- trgs = []
- trgfunc_signature = "trgfunc_new_lifecycle_date()"
- trgfunc_definition = """
- RETURNS TRIGGER AS $$
- BEGIN
- EXECUTE format(
- $query$
- INSERT INTO hame.lifecycle_date
- (lifecycle_status_id, %I, starting_at)
- VALUES
- ($1, $2, CURRENT_TIMESTAMP)
- $query$,
- TG_TABLE_NAME || '_id'
- ) USING NEW.lifecycle_status_id, NEW.id;
- EXECUTE format(
- $query$
- UPDATE hame.lifecycle_date
- SET ending_at=CURRENT_TIMESTAMP
- WHERE %I = $1
- AND ending_at IS NULL
- AND lifecycle_status_id = $2
- $query$,
- TG_TABLE_NAME || '_id'
- ) USING NEW.id, OLD.lifecycle_status_id;
- RETURN NEW;
- END;
- $$ language 'plpgsql'
- """
- trgfunc = PGFunction(
- schema="hame", signature=trgfunc_signature, definition=trgfunc_definition
- )
-
- for table in tables_with_lifecycle_date:
- trg_signature = f"trg_{table}_new_lifecycle_date"
- trg_definition = f"""
- BEFORE UPDATE ON {table}
- FOR EACH ROW
- WHEN (NEW.lifecycle_status_id <> OLD.lifecycle_status_id)
- EXECUTE FUNCTION hame.{trgfunc_signature}
- """
- trg = PGTrigger(
- schema="hame",
- signature=trg_signature,
- on_entity=f"hame.{table}",
- is_constraint=False,
- definition=trg_definition,
- )
- trgs.append(trg)
-
- return trgs, [trgfunc]
-
-
def generate_update_lifecycle_status_triggers() -> tuple[
list[PGTrigger], list[PGFunction]
]:
diff --git a/database/validation.py b/database/validation.py
deleted file mode 100644
index 24cbbac..0000000
--- a/database/validation.py
+++ /dev/null
@@ -1,163 +0,0 @@
-from alembic_utils.pg_function import PGFunction
-from alembic_utils.pg_trigger import PGTrigger
-
-trgfunc_validate_lifecycle_date = PGFunction(
- schema="hame",
- signature="trgfunc_lifecycle_date_validate_dates()",
- definition="""
- RETURNS TRIGGER AS $$
- BEGIN
- IF (
- NEW.starting_at IS NOT NULL AND
- NEW.ending_at IS NOT NULL AND
- NEW.starting_at > NEW.ending_at
- ) IS TRUE
- THEN
- RAISE EXCEPTION 'Status starting date % after ending date %',
- NEW.starting_at, NEW.ending_at
- USING HINT = 'Status ending date must be after starting date.';
- END IF;
- RETURN NEW;
- END;
- $$ language 'plpgsql'
- """,
-)
-
-trg_validate_lifecycle_date = PGTrigger(
- schema="hame",
- signature="trg_lifecycle_date_validate_dates",
- on_entity="hame.lifecycle_date",
- definition="""
- BEFORE INSERT OR UPDATE ON lifecycle_date
- FOR EACH ROW
- EXECUTE FUNCTION hame.trgfunc_lifecycle_date_validate_dates()
- """,
-)
-
-trgfunc_validate_event_date = PGFunction(
- schema="hame",
- signature="trgfunc_event_date_validate_dates()",
- definition="""
- RETURNS TRIGGER AS $$
- BEGIN
- IF (
- NEW.starting_at IS NOT NULL AND
- NEW.ending_at IS NOT NULL AND
- NEW.starting_at > NEW.ending_at
- ) IS TRUE
- THEN
- RAISE EXCEPTION 'Event starting date % after ending date %',
- NEW.starting_at, NEW.ending_at
- USING HINT = 'Event ending date must be after starting date.';
- END IF;
- RETURN NEW;
- END;
- $$ language 'plpgsql'
- """,
-)
-
-trg_validate_event_date = PGTrigger(
- schema="hame",
- signature="trg_event_date_validate_dates",
- on_entity="hame.event_date",
- definition="""
- BEFORE INSERT OR UPDATE ON event_date
- FOR EACH ROW
- EXECUTE FUNCTION hame.trgfunc_event_date_validate_dates()
- """,
-)
-
-trgfunc_validate_event_date_inside_status_date = PGFunction(
- schema="hame",
- signature="trgfunc_event_date_validate_inside_status_date()",
- definition="""
- RETURNS TRIGGER AS $$
- DECLARE
- status_starting_at TIMESTAMP WITH TIME ZONE;
- status_ending_at TIMESTAMP WITH TIME ZONE;
- BEGIN
- SELECT starting_at, ending_at INTO status_starting_at, status_ending_at
- FROM hame.lifecycle_date
- WHERE NEW.lifecycle_date_id = hame.lifecycle_date.id;
- IF (
- -- Does the event start before status starts?
- (
- NEW.starting_at < status_starting_at
- ) OR
- -- Missing event ending date means event is instantaneous. Only
- -- events with ending date have a duration. Both must have ending
- -- date specified to check if event ends after status ends.
- (
- NEW.ending_at IS NOT NULL AND status_ending_at IS NOT NULL AND
- NEW.ending_at > status_ending_at
- )
- ) IS TRUE
- THEN
- RAISE EXCEPTION 'Event dates % - % outside status dates % - %',
- NEW.starting_at, NEW.ending_at, status_starting_at, status_ending_at
- USING HINT = 'Event cannot be outside lifecycle status dates.';
- END IF;
- RETURN NEW;
- END;
- $$ language 'plpgsql'
- """,
-)
-
-trg_validate_event_date_inside_status_date = PGTrigger(
- schema="hame",
- signature="trg_event_date_validate_inside_status_date",
- on_entity="hame.event_date",
- definition="""
- BEFORE INSERT OR UPDATE ON event_date
- FOR EACH ROW
- EXECUTE FUNCTION hame.trgfunc_event_date_validate_inside_status_date()
- """,
-)
-
-trgfunc_validate_event_type = PGFunction(
- schema="hame",
- signature="trgfunc_event_date_validate_type()",
- definition="""
- RETURNS TRIGGER AS $$
- DECLARE
- status_id UUID;
- association_id UUID;
- BEGIN
- SELECT lifecycle_status_id INTO status_id
- FROM
- hame.lifecycle_date
- WHERE
- NEW.lifecycle_date_id = lifecycle_date.id;
- SELECT id INTO association_id
- FROM
- codes.allowed_events
- WHERE
- lifecycle_status_id = status_id AND (
- (NEW.decision_id IS NOT NULL AND
- name_of_plan_case_decision_id = NEW.decision_id) OR
- (NEW.processing_event_id IS NOT NULL AND
- type_of_processing_event_id = NEW.processing_event_id) OR
- (NEW.interaction_event_id IS NOT NULL AND
- type_of_interaction_event_id = NEW.interaction_event_id)
- );
- IF association_id IS NULL
- THEN
- RAISE EXCEPTION 'Wrong event type for status'
- USING HINT = 'This event type cannot be added to this lifecycle status.';
- END IF;
- RETURN NEW;
- END;
- $$ language 'plpgsql'
- """,
-)
-
-trg_validate_event_type = PGTrigger(
- schema="hame",
- signature="trg_event_date_validate_type",
- on_entity="hame.event_date",
- definition="""
- BEFORE INSERT OR UPDATE ON event_date
- FOR EACH ROW
- EXECUTE FUNCTION hame.trgfunc_event_date_validate_type()
- """,
-)
diff --git a/lambdas/ryhti_client/lambda_function.py b/lambdas/ryhti_client/lambda_function.py
index 7654981..b47b59c 100644
--- a/lambdas/ryhti_client/lambda_function.py
+++ b/lambdas/ryhti_client/lambda_function.py
@@ -389,15 +389,13 @@ def handler(
elif event_type is Action.GET_PLAN_MATTERS:
# just return the JSON to the user
- LOGGER.info("Formatting plan matter data...")
- plan_matters = database_client.get_plan_matters()
- response_title = "Returning serialized plan matters from database."
+ response_title = "Serializing plan matters not implemented yet."
LOGGER.info(response_title)
lambda_response = Response(
- statusCode=200,
+ statusCode=405,
body=ResponseBody(
title=response_title,
- details=cast("dict", plan_matters),
+ details={"error": "Not implemented yet."},
ryhti_responses={},
),
)
@@ -441,57 +439,26 @@ def handler(
)
elif event_type is Action.VALIDATE_PLAN_MATTERS:
- LOGGER.info("Authenticating to X-road Ryhti API...")
- client.xroad_ryhti_authenticate()
- # Documents are exported separately from plan matter. Also, they need to be
- # present in Ryhti *before* plan matter is validated or created.
- #
- # Therefore, let's export all the documents right away, and update them to
- # the latest version when needed. Otherwise, the plan matter would never be
- # valid. Only upload documents for those plans that have permanent plan
- # identifiers.
- # 1) If changed documents exist, upload documents
- LOGGER.info("Checking and updating plan documents for plans...")
- plan_documents = client.upload_plan_documents()
- LOGGER.info("Marking documents exported...")
- database_client.set_plan_documents(plan_documents)
- # 2) Validate plan matters with identifiers with X-Road API
- LOGGER.info("Validating plan matters for plans...")
- responses = client.validate_plan_matters()
- # 3) Save and return plan matter validation data
- LOGGER.info("Saving plan matter validation data for plans...")
- save_details = database_client.save_plan_matter_validation_responses(
- responses
- )
+ # Plan matter support removed from Arho. See git history for previous
+ # implementation.
lambda_response = Response(
- statusCode=200,
+ statusCode=405,
body=ResponseBody(
- title="Plan matter validations run.",
- details=save_details, # type: ignore[typeddict-item]
- ryhti_responses=responses,
+ title="Plan matter validation not implemented.",
+ details={}, # type: ignore[typeddict-item]
+ ryhti_responses={},
),
)
elif event_type is Action.POST_PLAN_MATTERS:
- LOGGER.info("Authenticating to X-road Ryhti API...")
- client.xroad_ryhti_authenticate()
- # 1) If changed documents exist, upload documents
- LOGGER.info("Checking and updating plan documents for plans...")
- plan_documents = client.upload_plan_documents()
- LOGGER.info("Marking documents exported...")
- database_client.set_plan_documents(plan_documents)
- # 2) Create or update Ryhti plan matters
- LOGGER.info("POSTing plan matters...")
- responses = client.post_plan_matters()
- # 3) Save and return plan matter update responses
- LOGGER.info("Saving plan matter POST data for posted plans...")
- save_details = database_client.save_plan_matter_post_responses(responses)
+ # Plan matter support removed from Arho. See git history for previous
+ # implementation.
lambda_response = Response(
- statusCode=200,
+ statusCode=405,
body=ResponseBody(
- title="Plan matters POSTed.",
- details=save_details, # type: ignore[typeddict-item]
- ryhti_responses=responses,
+ title="Plan matter posting not implemented.",
+ details={}, # type: ignore[typeddict-item]
+ ryhti_responses={},
),
)
elif event_type is Action.COPY_PLAN:
diff --git a/lambdas/ryhti_client/ryhti_client/database_client.py b/lambdas/ryhti_client/ryhti_client/database_client.py
index d5d82ef..5e7537e 100644
--- a/lambdas/ryhti_client/ryhti_client/database_client.py
+++ b/lambdas/ryhti_client/ryhti_client/database_client.py
@@ -13,17 +13,6 @@
from sqlalchemy.orm import sessionmaker
from database import base, codes, models
-from database.codes import (
- NameOfPlanCaseDecision,
- TypeOfDecisionMaker,
- TypeOfInteractionEvent,
- TypeOfProcessingEvent,
- decisionmaker_by_status,
- decisions_by_status,
- get_code_uri,
- interaction_events_by_status,
- processing_events_by_status,
-)
from database.enums import AttributeValueDataType
from ryhti_client.deserializer import (
Deserializer,
@@ -35,12 +24,8 @@
Period,
RyhtiAdditionalInformation,
RyhtiAttributeValue,
- RyhtiHandlingEvent,
- RyhtiInteractionEvent,
RyhtiPlan,
- RyhtiPlanDecision,
RyhtiPlanMatter,
- RyhtiPlanMatterPhase,
)
if TYPE_CHECKING:
@@ -182,97 +167,6 @@ def get_date(self, datetime_value: datetime.datetime) -> str:
"""Returns isoformatted date for the given datetime in local timezone."""
return datetime_value.astimezone(LOCAL_TZ).date().isoformat()
- def get_periods(
- self,
- dates_objects: list[models.LifeCycleDate] | list[models.EventDate],
- datetimes: bool = True,
- ) -> list[Period]:
- """Returns the time periods of given date objects. Optionally, only dates instead
- of datetimes may be returned.
- """
- return [
- {
- "begin": (
- self.get_isoformat_value_with_z(dates_object.starting_at)
- if datetimes
- else self.get_date(dates_object.starting_at)
- ),
- "end": (
- (
- self.get_isoformat_value_with_z(dates_object.ending_at)
- if datetimes
- else self.get_date(dates_object.ending_at)
- )
- if dates_object.ending_at
- else None
- ),
- }
- for dates_object in dates_objects
- ]
-
- def get_lifecycle_dates_for_status(
- self, plan_base: models.PlanBase, status_value: str
- ) -> list[models.LifeCycleDate]:
- """Returns the plan lifecycle date objects for the desired status."""
- return [
- lifecycle_date
- for lifecycle_date in plan_base.lifecycle_dates
- if lifecycle_date.lifecycle_status.value == status_value
- ]
-
- def get_lifecycle_periods(
- self, plan_base: models.PlanBase, status_value: str, datetimes: bool = True
- ) -> list[Period]:
- """Returns the start and end datetimes of lifecycle status for object. Optionally,
- only dates instead of datetimes may be returned.
-
- Note that for some lifecycle statuses, we may return multiple periods.
- """
- lifecycle_dates = self.get_lifecycle_dates_for_status(plan_base, status_value)
- return self.get_periods(lifecycle_dates, datetimes)
-
- def get_event_periods(
- self,
- lifecycle_date: models.LifeCycleDate,
- event_class: type[codes.CodeBase],
- event_value: str,
- datetimes: bool = True,
- ) -> list[Period]:
- """Returns the start and end datetimes of events with desired class and value
- linked to a lifecycle date object. Optionally, only dates instead of datetimes
- may be returned.
-
- Note that if the event occurs multiple times, we may return multiple periods.
- """
-
- def class_and_value_match(event_date: models.EventDate) -> bool:
- return bool(
- (
- event_class is NameOfPlanCaseDecision
- and event_date.decision
- and event_date.decision.value == event_value
- )
- or (
- event_class is TypeOfProcessingEvent
- and event_date.processing_event
- and event_date.processing_event.value == event_value
- )
- or (
- event_class is TypeOfInteractionEvent
- and event_date.interaction_event
- and event_date.interaction_event.value == event_value
- )
- )
-
- event_dates = [
- date for date in lifecycle_date.event_dates if class_and_value_match(date)
- ]
- return self.get_periods(event_dates, datetimes)
-
- def get_last_period(self, periods: list[Period]) -> Period | None:
- """Returns the last period in the list, or None if the list is empty."""
- return periods[-1] if periods else None
-
def serialize_date_period(
self, date_start: datetime.date, date_end: datetime.date | None
) -> Period:
@@ -786,223 +680,6 @@ def add_document_to_plan_dict(
)
return plan_dictionary
- def get_plan_decisions(self, plan: models.Plan) -> list[RyhtiPlanDecision]:
- """Construct a list of Ryhti compatible plan decisions from plan in the local
- database.
- """
- decisions: list[RyhtiPlanDecision] = []
- # Decision name must correspond to the phase the plan is in. This requires
- # mapping from lifecycle statuses to decision names.
- print(decisions_by_status.get(plan.lifecycle_status.value, []))
- for decision_value in decisions_by_status.get(plan.lifecycle_status.value, []):
- entry = RyhtiPlanDecision()
- # TODO: Let's just have random uuid for now, on the assumption that each
- # phase is only POSTed to ryhti once. If planners need to post and repost
- # the same phase, script needs logic to check if the phase exists in Ryhti
- # already before reposting.
- entry["planDecisionKey"] = str(uuid4())
- entry["name"] = get_code_uri(NameOfPlanCaseDecision, decision_value)
- entry["typeOfDecisionMaker"] = get_code_uri(
- TypeOfDecisionMaker,
- decisionmaker_by_status[plan.lifecycle_status.value],
- )
- # Plan must be embedded in decision when POSTing!
- entry["plans"] = [self.plan_dictionaries[plan.id]]
-
- try:
- lifecycle_date = self.get_lifecycle_dates_for_status(
- plan, plan.lifecycle_status.value
- )[-1]
- except IndexError:
- # If we have an old plan with no phase data, we cannot add decisions.
- LOGGER.warning(
- "Error in plan! Current lifecycle status is missing start date."
- )
- continue
- # Decision date will be
- # 1) decision date if found in database or, if not found,
- # 2) start of the current status period is used as backup.
- # TODO: Remove 2) once QGIS makes sure all necessary dates are filled in
- # manually (or automatically).
- period_of_decision = self.get_last_period(
- self.get_event_periods(
- lifecycle_date,
- NameOfPlanCaseDecision,
- decision_value,
- datetimes=False,
- )
- )
- period_of_current_status = self.get_periods(
- [lifecycle_date], datetimes=False
- )[-1]
- # Decision has no duration:
- entry["decisionDate"] = (
- period_of_decision["begin"]
- if period_of_decision
- else period_of_current_status["begin"]
- )
- entry["dateOfDecision"] = entry["decisionDate"]
-
- decisions.append(entry)
- return decisions
-
- def get_plan_handling_events(self, plan: models.Plan) -> list[RyhtiHandlingEvent]:
- """Construct a list of Ryhti compatible plan handling events from plan in the
- local database.
- """
- events: list[RyhtiHandlingEvent] = []
- # Decision name must correspond to the phase the plan is in. This requires
- # mapping from lifecycle statuses to decision names.
- for event_value in processing_events_by_status.get(
- plan.lifecycle_status.value, []
- ):
- entry = RyhtiHandlingEvent()
- # TODO: Let's just have random uuid for now, on the assumption that each
- # phase is only POSTed to ryhti once. If planners need to post and repost
- # the same phase, script needs logic to check if the phase exists in Ryhti
- # already before reposting.
- entry["handlingEventKey"] = str(uuid4())
- entry["handlingEventType"] = get_code_uri(
- TypeOfProcessingEvent, event_value
- )
-
- try:
- lifecycle_date = self.get_lifecycle_dates_for_status(
- plan, plan.lifecycle_status.value
- )[-1]
- except IndexError:
- # If we have an old plan with no phase data, we cannot add any events.
- LOGGER.warning(
- "Error in plan! Current lifecycle status is missing start date."
- )
- continue
- # Handling event date will be
- # 1) handling event date if found in database or, if not found,
- # 2) start of the current status period is used as backup.
- # TODO: Remove 2) once QGIS makes sure all necessary dates are filled in
- # manually (or automatically).
- period_of_handling_event = self.get_last_period(
- self.get_event_periods(
- lifecycle_date, TypeOfProcessingEvent, event_value, datetimes=False
- )
- )
- period_of_current_status = self.get_periods(
- [lifecycle_date], datetimes=False
- )[-1]
- # Handling event has no duration:
- entry["eventTime"] = (
- period_of_handling_event["begin"]
- if period_of_handling_event
- else period_of_current_status["begin"]
- )
- entry["cancelled"] = False
-
- events.append(entry)
- return events
-
- def get_interaction_events(self, plan: models.Plan) -> list[RyhtiInteractionEvent]:
- """Construct a list of Ryhti compatible interaction events from plan in the local
- database.
- """
- events: list[RyhtiInteractionEvent] = []
- # Decision name must correspond to the phase the plan is in. This requires
- # mapping from lifecycle statuses to decision names.
- for event_value in interaction_events_by_status.get(
- plan.lifecycle_status.value, []
- ):
- entry = RyhtiInteractionEvent()
- # TODO: Let's just have random uuid for now, on the assumption that each
- # phase is only POSTed to ryhti once. If planners need to post and repost
- # the same phase, script needs logic to check if the phase exists in Ryhti
- # already before reposting.
- entry["interactionEventKey"] = str(uuid4())
- entry["interactionEventType"] = get_code_uri(
- TypeOfInteractionEvent, event_value
- )
-
- try:
- lifecycle_date = self.get_lifecycle_dates_for_status(
- plan, plan.lifecycle_status.value
- )[-1]
- except IndexError:
- # If we have an old plan with no phase data, we cannot add any events.
- LOGGER.warning(
- "Error in plan! Current lifecycle status is missing start date."
- )
- continue
- # Interaction event period will be
- # 1) interaction event period if found in database or, if not found,
- # 2) 30 days at start of the current status period (nähtävilläoloaika) are
- # used as backup.
- # TODO: Remove 2) once QGIS makes sure all necessary dates are filled in
- # manually (or automatically).
- period_of_interaction_event = self.get_last_period(
- self.get_event_periods(
- lifecycle_date, TypeOfInteractionEvent, event_value
- )
- )
- period_of_current_status = self.get_periods([lifecycle_date])[-1]
- entry["eventTime"] = (
- period_of_interaction_event
- if period_of_interaction_event
- else {
- "begin": (period_of_current_status["begin"]),
- "end": (
- datetime.datetime.fromisoformat(
- period_of_current_status["begin"]
- )
- + datetime.timedelta(days=30)
- )
- .isoformat()
- .replace("+00:00", "Z"),
- }
- )
-
- events.append(entry)
- return events
-
- def get_plan_matter_phases(self, plan: models.Plan) -> list[RyhtiPlanMatterPhase]:
- """Construct a list of Ryhti compatible plan matter phases from plan in the local
- database.
-
- Currently, we only return the *current* phase, because our database does *not*
- save plan history between phases. However, we could return multiple phases or
- multiple decisions in the future, if there is a need to POST all the dates
- saved in the lifecycle_dates table.
-
- TODO: perhaps we will have to return multiple phases, if there may be multiple
- decision or multiple processing events in this phase. However, if we are only
- returning one phase per phase, then let's just return one phase. Simple, isn't
- it?
- """
- phase = RyhtiPlanMatterPhase()
- # TODO: Let's just have random uuid for now, on the assumption that each phase
- # is only POSTed to ryhti once. If planners need to post and repost the same
- # phase, script needs logic to check if the phase exists in Ryhti already before
- # reposting.
- #
- # However, such logic may not be forthcoming, if multiple phases with
- # the same lifecycle status are allowed??
- phase["planMatterPhaseKey"] = str(uuid4())
- # Always post phase and plan with the same status.
- phase["lifeCycleStatus"] = self.plan_dictionaries[plan.id]["lifeCycleStatus"]
- phase["geographicalArea"] = self.plan_dictionaries[plan.id]["geographicalArea"]
-
- # TODO: currently, the API spec only allows for one plan decision per phase,
- # for reasons unknown. Therefore, let's pick the first possible decision in
- # each phase.
- plan_decisions = self.get_plan_decisions(plan)
- phase["planDecision"] = plan_decisions[0] if plan_decisions else None
- # TODO: currently, the API spec only allows for one plan handling event per
- # phase, for reasons unknown. Therefore, let's pick the first possible event in
- # each phase.
- handling_events = self.get_plan_handling_events(plan)
- phase["handlingEvent"] = handling_events[0] if handling_events else None
- interaction_events = self.get_interaction_events(plan)
- phase["interactionEvents"] = interaction_events if interaction_events else None
-
- return [phase]
-
def get_source_datas(self, plan: models.Plan) -> list[dict]:
"""Construct a list of Ryhti compatible source datas from plan in the local
database.
@@ -1011,74 +688,10 @@ def get_source_datas(self, plan: models.Plan) -> list[dict]:
return []
def get_plan_matter(self, plan: models.Plan) -> RyhtiPlanMatter:
- """Construct a dict of single Ryhti compatible plan matter from plan in the local
- database.
- """
- ryhti_plan_matter = RyhtiPlanMatter()
-
- plan_matter = plan.plan_matter
-
- # TODO: permanentPlanIdentifier should be mandatory in this stage
- ryhti_plan_matter["permanentPlanIdentifier"] = (
- plan_matter.permanent_plan_identifier
- )
-
- # Plan type has to be proper URI (not just value) here, *unlike* when only
- # validating plan. Go figure.
- ryhti_plan_matter["planType"] = plan_matter.plan_type.uri
- # For reasons unknown, name is needed for plan matter but not for plan. Plan
- # only contains description, and only in one language.
- # TODO: name should be mandatory in this stage
- ryhti_plan_matter["name"] = self.format_language_string_value(plan_matter.name)
-
- # we should only have one pending period. If there are several, pick last
- dates_of_initiation = self.get_last_period(
- self.get_lifecycle_periods(plan, self.pending_status_value, datetimes=False)
- )
- ryhti_plan_matter["timeOfInitiation"] = (
- dates_of_initiation["begin"] if dates_of_initiation else None
- )
- # Hooray, unlike plan, the plan *matter* description allows multilanguage data!
- ryhti_plan_matter["description"] = self.format_language_string_value(
- plan_matter.description
- )
- ryhti_plan_matter["producerPlanIdentifier"] = (
- plan_matter.producers_plan_identifier
- )
- ryhti_plan_matter["caseIdentifiers"] = (
- [plan_matter.case_identifier] if plan_matter.case_identifier else []
- )
- ryhti_plan_matter["recordNumbers"] = (
- [plan_matter.record_number] if plan_matter.record_number else []
- )
- # Apparently Ryhti plans may cover multiple administrative areas, so the region
- # identifier has to be embedded in a list.
- ryhti_plan_matter["administrativeAreaIdentifiers"] = [
- (
- plan_matter.organisation.municipality.value
- if plan_matter.organisation.municipality
- else plan_matter.organisation.administrative_region.value
- )
- ]
- # We have no need of importing the digital origin code list as long as we are
- # not digitizing old plans:
- ryhti_plan_matter["digitalOrigin"] = (
- "http://uri.suomi.fi/codelist/rytj/RY_DigitaalinenAlkupera/code/01"
- )
- # TODO: kaava-asian liitteet
- # are these different from plan annexes? why? how??
- # plan_matter["matterAnnexes"] = self.get_plan_matter_annexes(plan)
- # TODO: lähdeaineistot
- # plan_matter["sourceDatas"] = self.get_source_datas(plan)
- ryhti_plan_matter["planMatterPhases"] = self.get_plan_matter_phases(plan)
- return ryhti_plan_matter
+ raise NotImplementedError
def get_plan_matters(self) -> dict[DbId, RyhtiPlanMatter]:
- """Construct a dict of Ryhti compatible plan matters from plans with
- permanent identifiers in the local database. In case plan has no
- permanent identifier, it is not included in the dict.
- """
- return {plan.id: self.get_plan_matter(plan) for plan in self.plans.values()}
+ raise NotImplementedError
def save_plan_validation_responses(
self, responses: dict[DbId, RyhtiResponse]
diff --git a/migrations/env.py b/migrations/env.py
index 5316739..bd698b0 100644
--- a/migrations/env.py
+++ b/migrations/env.py
@@ -17,37 +17,15 @@
generate_add_plan_id_fkey_triggers,
generate_instead_of_triggers_for_visualization_views,
generate_modified_at_triggers,
- generate_new_lifecycle_date_triggers,
generate_new_lifecycle_status_triggers,
- generate_new_object_add_lifecycle_date_triggers,
generate_update_lifecycle_status_triggers,
generate_created_at_triggers,
generate_no_created_at_update_triggers
)
-from database.validation import (
- trg_validate_event_date,
- trg_validate_event_date_inside_status_date,
- trg_validate_event_type,
- trg_validate_lifecycle_date,
- trgfunc_validate_event_date,
- trgfunc_validate_event_date_inside_status_date,
- trgfunc_validate_event_type,
- trgfunc_validate_lifecycle_date,
-)
from database.views import views
modified_at_trgs, modified_at_trgfuncs = generate_modified_at_triggers()
-(
- new_object_add_lifecycle_date_trgs,
- new_object_add_lifecycle_date_trgfuncs,
-) = generate_new_object_add_lifecycle_date_triggers()
-
-(
- new_lifecycle_date_trgs,
- new_lifecycle_date_trgfuncs,
-) = generate_new_lifecycle_date_triggers()
-
(
update_lifecycle_status_trgs,
update_lifecycle_status_trgfuncs,
@@ -79,10 +57,6 @@
imported_triggers = (
modified_at_trgfuncs
+ modified_at_trgs
- + new_object_add_lifecycle_date_trgfuncs
- + new_object_add_lifecycle_date_trgs
- + new_lifecycle_date_trgfuncs
- + new_lifecycle_date_trgs
+ update_lifecycle_status_trgfuncs
+ update_lifecycle_status_trgs
+ new_lifecycle_status_trgfuncs
@@ -95,14 +69,6 @@
+ created_at_trgfuncs
+ no_created_at_update_trgs
+ no_created_at_update_trgfuncs
- + [trgfunc_validate_lifecycle_date]
- + [trg_validate_lifecycle_date]
- + [trgfunc_validate_event_date]
- + [trg_validate_event_date]
- + [trgfunc_validate_event_date_inside_status_date]
- + [trg_validate_event_date_inside_status_date]
- + [trgfunc_validate_event_type]
- + [trg_validate_event_type]
)
register_entities(imported_triggers)
diff --git a/migrations/versions/2026_02_04_1234-d83654421156_remove_lifecycle_history_tables.py b/migrations/versions/2026_02_04_1234-d83654421156_remove_lifecycle_history_tables.py
new file mode 100644
index 0000000..abf9dce
--- /dev/null
+++ b/migrations/versions/2026_02_04_1234-d83654421156_remove_lifecycle_history_tables.py
@@ -0,0 +1,819 @@
+"""Remove lifecycle history tables
+
+Revision ID: d83654421156
+Revises: 489b23cdab0a
+Create Date: 2026-02-04 12:34:09.555102
+
+"""
+
+from collections.abc import Sequence
+
+import geoalchemy2
+import sqlalchemy as sa
+from alembic import op
+from alembic_utils.pg_function import PGFunction
+from alembic_utils.pg_trigger import PGTrigger
+from sqlalchemy import text as sql_text
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision: str = "d83654421156"
+down_revision: str | None = "489b23cdab0a"
+branch_labels: str | Sequence[str] | None = None
+depends_on: str | Sequence[str] | None = None
+
+
+def upgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+
+
+ hame_event_date_trg_event_date_modified_at = PGTrigger(
+ schema="hame",
+ signature="trg_event_date_modified_at",
+ on_entity="hame.event_date",
+ is_constraint=False,
+ definition="BEFORE INSERT OR UPDATE ON hame.event_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_modified_at()",
+ )
+ op.drop_entity(hame_event_date_trg_event_date_modified_at)
+
+ hame_lifecycle_date_trg_lifecycle_date_validate_dates = PGTrigger(
+ schema="hame",
+ signature="trg_lifecycle_date_validate_dates",
+ on_entity="hame.lifecycle_date",
+ is_constraint=False,
+ definition="BEFORE INSERT OR UPDATE ON hame.lifecycle_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_lifecycle_date_validate_dates()",
+ )
+ op.drop_entity(hame_lifecycle_date_trg_lifecycle_date_validate_dates)
+
+ hame_event_date_trg_event_date_validate_dates = PGTrigger(
+ schema="hame",
+ signature="trg_event_date_validate_dates",
+ on_entity="hame.event_date",
+ is_constraint=False,
+ definition="BEFORE INSERT OR UPDATE ON hame.event_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_event_date_validate_dates()",
+ )
+ op.drop_entity(hame_event_date_trg_event_date_validate_dates)
+
+ hame_event_date_trg_event_date_validate_inside_status_date = PGTrigger(
+ schema="hame",
+ signature="trg_event_date_validate_inside_status_date",
+ on_entity="hame.event_date",
+ is_constraint=False,
+ definition="BEFORE INSERT OR UPDATE ON hame.event_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_event_date_validate_inside_status_date()",
+ )
+ op.drop_entity(hame_event_date_trg_event_date_validate_inside_status_date)
+
+ hame_event_date_trg_event_date_validate_type = PGTrigger(
+ schema="hame",
+ signature="trg_event_date_validate_type",
+ on_entity="hame.event_date",
+ is_constraint=False,
+ definition="BEFORE INSERT OR UPDATE ON hame.event_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_event_date_validate_type()",
+ )
+ op.drop_entity(hame_event_date_trg_event_date_validate_type)
+
+ hame_land_use_area_trg_new_land_use_area_add_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_new_land_use_area_add_lifecycle_date",
+ on_entity="hame.land_use_area",
+ is_constraint=False,
+ definition="AFTER INSERT ON hame.land_use_area FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_new_object_add_lifecycle_date()",
+ )
+ op.drop_entity(hame_land_use_area_trg_new_land_use_area_add_lifecycle_date)
+
+ hame_line_trg_new_line_add_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_new_line_add_lifecycle_date",
+ on_entity="hame.line",
+ is_constraint=False,
+ definition="AFTER INSERT ON hame.line FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_new_object_add_lifecycle_date()",
+ )
+ op.drop_entity(hame_line_trg_new_line_add_lifecycle_date)
+
+ hame_other_area_trg_new_other_area_add_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_new_other_area_add_lifecycle_date",
+ on_entity="hame.other_area",
+ is_constraint=False,
+ definition="AFTER INSERT ON hame.other_area FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_new_object_add_lifecycle_date()",
+ )
+ op.drop_entity(hame_other_area_trg_new_other_area_add_lifecycle_date)
+
+ hame_plan_trg_new_plan_add_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_new_plan_add_lifecycle_date",
+ on_entity="hame.plan",
+ is_constraint=False,
+ definition="AFTER INSERT ON hame.plan FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_new_object_add_lifecycle_date()",
+ )
+ op.drop_entity(hame_plan_trg_new_plan_add_lifecycle_date)
+
+ hame_plan_proposition_trg_new_plan_proposition_add_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_new_plan_proposition_add_lifecycle_date",
+ on_entity="hame.plan_proposition",
+ is_constraint=False,
+ definition="AFTER INSERT ON hame.plan_proposition FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_new_object_add_lifecycle_date()",
+ )
+ op.drop_entity(hame_plan_proposition_trg_new_plan_proposition_add_lifecycle_date)
+
+ hame_plan_regulation_trg_new_plan_regulation_add_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_new_plan_regulation_add_lifecycle_date",
+ on_entity="hame.plan_regulation",
+ is_constraint=False,
+ definition="AFTER INSERT ON hame.plan_regulation FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_new_object_add_lifecycle_date()",
+ )
+ op.drop_entity(hame_plan_regulation_trg_new_plan_regulation_add_lifecycle_date)
+
+ hame_land_use_area_trg_land_use_area_new_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_land_use_area_new_lifecycle_date",
+ on_entity="hame.land_use_area",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.land_use_area FOR EACH ROW WHEN ((new.lifecycle_status_id <> old.lifecycle_status_id)) EXECUTE FUNCTION hame.trgfunc_new_lifecycle_date()",
+ )
+ op.drop_entity(hame_land_use_area_trg_land_use_area_new_lifecycle_date)
+
+ hame_line_trg_line_new_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_line_new_lifecycle_date",
+ on_entity="hame.line",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.line FOR EACH ROW WHEN ((new.lifecycle_status_id <> old.lifecycle_status_id)) EXECUTE FUNCTION hame.trgfunc_new_lifecycle_date()",
+ )
+ op.drop_entity(hame_line_trg_line_new_lifecycle_date)
+
+ hame_other_area_trg_other_area_new_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_other_area_new_lifecycle_date",
+ on_entity="hame.other_area",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.other_area FOR EACH ROW WHEN ((new.lifecycle_status_id <> old.lifecycle_status_id)) EXECUTE FUNCTION hame.trgfunc_new_lifecycle_date()",
+ )
+ op.drop_entity(hame_other_area_trg_other_area_new_lifecycle_date)
+
+ hame_plan_trg_plan_new_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_plan_new_lifecycle_date",
+ on_entity="hame.plan",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.plan FOR EACH ROW WHEN ((new.lifecycle_status_id <> old.lifecycle_status_id)) EXECUTE FUNCTION hame.trgfunc_new_lifecycle_date()",
+ )
+ op.drop_entity(hame_plan_trg_plan_new_lifecycle_date)
+
+ hame_plan_proposition_trg_plan_proposition_new_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_plan_proposition_new_lifecycle_date",
+ on_entity="hame.plan_proposition",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.plan_proposition FOR EACH ROW WHEN ((new.lifecycle_status_id <> old.lifecycle_status_id)) EXECUTE FUNCTION hame.trgfunc_new_lifecycle_date()",
+ )
+ op.drop_entity(hame_plan_proposition_trg_plan_proposition_new_lifecycle_date)
+
+ hame_plan_regulation_trg_plan_regulation_new_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_plan_regulation_new_lifecycle_date",
+ on_entity="hame.plan_regulation",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.plan_regulation FOR EACH ROW WHEN ((new.lifecycle_status_id <> old.lifecycle_status_id)) EXECUTE FUNCTION hame.trgfunc_new_lifecycle_date()",
+ )
+ op.drop_entity(hame_plan_regulation_trg_plan_regulation_new_lifecycle_date)
+
+ hame_point_trg_new_point_add_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_new_point_add_lifecycle_date",
+ on_entity="hame.point",
+ is_constraint=False,
+ definition="AFTER INSERT ON hame.point FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_new_object_add_lifecycle_date()",
+ )
+ op.drop_entity(hame_point_trg_new_point_add_lifecycle_date)
+
+ hame_point_trg_point_new_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_point_new_lifecycle_date",
+ on_entity="hame.point",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.point FOR EACH ROW WHEN ((new.lifecycle_status_id <> old.lifecycle_status_id)) EXECUTE FUNCTION hame.trgfunc_new_lifecycle_date()",
+ )
+ op.drop_entity(hame_point_trg_point_new_lifecycle_date)
+
+ hame_lifecycle_date_trg_lifecycle_date_created_at = PGTrigger(
+ schema="hame",
+ signature="trg_lifecycle_date_created_at",
+ on_entity="hame.lifecycle_date",
+ is_constraint=False,
+ definition="BEFORE INSERT ON hame.lifecycle_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_created_at()",
+ )
+ op.drop_entity(hame_lifecycle_date_trg_lifecycle_date_created_at)
+
+ hame_event_date_trg_event_date_created_at = PGTrigger(
+ schema="hame",
+ signature="trg_event_date_created_at",
+ on_entity="hame.event_date",
+ is_constraint=False,
+ definition="BEFORE INSERT ON hame.event_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_created_at()",
+ )
+ op.drop_entity(hame_event_date_trg_event_date_created_at)
+
+ hame_lifecycle_date_trg_lifecycle_date_001_no_created_at_update = PGTrigger(
+ schema="hame",
+ signature="trg_lifecycle_date_001_no_created_at_update",
+ on_entity="hame.lifecycle_date",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.lifecycle_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_no_created_at_update()",
+ )
+ op.drop_entity(hame_lifecycle_date_trg_lifecycle_date_001_no_created_at_update)
+
+ hame_event_date_trg_event_date_001_no_created_at_update = PGTrigger(
+ schema="hame",
+ signature="trg_event_date_001_no_created_at_update",
+ on_entity="hame.event_date",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.event_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_no_created_at_update()",
+ )
+ op.drop_entity(hame_event_date_trg_event_date_001_no_created_at_update)
+
+ hame_trgfunc_lifecycle_date_validate_dates = PGFunction(
+ schema="hame",
+ signature="trgfunc_lifecycle_date_validate_dates()",
+ definition="returns trigger\n LANGUAGE plpgsql\nAS $function$\n BEGIN\n IF (\n NEW.starting_at IS NOT NULL AND\n NEW.ending_at IS NOT NULL AND\n NEW.starting_at > NEW.ending_at\n ) IS TRUE\n THEN\n RAISE EXCEPTION 'Status starting date % after ending date %',\n NEW.starting_at, NEW.ending_at\n USING HINT = 'Status ending date must be after starting date.';\n END IF;\n RETURN NEW;\n END;\n $function$",
+ )
+ op.drop_entity(hame_trgfunc_lifecycle_date_validate_dates)
+
+ hame_trgfunc_event_date_validate_dates = PGFunction(
+ schema="hame",
+ signature="trgfunc_event_date_validate_dates()",
+ definition="returns trigger\n LANGUAGE plpgsql\nAS $function$\n BEGIN\n IF (\n NEW.starting_at IS NOT NULL AND\n NEW.ending_at IS NOT NULL AND\n NEW.starting_at > NEW.ending_at\n ) IS TRUE\n THEN\n RAISE EXCEPTION 'Event starting date % after ending date %',\n NEW.starting_at, NEW.ending_at\n USING HINT = 'Event ending date must be after starting date.';\n END IF;\n RETURN NEW;\n END;\n $function$",
+ )
+ op.drop_entity(hame_trgfunc_event_date_validate_dates)
+
+ hame_trgfunc_event_date_validate_inside_status_date = PGFunction(
+ schema="hame",
+ signature="trgfunc_event_date_validate_inside_status_date()",
+ definition="returns trigger\n LANGUAGE plpgsql\nAS $function$\n DECLARE\n status_starting_at TIMESTAMP WITH TIME ZONE;\n status_ending_at TIMESTAMP WITH TIME ZONE;\n BEGIN\n SELECT starting_at, ending_at INTO status_starting_at, status_ending_at\n FROM hame.lifecycle_date\n WHERE NEW.lifecycle_date_id = hame.lifecycle_date.id;\n IF (\n -- Does the event start before status starts?\n (\n NEW.starting_at < status_starting_at\n ) OR\n -- Missing event ending date means event is instantaneous. Only\n -- events with ending date have a duration. Both must have ending\n -- date specified to check if event ends after status ends.\n (\n NEW.ending_at IS NOT NULL AND status_ending_at IS NOT NULL AND\n NEW.ending_at > status_ending_at\n )\n ) IS TRUE\n THEN\n RAISE EXCEPTION 'Event dates % - % outside status dates % - %',\n NEW.starting_at, NEW.ending_at, status_starting_at, status_ending_at\n USING HINT = 'Event cannot be outside lifecycle status dates.';\n END IF;\n RETURN NEW;\n END;\n $function$",
+ )
+ op.drop_entity(hame_trgfunc_event_date_validate_inside_status_date)
+
+ hame_trgfunc_event_date_validate_type = PGFunction(
+ schema="hame",
+ signature="trgfunc_event_date_validate_type()",
+ definition="returns trigger\n LANGUAGE plpgsql\nAS $function$\n DECLARE\n status_id UUID;\n association_id UUID;\n BEGIN\n SELECT lifecycle_status_id INTO status_id\n FROM\n hame.lifecycle_date\n WHERE\n NEW.lifecycle_date_id = lifecycle_date.id;\n SELECT id INTO association_id\n FROM\n codes.allowed_events\n WHERE\n lifecycle_status_id = status_id AND (\n (NEW.decision_id IS NOT NULL AND\n name_of_plan_case_decision_id = NEW.decision_id) OR\n (NEW.processing_event_id IS NOT NULL AND\n type_of_processing_event_id = NEW.processing_event_id) OR\n (NEW.interaction_event_id IS NOT NULL AND\n type_of_interaction_event_id = NEW.interaction_event_id)\n );\n IF association_id IS NULL\n THEN\n RAISE EXCEPTION 'Wrong event type for status'\n USING HINT = 'This event type cannot be added to this lifecycle status.';\n END IF;\n RETURN NEW;\n END;\n $function$",
+ )
+ op.drop_entity(hame_trgfunc_event_date_validate_type)
+
+ hame_trgfunc_new_object_add_lifecycle_date = PGFunction(
+ schema="hame",
+ signature="trgfunc_new_object_add_lifecycle_date()",
+ definition="returns trigger\n LANGUAGE plpgsql\nAS $function$\n BEGIN\n EXECUTE format(\n $query$\n INSERT INTO hame.lifecycle_date\n (lifecycle_status_id, %I, starting_at)\n VALUES\n ($1, $2, CURRENT_TIMESTAMP)\n $query$,\n TG_TABLE_NAME || '_id'\n ) USING NEW.lifecycle_status_id, NEW.id;\n RETURN NEW;\n END;\n $function$",
+ )
+ op.drop_entity(hame_trgfunc_new_object_add_lifecycle_date)
+
+ hame_trgfunc_new_lifecycle_date = PGFunction(
+ schema="hame",
+ signature="trgfunc_new_lifecycle_date()",
+ definition="returns trigger\n LANGUAGE plpgsql\nAS $function$\n BEGIN\n EXECUTE format(\n $query$\n INSERT INTO hame.lifecycle_date\n (lifecycle_status_id, %I, starting_at)\n VALUES\n ($1, $2, CURRENT_TIMESTAMP)\n $query$,\n TG_TABLE_NAME || '_id'\n ) USING NEW.lifecycle_status_id, NEW.id;\n EXECUTE format(\n $query$\n UPDATE hame.lifecycle_date\n SET ending_at=CURRENT_TIMESTAMP\n WHERE %I = $1\n AND ending_at IS NULL\n AND lifecycle_status_id = $2\n $query$,\n TG_TABLE_NAME || '_id'\n ) USING NEW.id, OLD.lifecycle_status_id;\n RETURN NEW;\n END;\n $function$",
+ )
+ op.drop_entity(hame_trgfunc_new_lifecycle_date)
+
+ hame_lifecycle_date_trg_lifecycle_date_modified_at = PGTrigger(
+ schema="hame",
+ signature="trg_lifecycle_date_modified_at",
+ on_entity="hame.lifecycle_date",
+ is_constraint=False,
+ definition="BEFORE INSERT OR UPDATE ON hame.lifecycle_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_modified_at()",
+ )
+ op.drop_entity(hame_lifecycle_date_trg_lifecycle_date_modified_at)
+
+ op.drop_index(
+ op.f("ix_hame_event_date_lifecycle_date_id"),
+ table_name="event_date",
+ schema="hame",
+ )
+ op.drop_table("event_date", schema="hame")
+ op.drop_index(
+ op.f("ix_hame_lifecycle_date_land_use_area_id"),
+ table_name="lifecycle_date",
+ schema="hame",
+ )
+ op.drop_index(
+ op.f("ix_hame_lifecycle_date_lifecycle_status_id"),
+ table_name="lifecycle_date",
+ schema="hame",
+ )
+ op.drop_index(
+ op.f("ix_hame_lifecycle_date_line_id"),
+ table_name="lifecycle_date",
+ schema="hame",
+ )
+ op.drop_index(
+ op.f("ix_hame_lifecycle_date_other_area_id"),
+ table_name="lifecycle_date",
+ schema="hame",
+ )
+ op.drop_index(
+ op.f("ix_hame_lifecycle_date_plan_id"),
+ table_name="lifecycle_date",
+ schema="hame",
+ )
+ op.drop_index(
+ op.f("ix_hame_lifecycle_date_plan_proposition_id"),
+ table_name="lifecycle_date",
+ schema="hame",
+ )
+ op.drop_index(
+ op.f("ix_hame_lifecycle_date_plan_regulation_id"),
+ table_name="lifecycle_date",
+ schema="hame",
+ )
+ op.drop_index(
+ op.f("ix_hame_lifecycle_date_point_id"),
+ table_name="lifecycle_date",
+ schema="hame",
+ )
+ op.drop_table("lifecycle_date", schema="hame")
+
+
+
+ # ### end Alembic commands ###
+
+
+def downgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+
+
+ op.create_table(
+ "lifecycle_date",
+ sa.Column(
+ "lifecycle_status_id", sa.UUID(), autoincrement=False, nullable=False
+ ),
+ sa.Column("plan_id", sa.UUID(), autoincrement=False, nullable=True),
+ sa.Column("plan_regulation_id", sa.UUID(), autoincrement=False, nullable=True),
+ sa.Column("plan_proposition_id", sa.UUID(), autoincrement=False, nullable=True),
+ sa.Column(
+ "starting_at",
+ postgresql.TIMESTAMP(timezone=True),
+ autoincrement=False,
+ nullable=False,
+ ),
+ sa.Column(
+ "ending_at",
+ postgresql.TIMESTAMP(timezone=True),
+ autoincrement=False,
+ nullable=True,
+ ),
+ sa.Column(
+ "id",
+ sa.UUID(),
+ server_default=sa.text("gen_random_uuid()"),
+ autoincrement=False,
+ nullable=False,
+ ),
+ sa.Column(
+ "created_at",
+ postgresql.TIMESTAMP(timezone=True),
+ autoincrement=False,
+ nullable=True,
+ ),
+ sa.Column(
+ "modified_at",
+ postgresql.TIMESTAMP(timezone=True),
+ autoincrement=False,
+ nullable=True,
+ ),
+ sa.Column("land_use_area_id", sa.UUID(), autoincrement=False, nullable=True),
+ sa.Column("other_area_id", sa.UUID(), autoincrement=False, nullable=True),
+ sa.Column("line_id", sa.UUID(), autoincrement=False, nullable=True),
+ sa.Column("point_id", sa.UUID(), autoincrement=False, nullable=True),
+ sa.Column("creator", sa.VARCHAR(), autoincrement=False, nullable=True),
+ sa.Column("modifier", sa.VARCHAR(), autoincrement=False, nullable=True),
+ sa.ForeignKeyConstraint(
+ ["land_use_area_id"],
+ ["hame.land_use_area.id"],
+ name="land_use_area_id_fkey",
+ ondelete="CASCADE",
+ ),
+ sa.ForeignKeyConstraint(
+ ["lifecycle_status_id"],
+ ["codes.lifecycle_status.id"],
+ name="plan_lifecycle_status_id_fkey",
+ ),
+ sa.ForeignKeyConstraint(
+ ["line_id"], ["hame.line.id"], name="line_id_fkey", ondelete="CASCADE"
+ ),
+ sa.ForeignKeyConstraint(
+ ["other_area_id"],
+ ["hame.other_area.id"],
+ name="other_area_id_fkey",
+ ondelete="CASCADE",
+ ),
+ sa.ForeignKeyConstraint(
+ ["plan_id"], ["hame.plan.id"], name="plan_id_fkey", ondelete="CASCADE"
+ ),
+ sa.ForeignKeyConstraint(
+ ["plan_proposition_id"],
+ ["hame.plan_proposition.id"],
+ name="plan_proposition_id_fkey",
+ ondelete="CASCADE",
+ ),
+ sa.ForeignKeyConstraint(
+ ["plan_regulation_id"],
+ ["hame.plan_regulation.id"],
+ name="plan_regulation_id_fkey",
+ ondelete="CASCADE",
+ ),
+ sa.ForeignKeyConstraint(
+ ["point_id"], ["hame.point.id"], name="point_id_fkey", ondelete="CASCADE"
+ ),
+ sa.PrimaryKeyConstraint("id", name="lifecycle_date_pkey"),
+ schema="hame",
+ postgresql_ignore_search_path=False,
+ )
+ op.create_index(
+ op.f("ix_hame_lifecycle_date_point_id"),
+ "lifecycle_date",
+ ["point_id"],
+ unique=False,
+ schema="hame",
+ )
+ op.create_index(
+ op.f("ix_hame_lifecycle_date_plan_regulation_id"),
+ "lifecycle_date",
+ ["plan_regulation_id"],
+ unique=False,
+ schema="hame",
+ )
+ op.create_index(
+ op.f("ix_hame_lifecycle_date_plan_proposition_id"),
+ "lifecycle_date",
+ ["plan_proposition_id"],
+ unique=False,
+ schema="hame",
+ )
+ op.create_index(
+ op.f("ix_hame_lifecycle_date_plan_id"),
+ "lifecycle_date",
+ ["plan_id"],
+ unique=False,
+ schema="hame",
+ )
+ op.create_index(
+ op.f("ix_hame_lifecycle_date_other_area_id"),
+ "lifecycle_date",
+ ["other_area_id"],
+ unique=False,
+ schema="hame",
+ )
+ op.create_index(
+ op.f("ix_hame_lifecycle_date_line_id"),
+ "lifecycle_date",
+ ["line_id"],
+ unique=False,
+ schema="hame",
+ )
+ op.create_index(
+ op.f("ix_hame_lifecycle_date_lifecycle_status_id"),
+ "lifecycle_date",
+ ["lifecycle_status_id"],
+ unique=False,
+ schema="hame",
+ )
+ op.create_index(
+ op.f("ix_hame_lifecycle_date_land_use_area_id"),
+ "lifecycle_date",
+ ["land_use_area_id"],
+ unique=False,
+ schema="hame",
+ )
+ op.create_table(
+ "event_date",
+ sa.Column("lifecycle_date_id", sa.UUID(), autoincrement=False, nullable=False),
+ sa.Column("decision_id", sa.UUID(), autoincrement=False, nullable=True),
+ sa.Column("processing_event_id", sa.UUID(), autoincrement=False, nullable=True),
+ sa.Column(
+ "interaction_event_id", sa.UUID(), autoincrement=False, nullable=True
+ ),
+ sa.Column(
+ "starting_at",
+ postgresql.TIMESTAMP(timezone=True),
+ autoincrement=False,
+ nullable=False,
+ ),
+ sa.Column(
+ "ending_at",
+ postgresql.TIMESTAMP(timezone=True),
+ autoincrement=False,
+ nullable=True,
+ ),
+ sa.Column(
+ "id",
+ sa.UUID(),
+ server_default=sa.text("gen_random_uuid()"),
+ autoincrement=False,
+ nullable=False,
+ ),
+ sa.Column(
+ "created_at",
+ postgresql.TIMESTAMP(timezone=True),
+ autoincrement=False,
+ nullable=True,
+ ),
+ sa.Column(
+ "modified_at",
+ postgresql.TIMESTAMP(timezone=True),
+ autoincrement=False,
+ nullable=True,
+ ),
+ sa.Column("creator", sa.VARCHAR(), autoincrement=False, nullable=True),
+ sa.Column("modifier", sa.VARCHAR(), autoincrement=False, nullable=True),
+ sa.ForeignKeyConstraint(
+ ["decision_id"],
+ ["codes.name_of_plan_case_decision.id"],
+ name=op.f("name_of_plan_case_decision_id_fkey"),
+ ),
+ sa.ForeignKeyConstraint(
+ ["interaction_event_id"],
+ ["codes.type_of_interaction_event.id"],
+ name=op.f("type_of_interaction_event_fkey"),
+ ),
+ sa.ForeignKeyConstraint(
+ ["lifecycle_date_id"],
+ ["hame.lifecycle_date.id"],
+ name=op.f("lifecycle_date_id_fkey"),
+ ondelete="CASCADE",
+ ),
+ sa.ForeignKeyConstraint(
+ ["processing_event_id"],
+ ["codes.type_of_processing_event.id"],
+ name=op.f("type_of_processing_event_fkey"),
+ ),
+ sa.PrimaryKeyConstraint("id", name=op.f("event_date_pkey")),
+ schema="hame",
+ )
+ op.create_index(
+ op.f("ix_hame_event_date_lifecycle_date_id"),
+ "event_date",
+ ["lifecycle_date_id"],
+ unique=False,
+ schema="hame",
+ )
+
+ hame_trgfunc_new_lifecycle_date = PGFunction(
+ schema="hame",
+ signature="trgfunc_new_lifecycle_date()",
+ definition="returns trigger\n LANGUAGE plpgsql\nAS $function$\n BEGIN\n EXECUTE format(\n $query$\n INSERT INTO hame.lifecycle_date\n (lifecycle_status_id, %I, starting_at)\n VALUES\n ($1, $2, CURRENT_TIMESTAMP)\n $query$,\n TG_TABLE_NAME || '_id'\n ) USING NEW.lifecycle_status_id, NEW.id;\n EXECUTE format(\n $query$\n UPDATE hame.lifecycle_date\n SET ending_at=CURRENT_TIMESTAMP\n WHERE %I = $1\n AND ending_at IS NULL\n AND lifecycle_status_id = $2\n $query$,\n TG_TABLE_NAME || '_id'\n ) USING NEW.id, OLD.lifecycle_status_id;\n RETURN NEW;\n END;\n $function$",
+ )
+ op.create_entity(hame_trgfunc_new_lifecycle_date)
+
+ hame_trgfunc_new_object_add_lifecycle_date = PGFunction(
+ schema="hame",
+ signature="trgfunc_new_object_add_lifecycle_date()",
+ definition="returns trigger\n LANGUAGE plpgsql\nAS $function$\n BEGIN\n EXECUTE format(\n $query$\n INSERT INTO hame.lifecycle_date\n (lifecycle_status_id, %I, starting_at)\n VALUES\n ($1, $2, CURRENT_TIMESTAMP)\n $query$,\n TG_TABLE_NAME || '_id'\n ) USING NEW.lifecycle_status_id, NEW.id;\n RETURN NEW;\n END;\n $function$",
+ )
+ op.create_entity(hame_trgfunc_new_object_add_lifecycle_date)
+
+ hame_trgfunc_event_date_validate_type = PGFunction(
+ schema="hame",
+ signature="trgfunc_event_date_validate_type()",
+ definition="returns trigger\n LANGUAGE plpgsql\nAS $function$\n DECLARE\n status_id UUID;\n association_id UUID;\n BEGIN\n SELECT lifecycle_status_id INTO status_id\n FROM\n hame.lifecycle_date\n WHERE\n NEW.lifecycle_date_id = lifecycle_date.id;\n SELECT id INTO association_id\n FROM\n codes.allowed_events\n WHERE\n lifecycle_status_id = status_id AND (\n (NEW.decision_id IS NOT NULL AND\n name_of_plan_case_decision_id = NEW.decision_id) OR\n (NEW.processing_event_id IS NOT NULL AND\n type_of_processing_event_id = NEW.processing_event_id) OR\n (NEW.interaction_event_id IS NOT NULL AND\n type_of_interaction_event_id = NEW.interaction_event_id)\n );\n IF association_id IS NULL\n THEN\n RAISE EXCEPTION 'Wrong event type for status'\n USING HINT = 'This event type cannot be added to this lifecycle status.';\n END IF;\n RETURN NEW;\n END;\n $function$",
+ )
+ op.create_entity(hame_trgfunc_event_date_validate_type)
+
+ hame_trgfunc_event_date_validate_inside_status_date = PGFunction(
+ schema="hame",
+ signature="trgfunc_event_date_validate_inside_status_date()",
+ definition="returns trigger\n LANGUAGE plpgsql\nAS $function$\n DECLARE\n status_starting_at TIMESTAMP WITH TIME ZONE;\n status_ending_at TIMESTAMP WITH TIME ZONE;\n BEGIN\n SELECT starting_at, ending_at INTO status_starting_at, status_ending_at\n FROM hame.lifecycle_date\n WHERE NEW.lifecycle_date_id = hame.lifecycle_date.id;\n IF (\n -- Does the event start before status starts?\n (\n NEW.starting_at < status_starting_at\n ) OR\n -- Missing event ending date means event is instantaneous. Only\n -- events with ending date have a duration. Both must have ending\n -- date specified to check if event ends after status ends.\n (\n NEW.ending_at IS NOT NULL AND status_ending_at IS NOT NULL AND\n NEW.ending_at > status_ending_at\n )\n ) IS TRUE\n THEN\n RAISE EXCEPTION 'Event dates % - % outside status dates % - %',\n NEW.starting_at, NEW.ending_at, status_starting_at, status_ending_at\n USING HINT = 'Event cannot be outside lifecycle status dates.';\n END IF;\n RETURN NEW;\n END;\n $function$",
+ )
+ op.create_entity(hame_trgfunc_event_date_validate_inside_status_date)
+
+ hame_trgfunc_event_date_validate_dates = PGFunction(
+ schema="hame",
+ signature="trgfunc_event_date_validate_dates()",
+ definition="returns trigger\n LANGUAGE plpgsql\nAS $function$\n BEGIN\n IF (\n NEW.starting_at IS NOT NULL AND\n NEW.ending_at IS NOT NULL AND\n NEW.starting_at > NEW.ending_at\n ) IS TRUE\n THEN\n RAISE EXCEPTION 'Event starting date % after ending date %',\n NEW.starting_at, NEW.ending_at\n USING HINT = 'Event ending date must be after starting date.';\n END IF;\n RETURN NEW;\n END;\n $function$",
+ )
+ op.create_entity(hame_trgfunc_event_date_validate_dates)
+
+ hame_trgfunc_lifecycle_date_validate_dates = PGFunction(
+ schema="hame",
+ signature="trgfunc_lifecycle_date_validate_dates()",
+ definition="returns trigger\n LANGUAGE plpgsql\nAS $function$\n BEGIN\n IF (\n NEW.starting_at IS NOT NULL AND\n NEW.ending_at IS NOT NULL AND\n NEW.starting_at > NEW.ending_at\n ) IS TRUE\n THEN\n RAISE EXCEPTION 'Status starting date % after ending date %',\n NEW.starting_at, NEW.ending_at\n USING HINT = 'Status ending date must be after starting date.';\n END IF;\n RETURN NEW;\n END;\n $function$",
+ )
+ op.create_entity(hame_trgfunc_lifecycle_date_validate_dates)
+
+ hame_event_date_trg_event_date_001_no_created_at_update = PGTrigger(
+ schema="hame",
+ signature="trg_event_date_001_no_created_at_update",
+ on_entity="hame.event_date",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.event_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_no_created_at_update()",
+ )
+ op.create_entity(hame_event_date_trg_event_date_001_no_created_at_update)
+
+ hame_lifecycle_date_trg_lifecycle_date_001_no_created_at_update = PGTrigger(
+ schema="hame",
+ signature="trg_lifecycle_date_001_no_created_at_update",
+ on_entity="hame.lifecycle_date",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.lifecycle_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_no_created_at_update()",
+ )
+ op.create_entity(hame_lifecycle_date_trg_lifecycle_date_001_no_created_at_update)
+
+ hame_event_date_trg_event_date_created_at = PGTrigger(
+ schema="hame",
+ signature="trg_event_date_created_at",
+ on_entity="hame.event_date",
+ is_constraint=False,
+ definition="BEFORE INSERT ON hame.event_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_created_at()",
+ )
+ op.create_entity(hame_event_date_trg_event_date_created_at)
+
+ hame_lifecycle_date_trg_lifecycle_date_created_at = PGTrigger(
+ schema="hame",
+ signature="trg_lifecycle_date_created_at",
+ on_entity="hame.lifecycle_date",
+ is_constraint=False,
+ definition="BEFORE INSERT ON hame.lifecycle_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_created_at()",
+ )
+ op.create_entity(hame_lifecycle_date_trg_lifecycle_date_created_at)
+
+ hame_point_trg_point_new_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_point_new_lifecycle_date",
+ on_entity="hame.point",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.point FOR EACH ROW WHEN ((new.lifecycle_status_id <> old.lifecycle_status_id)) EXECUTE FUNCTION hame.trgfunc_new_lifecycle_date()",
+ )
+ op.create_entity(hame_point_trg_point_new_lifecycle_date)
+
+ hame_point_trg_new_point_add_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_new_point_add_lifecycle_date",
+ on_entity="hame.point",
+ is_constraint=False,
+ definition="AFTER INSERT ON hame.point FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_new_object_add_lifecycle_date()",
+ )
+ op.create_entity(hame_point_trg_new_point_add_lifecycle_date)
+
+ hame_plan_regulation_trg_plan_regulation_new_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_plan_regulation_new_lifecycle_date",
+ on_entity="hame.plan_regulation",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.plan_regulation FOR EACH ROW WHEN ((new.lifecycle_status_id <> old.lifecycle_status_id)) EXECUTE FUNCTION hame.trgfunc_new_lifecycle_date()",
+ )
+ op.create_entity(hame_plan_regulation_trg_plan_regulation_new_lifecycle_date)
+
+ hame_plan_proposition_trg_plan_proposition_new_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_plan_proposition_new_lifecycle_date",
+ on_entity="hame.plan_proposition",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.plan_proposition FOR EACH ROW WHEN ((new.lifecycle_status_id <> old.lifecycle_status_id)) EXECUTE FUNCTION hame.trgfunc_new_lifecycle_date()",
+ )
+ op.create_entity(hame_plan_proposition_trg_plan_proposition_new_lifecycle_date)
+
+ hame_plan_trg_plan_new_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_plan_new_lifecycle_date",
+ on_entity="hame.plan",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.plan FOR EACH ROW WHEN ((new.lifecycle_status_id <> old.lifecycle_status_id)) EXECUTE FUNCTION hame.trgfunc_new_lifecycle_date()",
+ )
+ op.create_entity(hame_plan_trg_plan_new_lifecycle_date)
+
+ hame_other_area_trg_other_area_new_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_other_area_new_lifecycle_date",
+ on_entity="hame.other_area",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.other_area FOR EACH ROW WHEN ((new.lifecycle_status_id <> old.lifecycle_status_id)) EXECUTE FUNCTION hame.trgfunc_new_lifecycle_date()",
+ )
+ op.create_entity(hame_other_area_trg_other_area_new_lifecycle_date)
+
+ hame_line_trg_line_new_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_line_new_lifecycle_date",
+ on_entity="hame.line",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.line FOR EACH ROW WHEN ((new.lifecycle_status_id <> old.lifecycle_status_id)) EXECUTE FUNCTION hame.trgfunc_new_lifecycle_date()",
+ )
+ op.create_entity(hame_line_trg_line_new_lifecycle_date)
+
+ hame_land_use_area_trg_land_use_area_new_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_land_use_area_new_lifecycle_date",
+ on_entity="hame.land_use_area",
+ is_constraint=False,
+ definition="BEFORE UPDATE ON hame.land_use_area FOR EACH ROW WHEN ((new.lifecycle_status_id <> old.lifecycle_status_id)) EXECUTE FUNCTION hame.trgfunc_new_lifecycle_date()",
+ )
+ op.create_entity(hame_land_use_area_trg_land_use_area_new_lifecycle_date)
+
+ hame_plan_regulation_trg_new_plan_regulation_add_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_new_plan_regulation_add_lifecycle_date",
+ on_entity="hame.plan_regulation",
+ is_constraint=False,
+ definition="AFTER INSERT ON hame.plan_regulation FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_new_object_add_lifecycle_date()",
+ )
+ op.create_entity(hame_plan_regulation_trg_new_plan_regulation_add_lifecycle_date)
+
+ hame_plan_proposition_trg_new_plan_proposition_add_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_new_plan_proposition_add_lifecycle_date",
+ on_entity="hame.plan_proposition",
+ is_constraint=False,
+ definition="AFTER INSERT ON hame.plan_proposition FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_new_object_add_lifecycle_date()",
+ )
+ op.create_entity(hame_plan_proposition_trg_new_plan_proposition_add_lifecycle_date)
+
+ hame_plan_trg_new_plan_add_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_new_plan_add_lifecycle_date",
+ on_entity="hame.plan",
+ is_constraint=False,
+ definition="AFTER INSERT ON hame.plan FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_new_object_add_lifecycle_date()",
+ )
+ op.create_entity(hame_plan_trg_new_plan_add_lifecycle_date)
+
+ hame_other_area_trg_new_other_area_add_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_new_other_area_add_lifecycle_date",
+ on_entity="hame.other_area",
+ is_constraint=False,
+ definition="AFTER INSERT ON hame.other_area FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_new_object_add_lifecycle_date()",
+ )
+ op.create_entity(hame_other_area_trg_new_other_area_add_lifecycle_date)
+
+ hame_line_trg_new_line_add_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_new_line_add_lifecycle_date",
+ on_entity="hame.line",
+ is_constraint=False,
+ definition="AFTER INSERT ON hame.line FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_new_object_add_lifecycle_date()",
+ )
+ op.create_entity(hame_line_trg_new_line_add_lifecycle_date)
+
+ hame_land_use_area_trg_new_land_use_area_add_lifecycle_date = PGTrigger(
+ schema="hame",
+ signature="trg_new_land_use_area_add_lifecycle_date",
+ on_entity="hame.land_use_area",
+ is_constraint=False,
+ definition="AFTER INSERT ON hame.land_use_area FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_new_object_add_lifecycle_date()",
+ )
+ op.create_entity(hame_land_use_area_trg_new_land_use_area_add_lifecycle_date)
+
+ hame_event_date_trg_event_date_validate_type = PGTrigger(
+ schema="hame",
+ signature="trg_event_date_validate_type",
+ on_entity="hame.event_date",
+ is_constraint=False,
+ definition="BEFORE INSERT OR UPDATE ON hame.event_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_event_date_validate_type()",
+ )
+ op.create_entity(hame_event_date_trg_event_date_validate_type)
+
+ hame_event_date_trg_event_date_validate_inside_status_date = PGTrigger(
+ schema="hame",
+ signature="trg_event_date_validate_inside_status_date",
+ on_entity="hame.event_date",
+ is_constraint=False,
+ definition="BEFORE INSERT OR UPDATE ON hame.event_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_event_date_validate_inside_status_date()",
+ )
+ op.create_entity(hame_event_date_trg_event_date_validate_inside_status_date)
+
+ hame_event_date_trg_event_date_validate_dates = PGTrigger(
+ schema="hame",
+ signature="trg_event_date_validate_dates",
+ on_entity="hame.event_date",
+ is_constraint=False,
+ definition="BEFORE INSERT OR UPDATE ON hame.event_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_event_date_validate_dates()",
+ )
+ op.create_entity(hame_event_date_trg_event_date_validate_dates)
+
+ hame_lifecycle_date_trg_lifecycle_date_validate_dates = PGTrigger(
+ schema="hame",
+ signature="trg_lifecycle_date_validate_dates",
+ on_entity="hame.lifecycle_date",
+ is_constraint=False,
+ definition="BEFORE INSERT OR UPDATE ON hame.lifecycle_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_lifecycle_date_validate_dates()",
+ )
+ op.create_entity(hame_lifecycle_date_trg_lifecycle_date_validate_dates)
+
+ hame_event_date_trg_event_date_modified_at = PGTrigger(
+ schema="hame",
+ signature="trg_event_date_modified_at",
+ on_entity="hame.event_date",
+ is_constraint=False,
+ definition="BEFORE INSERT OR UPDATE ON hame.event_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_modified_at()",
+ )
+ op.create_entity(hame_event_date_trg_event_date_modified_at)
+
+ hame_lifecycle_date_trg_lifecycle_date_modified_at = PGTrigger(
+ schema="hame",
+ signature="trg_lifecycle_date_modified_at",
+ on_entity="hame.lifecycle_date",
+ is_constraint=False,
+ definition="BEFORE INSERT OR UPDATE ON hame.lifecycle_date FOR EACH ROW EXECUTE FUNCTION hame.trgfunc_modified_at()",
+ )
+ op.create_entity(hame_lifecycle_date_trg_lifecycle_date_modified_at)
+
+ # ### end Alembic commands ###
diff --git a/test/conftest.py b/test/conftest.py
index e955628..3b20b01 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -1484,139 +1484,6 @@ def plan_map_instance(
return temp_session_feature(instance)
-# Date fixtures
-
-
-@pytest.fixture
-def lifecycle_date_instance(
- temp_session_feature: ReturnSame[models.LifeCycleDate],
- code_instance: codes.LifeCycleStatus,
-) -> models.LifeCycleDate:
- instance = models.LifeCycleDate(
- lifecycle_status=code_instance,
- starting_at=datetime(2024, 1, 1, tzinfo=LOCAL_TZ),
- ending_at=datetime(2025, 1, 1, tzinfo=LOCAL_TZ),
- )
- return temp_session_feature(instance)
-
-
-@pytest.fixture
-def pending_date_instance(
- temp_session_feature: ReturnSame[models.LifeCycleDate],
- plan_instance: codes.Plan,
- pending_status_instance: codes.LifeCycleStatus,
-) -> models.LifeCycleDate:
- instance = models.LifeCycleDate(
- plan=plan_instance,
- lifecycle_status=pending_status_instance,
- starting_at=datetime(2024, 1, 1, tzinfo=LOCAL_TZ),
- ending_at=datetime(2024, 2, 1, tzinfo=LOCAL_TZ),
- )
- return temp_session_feature(instance)
-
-
-@pytest.fixture
-def preparation_date_instance(
- temp_session_feature: ReturnSame[models.LifeCycleDate],
- plan_instance: codes.Plan,
- preparation_status_instance: codes.LifeCycleStatus,
-) -> models.LifeCycleDate:
- instance = models.LifeCycleDate(
- plan=plan_instance,
- lifecycle_status=preparation_status_instance,
- starting_at=datetime(2024, 2, 1, tzinfo=LOCAL_TZ),
- ending_at=datetime(2024, 3, 1, tzinfo=LOCAL_TZ),
- )
- return temp_session_feature(instance)
-
-
-@pytest.fixture
-def plan_proposal_date_instance(
- temp_session_feature: ReturnSame[models.LifeCycleDate],
- plan_instance: codes.Plan,
- plan_proposal_status_instance: codes.LifeCycleStatus,
-) -> models.LifeCycleDate:
- instance = models.LifeCycleDate(
- plan=plan_instance,
- lifecycle_status=plan_proposal_status_instance,
- starting_at=datetime(2024, 4, 1, tzinfo=LOCAL_TZ),
- ending_at=datetime(2024, 5, 1, tzinfo=LOCAL_TZ),
- )
- return temp_session_feature(instance)
-
-
-@pytest.fixture
-def approved_date_instance(
- temp_session_feature: ReturnSame[models.LifeCycleDate],
- plan_instance: codes.Plan,
- approved_status_instance: codes.LifeCycleStatus,
-) -> models.LifeCycleDate:
- instance = models.LifeCycleDate(
- plan=plan_instance,
- lifecycle_status=approved_status_instance,
- starting_at=datetime(2024, 4, 1, tzinfo=LOCAL_TZ),
- ending_at=datetime(2024, 5, 1, tzinfo=LOCAL_TZ),
- )
- return temp_session_feature(instance)
-
-
-@pytest.fixture
-def valid_date_instance(
- temp_session_feature: ReturnSame[models.LifeCycleDate],
- plan_instance: codes.Plan,
- valid_status_instance: codes.LifeCycleStatus,
-) -> models.LifeCycleDate:
- instance = models.LifeCycleDate(
- plan=plan_instance,
- lifecycle_status=valid_status_instance,
- starting_at=datetime(2024, 5, 1, tzinfo=LOCAL_TZ),
- )
- return temp_session_feature(instance)
-
-
-@pytest.fixture
-def decision_date_instance(
- temp_session_feature: ReturnSame[models.EventDate],
- preparation_date_instance: models.LifeCycleDate,
- participation_plan_presenting_for_public_decision: codes.NameOfPlanCaseDecision,
-) -> models.EventDate:
- instance = models.EventDate(
- lifecycle_date=preparation_date_instance,
- decision=participation_plan_presenting_for_public_decision,
- starting_at=datetime(2024, 2, 5, tzinfo=LOCAL_TZ),
- )
- return temp_session_feature(instance)
-
-
-@pytest.fixture
-def processing_event_date_instance(
- temp_session_feature: ReturnSame[models.EventDate],
- preparation_date_instance: models.LifeCycleDate,
- participation_plan_presenting_for_public_event: codes.TypeOfProcessingEvent,
-) -> models.EventDate:
- instance = models.EventDate(
- lifecycle_date=preparation_date_instance,
- processing_event=participation_plan_presenting_for_public_event,
- starting_at=datetime(2024, 2, 15, tzinfo=LOCAL_TZ),
- )
- return temp_session_feature(instance)
-
-
-@pytest.fixture
-def interaction_event_date_instance(
- temp_session_feature: ReturnSame[models.EventDate],
- preparation_date_instance: models.LifeCycleDate,
- presentation_to_the_public_interaction: codes.TypeOfInteractionEvent,
-) -> models.EventDate:
- instance = models.EventDate(
- lifecycle_date=preparation_date_instance,
- interaction_event=presentation_to_the_public_interaction,
- starting_at=datetime(2024, 2, 15, tzinfo=LOCAL_TZ),
- ending_at=datetime(2024, 2, 28, tzinfo=LOCAL_TZ),
- )
- return temp_session_feature(instance)
-
-
# Additional information fixtures
@@ -1717,11 +1584,6 @@ def complete_test_plan(
plan_proposal_requesting_for_opinions_event: codes.TypeOfProcessingEvent,
presentation_to_the_public_interaction: codes.TypeOfInteractionEvent,
decisionmaker_type: codes.TypeOfDecisionMaker,
- pending_date_instance: models.LifeCycleDate,
- preparation_date_instance: models.LifeCycleDate,
- decision_date_instance: models.EventDate,
- processing_event_date_instance: models.EventDate,
- interaction_event_date_instance: models.EventDate,
) -> models.Plan:
"""Plan data that might be more or less complete, to be tested and validated with the
Ryhti API.
@@ -1730,11 +1592,6 @@ def complete_test_plan(
linked to the plan in the database) to be committed to the database, and some
dates for the plan lifecycle statuses to be set.
"""
- # In tests, we need known dates for the phases. Plan has a trigger-generated additional
- # date for the preparation phase that we must delete before testing.
- session.delete(plan_instance.lifecycle_dates[2])
- session.commit()
-
# Add the optional (nullable) relationships. We don't want them to be present in
# all fixtures.
plan_instance.legal_effects_of_master_plan.append(
diff --git a/test/ryhti_client/test_copy_plan.py b/test/ryhti_client/test_copy_plan.py
index 8843a03..159afd0 100644
--- a/test/ryhti_client/test_copy_plan.py
+++ b/test/ryhti_client/test_copy_plan.py
@@ -129,9 +129,7 @@ def test_copy_plan(
assert copied_land_use_area_1 is not None
assert copied_land_use_area_1.lifecycle_status.value == valid_status_instance.value
assert copied_land_use_area_1.period_of_validity_start == period_of_validity_start
- assert len(land_use_area_1.lifecycle_dates) == len(
- copied_land_use_area_1.lifecycle_dates
- )
+
assert (
land_use_area_1.type_of_underground
== copied_land_use_area_1.type_of_underground
@@ -150,7 +148,7 @@ def test_copy_plan(
)
assert copied_other_area_1 is not None
assert copied_other_area_1.lifecycle_status.value == valid_status_instance.value
- assert len(other_area_1.lifecycle_dates) == len(copied_other_area_1.lifecycle_dates)
+
assert other_area_1.type_of_underground == copied_other_area_1.type_of_underground
# complete_test_plan fixture has no lines
@@ -164,7 +162,7 @@ def test_copy_plan(
)
assert copied_point_1 is not None
assert copied_point_1.lifecycle_status.value == valid_status_instance.value
- assert len(point_1.lifecycle_dates) == len(copied_point_1.lifecycle_dates)
+
assert point_1.type_of_underground == copied_point_1.type_of_underground
# General regulation groups
diff --git a/test/test_models.py b/test/test_models.py
index a38cc10..985dafb 100644
--- a/test/test_models.py
+++ b/test/test_models.py
@@ -334,123 +334,6 @@ def test_document(
session.flush()
-def test_lifecycle_date(
- session: Session,
- lifecycle_date_instance: models.LifeCycleDate,
- code_instance: codes.LifeCycleStatus,
- plan_instance: models.Plan,
- text_plan_regulation_instance: models.PlanRegulation,
- plan_proposition_instance: models.PlanProposition,
-) -> None:
- # non-nullable lifecycle date relations
- assert lifecycle_date_instance.lifecycle_status is code_instance
- assert code_instance.lifecycle_dates == [lifecycle_date_instance]
- # nullable lifecycle date relations
- assert lifecycle_date_instance.plan is None
- assert lifecycle_date_instance not in plan_instance.lifecycle_dates
- lifecycle_date_instance.plan = plan_instance
- assert lifecycle_date_instance.plan_regulation is None
- assert lifecycle_date_instance not in text_plan_regulation_instance.lifecycle_dates
- lifecycle_date_instance.plan_regulation = text_plan_regulation_instance
- assert lifecycle_date_instance.plan_proposition is None
- assert lifecycle_date_instance not in plan_proposition_instance.lifecycle_dates
- lifecycle_date_instance.plan_proposition = plan_proposition_instance
- session.flush()
-
- assert lifecycle_date_instance.plan is plan_instance
- assert lifecycle_date_instance.plan_regulation is text_plan_regulation_instance
- assert lifecycle_date_instance.plan_proposition is plan_proposition_instance
- assert lifecycle_date_instance in plan_instance.lifecycle_dates
- assert lifecycle_date_instance in text_plan_regulation_instance.lifecycle_dates
- assert lifecycle_date_instance in plan_proposition_instance.lifecycle_dates
-
-
-def test_decision_date(
- session: Session,
- preparation_date_instance: models.LifeCycleDate,
- decision_date_instance: models.EventDate,
- participation_plan_presenting_for_public_decision: codes.NameOfPlanCaseDecision,
-) -> None:
- # non-nullable decision date relations
- assert decision_date_instance.lifecycle_date is preparation_date_instance
- assert preparation_date_instance.event_dates == [decision_date_instance]
-
- # nullable decision date relations
- assert (
- decision_date_instance.decision
- == participation_plan_presenting_for_public_decision
- )
- assert participation_plan_presenting_for_public_decision.event_dates == [
- decision_date_instance
- ]
-
-
-def test_processing_event_date(
- session: Session,
- preparation_date_instance: models.LifeCycleDate,
- processing_event_date_instance: models.EventDate,
- participation_plan_presenting_for_public_event: codes.TypeOfProcessingEvent,
-) -> None:
- # non-nullable decision date relations
- assert processing_event_date_instance.lifecycle_date is preparation_date_instance
- assert preparation_date_instance.event_dates == [processing_event_date_instance]
-
- # nullable decision date relations
- assert (
- processing_event_date_instance.processing_event
- == participation_plan_presenting_for_public_event
- )
- assert participation_plan_presenting_for_public_event.event_dates == [
- processing_event_date_instance
- ]
-
-
-def test_interaction_event_date(
- session: Session,
- preparation_date_instance: models.LifeCycleDate,
- interaction_event_date_instance: models.EventDate,
- presentation_to_the_public_interaction: codes.TypeOfProcessingEvent,
-) -> None:
- # non-nullable decision date relations
- assert interaction_event_date_instance.lifecycle_date is preparation_date_instance
- assert preparation_date_instance.event_dates == [interaction_event_date_instance]
-
- # nullable decision date relations
- assert (
- interaction_event_date_instance.interaction_event
- == presentation_to_the_public_interaction
- )
- assert presentation_to_the_public_interaction.event_dates == [
- interaction_event_date_instance
- ]
-
-
-@pytest.mark.parametrize(
- "fixture_name",
- [
- pytest.param("land_use_area_instance", id="land_use_area"),
- pytest.param("line_instance", id="line"),
- pytest.param("point_instance", id="point"),
- pytest.param("other_area_instance", id="other_area"),
- pytest.param("plan_proposition_instance", id="plan_proposition"),
- pytest.param("empty_value_plan_regulation_instance", id="plan_regulation"),
- ],
-)
-def test_cascade_delete_of_lifecycle_dates_using_orm(
- session: Session, request: pytest.FixtureRequest, fixture_name: str
-) -> None:
- """Test that deleting a plan object cascades to the lifecycle date when using ORM.
- This makes sure that the cascade options are configured correctly in the SqlAlchemy models.
- """
- plan_base_object = request.getfixturevalue(fixture_name)
- assert len(plan_base_object.lifecycle_dates) == 1
- lifecycle_date = plan_base_object.lifecycle_dates[0]
- session.delete(plan_base_object)
- session.commit()
-
- assert inspect(lifecycle_date).was_deleted
-
-
@pytest.mark.parametrize(
("parent_fixture_name", "child_fixture_name", "child_collection_name"),
[
@@ -495,59 +378,6 @@ def test_cascade_delete_using_orm(
assert inspect(child_object).was_deleted
-@pytest.mark.parametrize(
- "fixture_name",
- [
- pytest.param("land_use_area_instance", id="land_use_area"),
- pytest.param("line_instance", id="line"),
- pytest.param("point_instance", id="point"),
- pytest.param("other_area_instance", id="other_area"),
- pytest.param("plan_proposition_instance", id="plan_proposition"),
- pytest.param("empty_value_plan_regulation_instance", id="plan_regulation"),
- ],
-)
-def test_cascade_delete_of_lifecycle_dates_using_db(
- session: Session, request: pytest.FixtureRequest, fixture_name: str
-) -> None:
- """Test that deleting a plan object cascades to the lifecycle date when using raw SQL.
- This makes sure that the ON DELETE cascade options are configured correctly for the foreign keys in the database.
- """
- plan_base_object = request.getfixturevalue(fixture_name)
- lifecycle_object = plan_base_object.lifecycle_dates[0]
-
- connection = session.connection().connection
- cur = connection.cursor()
-
- def lifecycle_date_exists() -> bool:
- cur.execute(
- sql.SQL("SELECT EXISTS (SELECT id FROM {table} WHERE id=%s)").format(
- table=sql.Identifier(
- lifecycle_object.__table__.schema, lifecycle_object.__table__.name
- )
- ),
- (lifecycle_object.id,),
- )
- row = cur.fetchone()
- return bool(row[0]) if row else False
-
- def delete_plan_base_object() -> None:
- cur.execute(
- sql.SQL("DELETE FROM {table} WHERE id=%s").format(
- table=sql.Identifier(
- plan_base_object.__table__.schema, plan_base_object.__table__.name
- )
- ),
- (plan_base_object.id,),
- )
-
- assert lifecycle_date_exists()
- delete_plan_base_object()
- assert not lifecycle_date_exists()
-
- cur.close()
- connection.rollback()
-
-
@pytest.mark.parametrize(
("parent_fixture_name", "child_fixture_name"),
[
diff --git a/test/test_ryhti_client.py b/test/test_ryhti_client.py
index 221ed47..c57f398 100644
--- a/test/test_ryhti_client.py
+++ b/test/test_ryhti_client.py
@@ -470,7 +470,6 @@ def client_with_plan_data_in_proposal_phase(
dba_connection_string: str,
complete_test_plan: models.Plan,
plan_proposal_status_instance: codes.LifeCycleStatus,
- plan_proposal_date_instance: models.LifeCycleDate,
) -> RyhtiClient:
"""Return RyhtiClient that has plan data in proposal phase read in.
@@ -483,11 +482,6 @@ def client_with_plan_data_in_proposal_phase(
session.add(plan_proposal_status_instance)
complete_test_plan.lifecycle_status = plan_proposal_status_instance
session.commit()
- # Delete the new additional date for proposal phase that just appeared. Our fixture already
- # has a proposal date.
- session.refresh(complete_test_plan)
- session.delete(complete_test_plan.lifecycle_dates[2])
- session.commit()
# Let's mock production x-road with gispo organization client here.
database_client = DatabaseClient(dba_connection_string)
@@ -837,178 +831,3 @@ def test_upload_unchanged_plan_documents(
assert plan_instance.documents[0].exported_at == old_exported_at
assert plan_instance.documents[0].exported_file_key == old_file_key
assert plan_instance.documents[0].exported_file_etag == old_file_etag
-
-
-def test_get_plan_matters(
- client_with_plan_with_permanent_identifier_and_documents: RyhtiClient,
- plan_instance: models.Plan,
- desired_plan_matter_dict: dict,
-) -> None:
- """Check that correct JSON structure is generated for plan matter. This requires that
- the client has already fetched a permanent identifer for the plan.
- """
- plan_matter_dictionaries = client_with_plan_with_permanent_identifier_and_documents.database_client.get_plan_matters()
- plan_matter = plan_matter_dictionaries[plan_instance.id]
- deepcompare(
- plan_matter,
- desired_plan_matter_dict,
- ignore_keys=[
- "planMatterPhaseKey",
- "handlingEventKey",
- "interactionEventKey",
- "planDecisionKey",
- "planMapKey",
- "attachmentDocumentKey",
- "planReportKey",
- "otherPlanMaterialKey",
- "fileKey",
- ],
- ignore_order_for_keys=[
- "planRegulationGroupRelations",
- "additionalInformations",
- ],
- )
-
-
-def test_validate_plan_matters(
- client_with_plan_with_permanent_identifier_and_documents: RyhtiClient,
- plan_instance: models.Plan,
- mock_xroad_ryhti_validate_invalid: Callable,
-) -> None:
- """Check that JSON is posted and response received"""
- responses = (
- client_with_plan_with_permanent_identifier_and_documents.validate_plan_matters()
- )
- for plan_id, response in responses.items():
- assert plan_id == plan_instance.id
- assert response["errors"] == [
- {
- "ruleId": mock_matter_rule,
- "message": mock_matter_error_string,
- "instance": mock_matter_instance,
- }
- ]
-
-
-def test_save_plan_matter_validation_responses(
- session: Session,
- client_with_plan_with_permanent_identifier_and_documents: RyhtiClient,
- plan_instance: models.Plan,
- mock_xroad_ryhti_validate_invalid: Callable,
-) -> None:
- """Check that Ryhti X-Road validation error is saved to database."""
- responses = (
- client_with_plan_with_permanent_identifier_and_documents.validate_plan_matters()
- )
- message = client_with_plan_with_permanent_identifier_and_documents.database_client.save_plan_matter_validation_responses(
- responses
- )
- session.refresh(plan_instance)
- assert plan_instance.validated_at
- assert plan_instance.validation_errors == next(iter(responses.values()))["errors"]
-
-
-def test_post_new_plan_matters(
- client_with_plan_with_permanent_identifier_and_documents: RyhtiClient,
- plan_instance: models.Plan,
- mock_xroad_ryhti_post_new_plan_matter: Callable,
-) -> None:
- """Check that JSON is posted and response received when the plan matter does not
- exist in Ryhti yet.
- """
- responses = (
- client_with_plan_with_permanent_identifier_and_documents.post_plan_matters()
- )
- for plan_id, response in responses.items():
- assert plan_id == plan_instance.id
- assert response["warnings"]
- assert not response["errors"]
-
-
-def test_save_new_plan_matter_post_responses(
- session: Session,
- client_with_plan_with_permanent_identifier_and_documents: RyhtiClient,
- plan_instance: models.Plan,
- mock_xroad_ryhti_post_new_plan_matter: Callable,
-) -> None:
- """Check that export time is saved to database."""
- responses = (
- client_with_plan_with_permanent_identifier_and_documents.post_plan_matters()
- )
- message = client_with_plan_with_permanent_identifier_and_documents.database_client.save_plan_matter_post_responses(
- responses
- )
- session.refresh(plan_instance)
- assert plan_instance.exported_at
- assert plan_instance.validation_errors == "Uusi kaava-asian vaihe on viety Ryhtiin."
-
-
-def test_update_existing_plan_matters(
- session: Session,
- client_with_plan_with_permanent_identifier_and_documents_in_proposal_phase: RyhtiClient,
- plan_instance: models.Plan,
- mock_xroad_ryhti_update_existing_plan_matter: Callable,
-) -> None:
- """Check that JSON is posted and response received when the plan matter exists in Ryhti
- and a new plan matter phase must be posted.
- """
- responses = client_with_plan_with_permanent_identifier_and_documents_in_proposal_phase.post_plan_matters()
- for plan_id, response in responses.items():
- assert plan_id == plan_instance.id
- assert response["warnings"]
- assert not response["errors"]
-
-
-def test_save_update_existing_matter_post_responses(
- session: Session,
- client_with_plan_with_permanent_identifier_and_documents_in_proposal_phase: RyhtiClient,
- plan_instance: models.Plan,
- mock_xroad_ryhti_update_existing_plan_matter: Callable,
-) -> None:
- """Check that export time is saved to database."""
- responses = client_with_plan_with_permanent_identifier_and_documents_in_proposal_phase.post_plan_matters()
- message = client_with_plan_with_permanent_identifier_and_documents_in_proposal_phase.database_client.save_plan_matter_post_responses(
- responses
- )
- session.refresh(plan_instance)
- assert plan_instance.exported_at
- assert plan_instance.validation_errors == "Uusi kaava-asian vaihe on viety Ryhtiin."
-
-
-def test_update_existing_plan_matter_phase(
- session: Session,
- client_with_plan_with_permanent_identifier_and_documents: RyhtiClient,
- plan_instance: models.Plan,
- mock_xroad_ryhti_update_existing_plan_matter: Callable,
-) -> None:
- """Check that JSON is posted and response received when the plan matter and the plan matter
- phase exist in Ryhti and the plan matter phase must be updated.
- """
- responses = (
- client_with_plan_with_permanent_identifier_and_documents.post_plan_matters()
- )
- for plan_id, response in responses.items():
- assert plan_id == plan_instance.id
- assert response["warnings"]
- assert not response["errors"]
-
-
-def test_save_update_existing_matter_phase_post_responses(
- session: Session,
- client_with_plan_with_permanent_identifier_and_documents: RyhtiClient,
- plan_instance: models.Plan,
- mock_xroad_ryhti_update_existing_plan_matter: Callable,
-) -> None:
- """Check that export time is saved to database."""
- responses = (
- client_with_plan_with_permanent_identifier_and_documents.post_plan_matters()
- )
- message = client_with_plan_with_permanent_identifier_and_documents.database_client.save_plan_matter_post_responses(
- responses
- )
- session.refresh(plan_instance)
- assert plan_instance.exported_at
- # TODO: switch to using correct message once Ryhti responds correctly. Currently, Ryhti
- # claims a new phase is created every time the existing phase is updated.
- # assert plan_instance.validation_errors == "Kaava-asian vaihe on päivitetty Ryhtiin."
- assert plan_instance.validation_errors == "Uusi kaava-asian vaihe on viety Ryhtiin."
diff --git a/test/test_services.py b/test/test_services.py
index ccf8f65..4cf1c6b 100644
--- a/test/test_services.py
+++ b/test/test_services.py
@@ -503,393 +503,6 @@ def test_get_permanent_plan_identifier(
conn.close()
-@pytest.fixture
-def get_single_plan_matter(
- ryhti_client_url: str,
- complete_test_plan: Plan,
- another_test_plan: Plan,
- desired_plan_matter_dict: dict,
-) -> None:
- """Get single plan matter JSON from lambda by id. Another plan in the database should
- not be serialized.
-
- Getting plan matter should make lambda return http 200 OK (to indicate that
- serialization has been run successfully), with the ryhti_responses dict empty, and
- details dict containing the serialized plan matter.
-
- Formatting plan matter requires that a plan has a permanent identifier.
- """
- payload = {
- "action": "get_permanent_plan_identifiers",
- "plan_uuid": complete_test_plan.id,
- "save_json": True,
- }
- r = requests.post(ryhti_client_url, data=json.dumps(payload))
- # now the plan has permanent identifier, we can proceed:
- payload = {
- "action": "get_plan_matters",
- "plan_uuid": complete_test_plan.id,
- "save_json": True,
- }
- r = requests.post(ryhti_client_url, data=json.dumps(payload))
- data = r.json()
- print(data)
- assert data["statusCode"] == 200
- body = data["body"]
- assert body["title"] == "Returning serialized plan matters from database."
- # Check that other plan matter is NOT returned
- assert len(body["details"]) == 1
- deepcompare(
- body["details"][complete_test_plan.id],
- desired_plan_matter_dict,
- ignore_keys=[
- "planMatterPhaseKey",
- "handlingEventKey",
- "interactionEventKey",
- "planDecisionKey",
- "planMaps", # currently, we do not upload documents when getting plan matter json
- "planAnnexes", # currently, we do not upload documents when getting plan matter json
- "planReport", # currently, we do not upload documents when getting plan matter json
- "otherPlanMaterials", # currently, we do not upload documents when getting plan matter json
- "fileKey",
- ],
- ignore_order_for_keys=[
- "planRegulationGroupRelations",
- "additionalInformations",
- ],
- )
- assert not body["ryhti_responses"]
-
-
-def test_get_single_plan_matter(
- get_single_plan_matter: None, main_db_params: ConnectionParameters
-) -> None:
- """Test the whole lambda endpoint with single_plan_matter"""
- # getting plan JSON from lambda should not run validations
- conn = psycopg.connect(**main_db_params)
- try:
- with conn.cursor() as cur:
- # Check that plans are NOT validated
- cur.execute("SELECT validated_at, validation_errors FROM hame.plan")
- row = cur.fetchone()
- assert row is not None
- validation_date, errors = row
- assert not validation_date
- assert not errors
-
- row = cur.fetchone()
- assert row is not None
- validation_date, errors = row
- assert not validation_date
- assert not errors
- finally:
- conn.close()
-
-
-@pytest.fixture
-def validate_valid_plan_matter_in_preparation(
- ryhti_client_url: str, complete_test_plan: Plan
-) -> None:
- """Validate a valid Ryhti plan and plan matter against the Ryhti API. This guarantees
- that the Ryhti plan is formed according to spec and passes open Ryhti API validation.
-
- If the plan has a permanent plan identifier, the client proceeds to also validate
- the plan matter.
-
- Since local tests or CI/CD cannot connect to X-Road servers, we validate the plan
- *matter* against a Mock X-Road API that responds with 200 OK. Therefore, for the
- X-Road APIs, this only guarantees that the lambda runs correctly, not that the plan
- *matter* is formed according to spec.
-
- A valid plan should make lambda return http 200 OK (to indicate that the validation
- has been run successfully), with the validation errors list empty and validation
- warnings returned.
- """
- payload = {
- "action": "get_permanent_plan_identifiers",
- "plan_uuid": complete_test_plan.id,
- "save_json": True,
- }
- r = requests.post(ryhti_client_url, data=json.dumps(payload))
- # now the plan has permanent identifier, we can proceed:
- payload = {
- "action": "validate_plan_matters",
- "plan_uuid": complete_test_plan.id,
- "save_json": True,
- }
- r = requests.post(ryhti_client_url, data=json.dumps(payload))
- data = r.json()
- print(data)
- assert data["statusCode"] == 200
- body = data["body"]
- assert body["title"] == "Plan matter validations run."
- assert (
- body["details"][complete_test_plan.id]
- == f"Plan matter validation successful for {complete_test_plan.id}!"
- )
- assert body["ryhti_responses"][complete_test_plan.id]["status"] == 200
- assert body["ryhti_responses"][complete_test_plan.id]["warnings"]
- assert not body["ryhti_responses"][complete_test_plan.id]["errors"]
-
-
-def test_validate_valid_plan_matter_in_preparation(
- validate_valid_plan_matter_in_preparation: None,
- main_db_params: ConnectionParameters,
-) -> None:
- """Test the whole lambda endpoint with a valid plan and plan matter in preparation
- stage. Plan is validated with public Ryhti API. Validate plan matter with mock
- X-Road API.
-
- The mock X-Road should return a permanent identifier and report the plan matter
- as valid. Also, validating plan matter should make sure that plan documents
- are included in the plan matter.
- """
- conn = psycopg.connect(**main_db_params)
- try:
- with conn.cursor() as cur:
- cur.execute(
- "SELECT p.id, validated_at, validation_errors, pm.permanent_plan_identifier "
- "FROM hame.plan p JOIN hame.plan_matter pm "
- "ON p.plan_matter_id = pm.id"
- )
- row = cur.fetchone()
- assert row is not None
- exported_plan_id, validation_date, errors, permanent_plan_identifier = row
- assert validation_date
- assert errors == "Kaava-asia on validi ja sen voi viedä Ryhtiin."
- assert permanent_plan_identifier == "MK-123456"
-
- # Document should be exported
- cur.execute(
- "SELECT plan_id, exported_at, exported_file_key FROM hame.document"
- )
- row = cur.fetchone()
- assert row is not None
- plan_id, exported_at, exported_file_key = row
- assert plan_id == exported_plan_id
- assert exported_at
- assert exported_file_key
- finally:
- conn.close()
-
-
-@pytest.fixture
-def post_plan_matters_in_preparation(
- ryhti_client_url: str, complete_test_plan: Plan, another_test_plan: Plan
-) -> None:
- """POST all plans to the mock X-Road API. Plans need not be validated, but they need
- to have permanent identifiers set.
-
- POSTing plans should make lambda return http 200 OK (to indicate that POSTs
- have been run successfully), with the validation errors list empty and validation
- warnings returned (if plan was valid) or validation errors (if plan was invalid).
- """
- payload = {
- "action": "get_permanent_plan_identifiers",
- "plan_uuid": complete_test_plan.id,
- "save_json": True,
- }
- r = requests.post(ryhti_client_url, data=json.dumps(payload))
- # now one plan has a permanent identifier, the other does not:
- payload = {"action": "post_plan_matters", "save_json": True}
- r = requests.post(ryhti_client_url, data=json.dumps(payload))
- data = r.json()
- print(data)
- assert data["statusCode"] == 200
- body = data["body"]
- assert body["title"] == "Plan matters POSTed."
- assert (
- body["details"][complete_test_plan.id]
- == f"Plan matter or plan matter phase POST successful for {complete_test_plan.id}."
- )
- assert (
- body["details"][another_test_plan.id]
- == f"Plan {another_test_plan.id} had no permanent identifier. Could not create plan matter!"
- )
- # Valid plan was posted
- assert body["ryhti_responses"][complete_test_plan.id]["status"] == 201
- assert body["ryhti_responses"][complete_test_plan.id]["warnings"]
- assert not body["ryhti_responses"][complete_test_plan.id]["errors"]
- # Another plan had no identifier and has no ryhti response
- assert another_test_plan.id not in body["ryhti_responses"]
-
-
-def test_post_plan_matters_in_preparation(
- post_plan_matters_in_preparation: None, main_db_params: ConnectionParameters
-) -> None:
- """Test the whole lambda endpoint with multiple plans and plan matters in preparation
- stage. POST plan matters with mock X-Road API.
-
- The mock X-Road should accept POSTed plan matter and report the plan matter as being
- created in Ryhti. The plan matter without identifier should not be exported. Also,
- POSTing plan matter should make sure that plan documents are included in the plan matter.
- """
- conn = psycopg.connect(**main_db_params)
- try:
- with conn.cursor() as cur:
- cur.execute(
- """
- SELECT
- p.id,
- validated_at,
- validation_errors,
- pm.permanent_plan_identifier,
- exported_at
- FROM hame.plan p JOIN hame.plan_matter pm
- ON p.plan_matter_id = pm.id
- ORDER BY p.modified_at DESC
- """
- )
- # Exported plan should also be reported validated
- row = cur.fetchone()
- assert row is not None
- (
- exported_plan_id,
- validation_date,
- errors,
- permanent_plan_identifier,
- exported_at,
- ) = row
- assert validation_date
- assert errors == "Uusi kaava-asian vaihe on viety Ryhtiin."
- assert permanent_plan_identifier == "MK-123456"
- assert exported_at
- # Check that other plan is NOT modified because it had no identifier
- row = cur.fetchone()
- assert row is not None
- (
- _other_plan_id,
- validation_date,
- errors,
- permanent_plan_identifier,
- exported_at,
- ) = row
- assert not validation_date
- assert not errors
- assert not permanent_plan_identifier
- assert not exported_at
- # Document should be exported
- cur.execute(
- "SELECT plan_id, exported_at, exported_file_key FROM hame.document"
- )
- row = cur.fetchone()
- assert row is not None
- plan_id, exported_at, exported_file_key = row
- assert plan_id == exported_plan_id
- assert exported_at
- assert exported_file_key
- finally:
- conn.close()
-
-
-@pytest.fixture
-def post_valid_plan_matter_in_preparation(
- ryhti_client_url: str, complete_test_plan: Plan, another_test_plan
-) -> None:
- """POST single valid plan to the mock X-Road API. Plan needs not be validated.
-
- A POSTed plan should make lambda return http 200 OK (to indicate that the POST
- has been run successfully), with the validation errors list empty and validation
- warnings returned.
- """
- payload = {
- "action": "get_permanent_plan_identifiers",
- "plan_uuid": complete_test_plan.id,
- "save_json": True,
- }
- r = requests.post(ryhti_client_url, data=json.dumps(payload))
- # now the plan has permanent identifier, we can proceed:
- payload = {
- "action": "post_plan_matters",
- "plan_uuid": complete_test_plan.id,
- "save_json": True,
- }
- r = requests.post(ryhti_client_url, data=json.dumps(payload))
- data = r.json()
- print(data)
- assert data["statusCode"] == 200
- body = data["body"]
- assert body["title"] == "Plan matters POSTed."
- # Check that other plan is NOT processed
- assert len(body["details"]) == 1
- assert (
- body["details"][complete_test_plan.id]
- == f"Plan matter or plan matter phase POST successful for {complete_test_plan.id}."
- )
- assert len(body["ryhti_responses"]) == 1
- assert body["ryhti_responses"][complete_test_plan.id]["status"] == 201
- assert body["ryhti_responses"][complete_test_plan.id]["warnings"]
- assert not body["ryhti_responses"][complete_test_plan.id]["errors"]
-
-
-def test_post_valid_plan_matter_in_preparation(
- post_valid_plan_matter_in_preparation: None, main_db_params: ConnectionParameters
-) -> None:
- """Test the whole lambda endpoint with a valid plan and plan matter in preparation
- stage. POST plan matter with mock X-Road API.
-
- The mock X-Road should accept POSTed plan matter and report the plan matter as
- being created in Ryhti. Also, POSTing plan matter should make sure that plan
- documents are included in the plan matter.
- """
- conn = psycopg.connect(**main_db_params)
- try:
- with conn.cursor() as cur:
- cur.execute(
- """
- SELECT
- p.id,
- validated_at,
- validation_errors,
- pm.permanent_plan_identifier,
- exported_at
- FROM hame.plan p JOIN hame.plan_matter pm
- ON p.plan_matter_id = pm.id
- ORDER BY p.modified_at DESC
- """
- )
- # Exported plan should also be reported validated
- row = cur.fetchone()
- assert row is not None
- (
- exported_plan_id,
- validation_date,
- errors,
- permanent_plan_identifier,
- exported_at,
- ) = row
- assert validation_date
- assert errors == "Uusi kaava-asian vaihe on viety Ryhtiin."
- assert permanent_plan_identifier == "MK-123456"
- assert exported_at
- # Check that other plan is NOT modified
- row = cur.fetchone()
- assert row is not None
- (
- _other_plan_id,
- validation_date,
- errors,
- permanent_plan_identifier,
- exported_at,
- ) = row
- assert not validation_date
- assert not errors
- assert not permanent_plan_identifier
- assert not exported_at
- # Document should be exported
- cur.execute(
- "SELECT plan_id, exported_at, exported_file_key FROM hame.document"
- )
- row = cur.fetchone()
- assert row is not None
- plan_id, exported_at, exported_file_key = row
- assert plan_id == exported_plan_id
- assert exported_at
- assert exported_file_key
- finally:
- conn.close()
-
-
@pytest.fixture
def extra_data(plan_matter_instance: PlanMatter) -> dict:
return {"name": "test_plan", "plan_matter_id": plan_matter_instance.id}
diff --git a/test/test_triggers.py b/test/test_triggers.py
index 22aa70f..07d494e 100644
--- a/test/test_triggers.py
+++ b/test/test_triggers.py
@@ -92,7 +92,6 @@ def test_modified_at_triggers(
source_data_instance: models.SourceData,
organisation_instance: models.Organisation,
plan_map_instance: models.Document,
- lifecycle_date_instance: models.LifeCycleDate,
) -> None:
# Save old modified_at timestamps
plan_old_modified_at = plan_instance.modified_at
@@ -108,7 +107,6 @@ def test_modified_at_triggers(
source_data_instance_old_modified_at = source_data_instance.modified_at
organisation_instance_old_modified_at = organisation_instance.modified_at
plan_map_instance_old_modified_at = plan_map_instance.modified_at
- lifecycle_date_instance_old_modified_at = lifecycle_date_instance.modified_at
# Edit tables to fire the triggers
plan_instance.exported_at = datetime.now()
@@ -122,7 +120,7 @@ def test_modified_at_triggers(
source_data_instance.additional_information_uri = "http://test2.fi"
organisation_instance.business_id = "foo"
plan_map_instance.name = {"fin": "foo"}
- lifecycle_date_instance.ending_at = datetime.now()
+
session.flush()
session.refresh(plan_instance)
session.refresh(land_use_area_instance)
@@ -135,7 +133,6 @@ def test_modified_at_triggers(
session.refresh(source_data_instance)
session.refresh(organisation_instance)
session.refresh(plan_map_instance)
- session.refresh(lifecycle_date_instance)
assert plan_instance.modified_at != plan_old_modified_at
assert land_use_area_instance.modified_at != land_use_area_instance_old_modified_at
@@ -154,178 +151,6 @@ def test_modified_at_triggers(
assert source_data_instance.modified_at != source_data_instance_old_modified_at
assert organisation_instance.modified_at != organisation_instance_old_modified_at
assert plan_map_instance != plan_map_instance_old_modified_at
- assert (
- lifecycle_date_instance.modified_at != lifecycle_date_instance_old_modified_at
- )
-
-
-def test_new_object_add_lifecycle_date_triggers(
- plan_instance: models.Plan,
- text_plan_regulation_instance: models.PlanRegulation,
- plan_proposition_instance: models.PlanProposition,
- land_use_area_instance: models.LandUseArea,
- other_area_instance: models.OtherArea,
- line_instance: models.Line,
- point_instance: models.Point,
-) -> None:
- assert plan_instance.lifecycle_dates
- lifecycle_date = next(iter(plan_instance.lifecycle_dates))
- assert lifecycle_date.lifecycle_status == plan_instance.lifecycle_status
- assert lifecycle_date.starting_at
- assert not lifecycle_date.ending_at
-
- assert text_plan_regulation_instance.lifecycle_status
- lifecycle_date = next(iter(text_plan_regulation_instance.lifecycle_dates))
- assert (
- lifecycle_date.lifecycle_status
- == text_plan_regulation_instance.lifecycle_status
- )
- assert lifecycle_date.starting_at
- assert not lifecycle_date.ending_at
-
- assert plan_proposition_instance.lifecycle_dates
- lifecycle_date = next(iter(plan_proposition_instance.lifecycle_dates))
- assert lifecycle_date.lifecycle_status == plan_proposition_instance.lifecycle_status
- assert lifecycle_date.starting_at
- assert not lifecycle_date.ending_at
-
- assert land_use_area_instance.lifecycle_dates
- lifecycle_date = next(iter(land_use_area_instance.lifecycle_dates))
- assert lifecycle_date.lifecycle_status == land_use_area_instance.lifecycle_status
- assert lifecycle_date.starting_at
- assert not lifecycle_date.ending_at
-
- assert other_area_instance.lifecycle_dates
- lifecycle_date = next(iter(other_area_instance.lifecycle_dates))
- assert lifecycle_date.lifecycle_status == other_area_instance.lifecycle_status
- assert lifecycle_date.starting_at
- assert not lifecycle_date.ending_at
-
- assert line_instance.lifecycle_dates
- lifecycle_date = next(iter(line_instance.lifecycle_dates))
- assert lifecycle_date.lifecycle_status == line_instance.lifecycle_status
- assert lifecycle_date.starting_at
- assert not lifecycle_date.ending_at
-
- assert point_instance.lifecycle_dates
- lifecycle_date = next(iter(point_instance.lifecycle_dates))
- assert lifecycle_date.lifecycle_status == point_instance.lifecycle_status
- assert lifecycle_date.starting_at
- assert not lifecycle_date.ending_at
-
-
-def test_new_lifecycle_date_triggers(
- session: Session,
- plan_instance: models.Plan,
- text_plan_regulation_instance: models.PlanRegulation,
- plan_proposition_instance: models.PlanProposition,
- land_use_area_instance: models.LandUseArea,
- other_area_instance: models.OtherArea,
- line_instance: models.Line,
- point_instance: models.Point,
- code_instance: codes.LifeCycleStatus,
- another_code_instance: codes.LifeCycleStatus,
-) -> None:
- assert plan_instance.lifecycle_status_id != another_code_instance.id
- assert text_plan_regulation_instance.lifecycle_status_id != another_code_instance.id
- assert plan_proposition_instance.lifecycle_status_id != another_code_instance.id
- assert land_use_area_instance.lifecycle_status_id != another_code_instance.id
- assert other_area_instance.lifecycle_status_id != another_code_instance.id
- assert line_instance.lifecycle_status_id != another_code_instance.id
- assert point_instance.lifecycle_status_id != another_code_instance.id
-
- # Update lifecycle_statuses to populate starting_at fields
- plan_instance.lifecycle_status = another_code_instance
- text_plan_regulation_instance.lifecycle_status = another_code_instance
- plan_proposition_instance.lifecycle_status = another_code_instance
- land_use_area_instance.lifecycle_status = another_code_instance
- other_area_instance.lifecycle_status = another_code_instance
- line_instance.lifecycle_status = another_code_instance
- point_instance.lifecycle_status = another_code_instance
- session.flush()
-
- # Update again to populate ending_at fields
- plan_instance.lifecycle_status = code_instance
- text_plan_regulation_instance.lifecycle_status = code_instance
- plan_proposition_instance.lifecycle_status = code_instance
- land_use_area_instance.lifecycle_status = code_instance
- other_area_instance.lifecycle_status = code_instance
- line_instance.lifecycle_status = code_instance
- point_instance.lifecycle_status = code_instance
- session.flush()
- session.refresh(plan_instance)
- session.refresh(text_plan_regulation_instance)
- session.refresh(plan_proposition_instance)
- session.refresh(land_use_area_instance)
- session.refresh(other_area_instance)
- session.refresh(line_instance)
- session.refresh(point_instance)
-
- # Get old and new entries in lifecycle_date table
- def get_new_lifecycle_date(instance: models.PlanBase) -> models.LifeCycleDate:
- return next(
- date
- for date in instance.lifecycle_dates
- if date.lifecycle_status == code_instance
- )
-
- def get_old_lifecycle_date(instance: models.PlanBase) -> models.LifeCycleDate:
- return next(
- date
- for date in instance.lifecycle_dates
- if date.lifecycle_status == another_code_instance
- )
-
- plan_new_lifecycle_date = get_new_lifecycle_date(plan_instance)
- plan_regulation_new_lifecycle_date = get_new_lifecycle_date(
- text_plan_regulation_instance
- )
- plan_proposition_new_lifecycle_date = get_new_lifecycle_date(
- plan_proposition_instance
- )
- land_use_area_new_lifecycle_date = get_new_lifecycle_date(land_use_area_instance)
- other_area_new_lifecycle_date = get_new_lifecycle_date(other_area_instance)
- line_new_lifecycle_date = get_new_lifecycle_date(line_instance)
- point_new_lifecycle_date = get_new_lifecycle_date(point_instance)
- plan_old_lifecycle_date = get_old_lifecycle_date(plan_instance)
- plan_regulation_old_lifecycle_date = get_old_lifecycle_date(
- text_plan_regulation_instance
- )
- plan_proposition_old_lifecycle_date = get_old_lifecycle_date(
- plan_proposition_instance
- )
- land_use_area_old_lifecycle_date = get_old_lifecycle_date(land_use_area_instance)
- other_area_old_lifecycle_date = get_old_lifecycle_date(other_area_instance)
- line_old_lifecycle_date = get_old_lifecycle_date(line_instance)
- point_old_lifecycle_date = get_old_lifecycle_date(point_instance)
-
- assert plan_new_lifecycle_date.lifecycle_status_id == code_instance.id
- assert plan_new_lifecycle_date.starting_at is not None
- assert plan_old_lifecycle_date.ending_at is not None
-
- assert plan_regulation_new_lifecycle_date.lifecycle_status_id == code_instance.id
- assert plan_regulation_new_lifecycle_date.starting_at is not None
- assert plan_regulation_old_lifecycle_date.ending_at is not None
-
- assert plan_proposition_new_lifecycle_date.lifecycle_status_id == code_instance.id
- assert plan_proposition_new_lifecycle_date.starting_at is not None
- assert plan_proposition_old_lifecycle_date.ending_at is not None
-
- assert land_use_area_instance.lifecycle_status_id == code_instance.id
- assert land_use_area_new_lifecycle_date.starting_at is not None
- assert land_use_area_old_lifecycle_date.ending_at is not None
-
- assert other_area_instance.lifecycle_status_id == code_instance.id
- assert other_area_new_lifecycle_date.starting_at is not None
- assert other_area_old_lifecycle_date.ending_at is not None
-
- assert line_instance.lifecycle_status_id == code_instance.id
- assert line_new_lifecycle_date.starting_at is not None
- assert line_old_lifecycle_date.ending_at is not None
-
- assert point_instance.lifecycle_status_id == code_instance.id
- assert point_new_lifecycle_date.starting_at is not None
- assert point_old_lifecycle_date.ending_at is not None
def test_new_lifecycle_status_triggers(
diff --git a/test/test_validation.py b/test/test_validation.py
deleted file mode 100644
index 4e71904..0000000
--- a/test/test_validation.py
+++ /dev/null
@@ -1,155 +0,0 @@
-from datetime import datetime, timedelta
-
-import pytest
-from sqlalchemy.exc import ProgrammingError
-from sqlalchemy.orm import Session
-
-from database import codes, models
-
-
-def test_validate_lifecycle_dates(
- session: Session,
- plan_instance: models.Plan,
- lifecycle_date_instance: models.LifeCycleDate,
-) -> None:
- assert (
- lifecycle_date_instance.ending_at
- and lifecycle_date_instance.starting_at < lifecycle_date_instance.ending_at
- )
- session.add(lifecycle_date_instance)
- # check that modified date cannot start after ending
- with pytest.raises(ProgrammingError):
- lifecycle_date_instance.starting_at = (
- lifecycle_date_instance.ending_at + timedelta(days=1)
- )
- session.flush()
- session.rollback()
- # check that new date cannot start after ending
- with pytest.raises(ProgrammingError):
- new_lifecycle_date_instance = models.LifeCycleDate(
- plan=plan_instance,
- starting_at=datetime.now() + timedelta(days=1),
- ending_at=datetime.now(),
- )
- session.add(new_lifecycle_date_instance)
- session.flush()
- session.rollback()
-
-
-def test_validate_event_dates(
- session: Session,
- preparation_date_instance: models.LifeCycleDate,
- interaction_event_date_instance: models.EventDate,
-) -> None:
- assert (
- interaction_event_date_instance.ending_at
- and interaction_event_date_instance.starting_at
- < interaction_event_date_instance.ending_at
- )
- session.add(interaction_event_date_instance)
- # check that modified event cannot start after ending
- with pytest.raises(ProgrammingError):
- interaction_event_date_instance.starting_at = (
- interaction_event_date_instance.ending_at + timedelta(days=1)
- )
- session.flush()
- session.rollback()
- # check that new event cannot start after ending
- with pytest.raises(ProgrammingError):
- new_event_date_instance = models.EventDate(
- lifecycle_date=preparation_date_instance,
- starting_at=datetime.now() + timedelta(days=1),
- ending_at=datetime.now(),
- )
- session.add(new_event_date_instance)
- session.flush()
- session.rollback()
-
-
-def test_validate_event_dates_inside_status_dates(
- session: Session,
- preparation_date_instance: models.LifeCycleDate,
- interaction_event_date_instance: models.EventDate,
-) -> None:
- assert (
- interaction_event_date_instance.starting_at
- > preparation_date_instance.starting_at
- )
- assert (
- preparation_date_instance.ending_at
- and interaction_event_date_instance.ending_at
- and interaction_event_date_instance.ending_at
- < preparation_date_instance.ending_at
- )
- # check that modified event cannot start before status starts
- with pytest.raises(ProgrammingError):
- interaction_event_date_instance.starting_at = (
- preparation_date_instance.starting_at - timedelta(days=1)
- )
- session.flush()
- session.rollback()
- # check that modified event cannot end after status ends
- with pytest.raises(ProgrammingError):
- interaction_event_date_instance.ending_at = (
- preparation_date_instance.ending_at + timedelta(days=1)
- )
- session.flush()
- session.rollback()
- # check that new event cannot start before status starts
- with pytest.raises(ProgrammingError):
- new_event_date_instance = models.EventDate(
- lifecycle_date=preparation_date_instance,
- starting_at=preparation_date_instance.starting_at - timedelta(days=1),
- )
- session.add(new_event_date_instance)
- session.flush()
- session.rollback()
- # check that new event cannot end after status ends
- with pytest.raises(ProgrammingError):
- new_event_date_instance = models.EventDate(
- lifecycle_date=preparation_date_instance,
- starting_at=preparation_date_instance.ending_at + timedelta(days=1),
- )
- session.add(new_event_date_instance)
- session.flush()
- session.rollback()
-
-
-def test_validate_event_types(
- session: Session,
- preparation_date_instance: models.LifeCycleDate,
- approved_date_instance: models.LifeCycleDate,
- decision_date_instance: models.EventDate,
- participation_plan_presenting_for_public_decision: codes.NameOfPlanCaseDecision,
- plan_proposal_presenting_for_public_decision: codes.NameOfPlanCaseDecision,
-) -> None:
- assert decision_date_instance.lifecycle_date == preparation_date_instance
- assert (
- decision_date_instance.decision
- == participation_plan_presenting_for_public_decision
- )
- session.add(decision_date_instance)
- # check that modified event cannot be added to wrong status
- with pytest.raises(ProgrammingError):
- decision_date_instance.lifecycle_date = approved_date_instance
- decision_date_instance.starting_at = approved_date_instance.starting_at
- decision_date_instance.ending_at = (
- approved_date_instance.starting_at + timedelta(days=30)
- )
- session.flush()
- session.rollback()
- # check that modified event cannot be added to wrong event type
- with pytest.raises(ProgrammingError):
- decision_date_instance.decision = plan_proposal_presenting_for_public_decision
- session.flush()
- session.rollback()
- # check that new event cannot be added to wrong status/event type combination
- with pytest.raises(ProgrammingError):
- new_event_date_instance = models.EventDate(
- lifecycle_date=approved_date_instance,
- starting_at=approved_date_instance.starting_at,
- decision=participation_plan_presenting_for_public_decision,
- )
- session.add(new_event_date_instance)
- session.flush()
- session.rollback()