Skip to content

Commit

Permalink
Min/Max anticipated and actual date checking for events (bcgov#1814)
Browse files Browse the repository at this point in the history
* min/max start date checking for events

* event service data validation and rf minor fies

* changes to start date validation in events
  • Loading branch information
dinesh-aot committed Feb 7, 2024
1 parent 0884d57 commit 41d6a31
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""simple_title_in_work_history
Revision ID: 5b390f888c3f
Revises: 73868272c32c
Create Date: 2024-02-06 22:38:55.052178
"""

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = "5b390f888c3f"
down_revision = "73868272c32c"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###

with op.batch_alter_table("indigenous_works_history", schema=None) as batch_op:
batch_op.alter_column(
"indigenous_consultation_level_id",
existing_type=sa.INTEGER(),
nullable=False,
autoincrement=False,
)

with op.batch_alter_table("works_history", schema=None) as batch_op:
batch_op.alter_column(
"simple_title",
existing_type=sa.TEXT(),
type_=sa.String(),
existing_nullable=True,
autoincrement=False,
)

# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("works_history", schema=None) as batch_op:
batch_op.alter_column(
"simple_title",
existing_type=sa.String(),
type_=sa.TEXT(),
existing_nullable=True,
autoincrement=False,
)

with op.batch_alter_table("indigenous_works_history", schema=None) as batch_op:
batch_op.alter_column(
"indigenous_consultation_level_id",
existing_type=sa.INTEGER(),
nullable=True,
autoincrement=False,
)

# ### end Alembic commands ###
16 changes: 16 additions & 0 deletions epictrack-api/src/api/application_constants/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright © 2019 Province of British Columbia
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""This module holds the application wide constants"""

MIN_WORK_START_DATE = "1995-06-30"
2 changes: 1 addition & 1 deletion epictrack-api/src/api/reports/resource_forecast_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ def _fetch_data(self, report_date: datetime):
WorkType.name.label("ea_type"),
WorkType.report_title.label("ea_type_label"),
WorkType.sort_order.label("ea_type_sort_order"),
PhaseCode.name.label("project_phase"),
WorkPhase.name.label("project_phase"),
EAAct.name.label("ea_act"),
FederalInvolvement.name.label("iaac"),
SubType.short_name.label("sub_type"),
Expand Down
85 changes: 85 additions & 0 deletions epictrack-api/src/api/services/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import functools
from datetime import datetime, timedelta
from typing import List
import pytz

from sqlalchemy import and_, extract, func, or_

Expand All @@ -42,6 +43,7 @@
from api.models.work_type import WorkType
from api.services.outcome_configuration import OutcomeConfigurationService
from api.utils import util
from api.application_constants import MIN_WORK_START_DATE

from ..utils.roles import Membership
from ..utils.roles import Role as KeycloakRole
Expand Down Expand Up @@ -295,6 +297,7 @@ def _process_events(
current_work_phase_index = util.find_index_in_array(
all_work_phases, current_work_phase
)
cls._validate_dates(event, current_work_phase, all_work_phases)
cls._previous_event_acutal_date_rule(
all_work_events, all_work_phases, current_work_phase_index, event, event_old
)
Expand Down Expand Up @@ -383,6 +386,88 @@ def _process_events(
current_event_index,
)

@classmethod
def _validate_dates(
cls, event: Event, current_work_phase: WorkPhase, all_work_phases: [WorkPhase]
):
"""Perform date validations for the min and max dates for events"""
if event.actual_date:
actual_min_date = cls._find_actual_date_min(
event, current_work_phase, all_work_phases
)
actual_max_date = cls._find_actual_date_max(current_work_phase)
if (
event.actual_date < actual_min_date
or event.actual_date > actual_max_date
):
raise UnprocessableEntityError(
f"Actual date should be between {actual_min_date} and {actual_max_date}"
)
if not event.actual_date:
anticipated_min_date = cls._find_anticipated_date_min(
event, current_work_phase, all_work_phases
)
if event.anticipated_date < anticipated_min_date:
raise UnprocessableEntityError(
f"Anticipdated date should be greater than {anticipated_min_date}"
)

@classmethod
def _find_anticipated_date_min(
cls, event: Event, current_work_phase: WorkPhase, all_work_phases: [WorkPhase]
):
"""Return the min date of anticipated date"""
anticipated_date_min = (
datetime.strptime(MIN_WORK_START_DATE, "%Y-%m-%d").replace(tzinfo=pytz.utc)
if cls._is_start_event(event)
and cls._is_start_phase(current_work_phase, all_work_phases)
else current_work_phase.work.start_date
)
return anticipated_date_min

@classmethod
def _find_actual_date_min(
cls, event: Event, current_work_phase: WorkPhase, all_work_phases: [WorkPhase]
):
"""Return the min date of actual date"""
actual_date_min = (
datetime.strptime(MIN_WORK_START_DATE, "%Y-%m-%d").replace(tzinfo=pytz.utc)
if cls._is_start_event(event)
and cls._is_start_phase(current_work_phase, all_work_phases)
else current_work_phase.start_date
)
return actual_date_min

@classmethod
def _find_actual_date_max(cls, current_work_phase: WorkPhase):
"""Return the max date of actual date"""
date_diff_days = (
(current_work_phase.end_date - current_work_phase.start_date).days
if current_work_phase.legislated
else 0
)
actual_date_max = (
current_work_phase.start_date + timedelta(days=date_diff_days)
if current_work_phase.legislated
else datetime.utcnow().replace(tzinfo=pytz.utc)
)
return actual_date_max

@classmethod
def _is_start_event(cls, event):
"""Return true if the given event is start event"""
return (
event.event_configuration.event_position.value
== EventPositionEnum.START.value
)

@classmethod
def _is_start_phase(
cls, current_work_phase: WorkPhase, all_work_phases: [WorkPhase]
):
"""Return true if the current phase is start phase"""
return all_work_phases[0].id == current_work_phase.id

@classmethod
def _handle_work_phase_for_start_event(
cls,
Expand Down

0 comments on commit 41d6a31

Please sign in to comment.