diff --git a/epictrack-api/src/api/actions/common.py b/epictrack-api/src/api/actions/common.py index 596abb630..4788883ec 100644 --- a/epictrack-api/src/api/actions/common.py +++ b/epictrack-api/src/api/actions/common.py @@ -2,6 +2,8 @@ from datetime import datetime from api.models import Event, EventConfiguration, WorkPhase, db from api.models.phase_code import PhaseCode, PhaseVisibilityEnum +from api.models.work_calendar_event import WorkCalendarEvent +from api.models.calendar_event import CalendarEvent def find_configuration(source_event: Event, params) -> int: @@ -42,3 +44,29 @@ def find_event_date(source_event: Event) -> datetime: if source_event.actual_date else source_event.anticipated_date ) + + +def deactivate_calendar_events_by_configuration_ids(configuration_ids: [int]): + """Make the calendar events inactive based on the source event configuration ids""" + if len(configuration_ids) > 0: + events_result = ( + db.session.query(Event) + .filter(Event.event_configuration_id.in_(configuration_ids)) + .all() + ) + source_event_ids = list(map(lambda x: x.id, events_result)) + work_calendar_events = ( + db.session.query(WorkCalendarEvent) + .filter(WorkCalendarEvent.source_event_id.in_(source_event_ids)) + .all() + ) + work_calendar_event_ids = list(map(lambda x: x.id, work_calendar_events)) + calendar_event_ids = list( + map(lambda x: x.calendar_event_id, work_calendar_events) + ) + db.session.query(WorkCalendarEvent).filter( + WorkCalendarEvent.id.in_(work_calendar_event_ids) + ).update({WorkCalendarEvent.is_active: False}) + db.session.query(CalendarEvent).filter( + CalendarEvent.id.in_(calendar_event_ids) + ).update({CalendarEvent.is_active: False}) diff --git a/epictrack-api/src/api/actions/set_events_status.py b/epictrack-api/src/api/actions/set_events_status.py index 530389370..cec2b5cb0 100644 --- a/epictrack-api/src/api/actions/set_events_status.py +++ b/epictrack-api/src/api/actions/set_events_status.py @@ -3,7 +3,7 @@ from api.models import db from api.models.event import Event -from .common import find_configuration +from .common import find_configuration, deactivate_calendar_events_by_configuration_ids class SetEventsStatus(ActionFactory): @@ -11,9 +11,13 @@ class SetEventsStatus(ActionFactory): def run(self, source_event, params): """Sets all future events to INACTIVE""" + event_configuration_ids = [] if isinstance(params, list): for event_params in params: event_configuration = find_configuration(source_event, event_params) + event_configuration_ids.append(event_configuration.id) db.session.query(Event).filter( Event.event_configuration_id == event_configuration.id ).update({Event.is_active: event_params.get("is_active")}) + # Deactivate all child events (Calendar events at this point) of the main event + deactivate_calendar_events_by_configuration_ids(event_configuration_ids) diff --git a/epictrack-api/src/api/actions/set_phases_status.py b/epictrack-api/src/api/actions/set_phases_status.py index b3ac9f0b5..729234dbf 100644 --- a/epictrack-api/src/api/actions/set_phases_status.py +++ b/epictrack-api/src/api/actions/set_phases_status.py @@ -5,6 +5,11 @@ from api.models.event_configuration import EventConfiguration from api.models.phase_code import PhaseCode, PhaseVisibilityEnum from api.models.work_phase import WorkPhase +from api.models.work_calendar_event import WorkCalendarEvent +from api.models.calendar_event import CalendarEvent +from api.models.task_event import TaskEvent + +from .common import deactivate_calendar_events_by_configuration_ids class SetPhasesStatus(ActionFactory): @@ -48,8 +53,30 @@ def run(self, source_event: Event, params): db.session.query(WorkPhase).filter( WorkPhase.id == source_event.event_configuration.work_phase_id ).update({WorkPhase.is_completed: True}) + source_event_ids = list(map(lambda x: x.id, events_to_be_updated)) + self.deactivate_calendar_events(source_event_ids) self._deactivate_phases_and_events(work_phase_ids) + def deactivate_calendar_events(self, source_event_ids: [int]) -> None: + """Deactivate calendar events by source event ids""" + work_calendar_events = db.session.query(WorkCalendarEvent).filter( + WorkCalendarEvent.source_event_id.in_(source_event_ids) + ) + work_calendar_events_ids = list(map(lambda x: x.id, work_calendar_events)) + calendar_event_ids = list( + map(lambda x: x.calendar_event_id, work_calendar_events) + ) + db.session.query(WorkCalendarEvent).filter( + WorkCalendarEvent.id.in_(work_calendar_events_ids) + ).update({WorkCalendarEvent.is_active: False}) + db.session.query(CalendarEvent).filter(CalendarEvent.id.in_(calendar_event_ids)) + + def deactivate_task_events(self, work_phase_ids: [int]) -> None: + """Deactivate task events by work phase ids""" + db.session.query(TaskEvent).filter( + TaskEvent.work_phase_id.in_(work_phase_ids) + ).update({TaskEvent.is_active: False}) + def get_additional_params(self, source_event: Event, params) -> int: """Returns additional parameter""" work_phase = ( @@ -81,3 +108,5 @@ def _deactivate_phases_and_events(self, work_phase_ids: [int]) -> None: db.session.query(Event).filter( Event.event_configuration_id.in_(event_configuration_ids) ).update({Event.is_active: False}) + deactivate_calendar_events_by_configuration_ids(event_configuration_ids) + self.deactivate_task_events(work_phase_ids) diff --git a/epictrack-api/src/api/schemas/response/work_response.py b/epictrack-api/src/api/schemas/response/work_response.py index 6404bda99..9818bf3c4 100644 --- a/epictrack-api/src/api/schemas/response/work_response.py +++ b/epictrack-api/src/api/schemas/response/work_response.py @@ -136,6 +136,7 @@ class WorkPhaseAdditionalInfoResponseSchema(Schema): ) next_milestone = fields.Str(metadata={"description": "Next milestone in the phase"}) milestone_progress = fields.Number(metadata={"description": "Milestone progress"}) + is_last_phase = fields.Number(metadata={"description": "Indicate if this the last phase of the work"}) days_left = fields.Number( metadata={"description": "Number of days left in the phase"} ) diff --git a/epictrack-api/src/api/services/work_phase.py b/epictrack-api/src/api/services/work_phase.py index 39d960b74..8ca7cb773 100644 --- a/epictrack-api/src/api/services/work_phase.py +++ b/epictrack-api/src/api/services/work_phase.py @@ -109,12 +109,15 @@ def find_multiple_works_phases_status( result_dict = {} work_ids = list(work_params_dict.keys()) - work_phases_dict = cls._query_work_phases(work_ids) + work_phases_dict, total_work_phases = cls._query_work_phases(work_ids) + index = 1 for work_id, work_phase_id in work_params_dict.items(): result_dict[work_id] = cls._find_work_phase_status( work_id, work_phase_id, work_phases_dict.get(work_id, []) ) + result_dict["is_last_phase"] = index == total_work_phases + index = index + 1 return result_dict @@ -126,19 +129,19 @@ def _query_work_phases(cls, work_ids): .join(PhaseCode, WorkPhase.phase_id == PhaseCode.id) .filter( WorkPhase.work_id.in_(work_ids), - WorkPhase.is_active.is_(True), WorkPhase.is_deleted.is_(False), WorkPhase.visibility != PhaseVisibilityEnum.HIDDEN.value, ) .order_by(WorkPhase.sort_order) .all() ) - + total_work_phases = len(work_phases_dict) + work_phases_dict = list(filter(lambda x: x[1].is_active is True, work_phases_dict)) result_dict = defaultdict(list) for work_id, work_phase in work_phases_dict: result_dict[work_id].append(work_phase) - return result_dict + return result_dict, total_work_phases @classmethod def _find_work_phase_status(cls, work_id, work_phase_id, work_phases): diff --git a/epictrack-web/src/components/workPlan/event/EventForm.tsx b/epictrack-web/src/components/workPlan/event/EventForm.tsx index 718c05bd1..0d87345de 100644 --- a/epictrack-web/src/components/workPlan/event/EventForm.tsx +++ b/epictrack-web/src/components/workPlan/event/EventForm.tsx @@ -153,9 +153,9 @@ const EventForm = ({ }, [selectedConfiguration, event]); const decisionMakerPositionIds = useMemo(() => { - const workPhaseIndex = ctx.workPhases.findIndex( - (p) => p.work_phase.id === ctx.selectedWorkPhase?.work_phase.id - ); + // const workPhaseIndex = ctx.workPhases.findIndex( + // (p) => p.work_phase.id === ctx.selectedWorkPhase?.work_phase.id + // ); const lastDecisionIndex = milestoneEvents.findLastIndex( (p: EventsGridModel) => p.event_configuration.event_category_id === EventCategory.DECISION @@ -164,7 +164,7 @@ const EventForm = ({ (p: EventsGridModel) => p.id === event?.id ); if ( - workPhaseIndex === ctx.workPhases.length - 1 && + ctx.selectedWorkPhase?.is_last_phase && lastDecisionIndex === currentEventIndex ) { return [Number(ctx.work?.decision_maker_position_id)]; diff --git a/epictrack-web/src/models/work.ts b/epictrack-web/src/models/work.ts index c51bdc960..ade89743e 100644 --- a/epictrack-web/src/models/work.ts +++ b/epictrack-web/src/models/work.ts @@ -71,6 +71,7 @@ export interface WorkPhaseAdditionalInfo { next_milestone: string; milestone_progress: number; days_left: number; + is_last_phase: boolean; } export interface TemplateStatus extends MasterBase {