Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add_phase action bug fix #1344

Merged
merged 1 commit into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions epictrack-api/src/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def add_version(response): # pylint: disable=unused-variable

@app.errorhandler(Exception)
def handle_error(err):
current_app.logger.error(str(err))
if isinstance(err, ValidationError):
return err.messages, HTTPStatus.BAD_REQUEST
if isinstance(err, ResourceExistsError):
Expand Down
77 changes: 47 additions & 30 deletions epictrack-api/src/api/actions/add_phase.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from datetime import timedelta

from api.actions.base import ActionFactory
from api.models import Event, EventConfiguration, WorkPhase, db
from api.models import Event, EventConfiguration, WorkPhase, Work, db
from api.models.phase_code import PhaseCode, PhaseVisibilityEnum
from api.schemas import response as res

Expand All @@ -11,41 +11,58 @@
class AddPhase(ActionFactory):
"""Add a new phase"""

def run(self, source_event: Event, params: dict) -> None:
def run(self, source_event: Event, params) -> None:
"""Adds a new phase based on params"""
# Importing here to avoid circular imports
from api.services.work import WorkService
from api.services.work_phase import WorkPhaseService

self.preset_sort_order(source_event, len(params))
phase_start_date = source_event.actual_date + timedelta(days=1)
work_phase_data = self.get_additional_params(source_event, params)
work_phase_data.update(
{
"work_id": source_event.work.id,
"start_date": f"{phase_start_date}",
"end_date": f"{phase_start_date + timedelta(days=work_phase_data['number_of_days'])}",
"sort_order": source_event.event_configuration.work_phase.sort_order + 1,
}
)
work_phases = (
db.session.query(WorkPhase)
.filter(
WorkPhase.sort_order > source_event.event_configuration.work_phase.sort_order,
WorkPhase.work_id == source_event.work_id,
sort_order = source_event.event_configuration.work_phase.sort_order + 1
for param in params:
work_phase_data = self.get_additional_params(source_event, param)
end_date = phase_start_date + timedelta(
days=work_phase_data["number_of_days"]
)
.all()
)
for work_phase in work_phases:
work_phase.sort_order = work_phase.sort_order + 1
work_phase.update(work_phase.as_dict(recursive=False), commit=False)
work_phase = WorkPhase.flush(WorkPhase(**work_phase_data))
event_configurations = self.get_configurations(source_event, params)
event_configurations = res.EventConfigurationResponseSchema(many=True).dump(
event_configurations
work_phase_data.update(
{
"work_id": source_event.work.id,
"start_date": f"{phase_start_date}",
"end_date": end_date,
"sort_order": sort_order,
}
)
work_phase = WorkPhase.flush(WorkPhase(**work_phase_data))
event_configurations = self.get_configurations(source_event, param)
event_configurations = res.EventConfigurationResponseSchema(many=True).dump(
event_configurations
)
new_event_configurations = WorkService.create_configurations(
work_phase, event_configurations, False
)
WorkService.create_events_by_configuration(
work_phase, new_event_configurations
)
sort_order = sort_order + 1
phase_start_date = end_date + timedelta(days=1)
# update the current work phase
current_work_phase = WorkPhaseService.find_current_work_phase(
source_event.work_id
)
new_event_configurations = WorkService.create_configurations(
work_phase, event_configurations, False
work = Work.find_by_id(source_event.work_id)
work.current_work_phase_id = current_work_phase.id
work.update(work.as_dict(recursive=False), commit=False)

def preset_sort_order(self, source_event, number_of_new_work_phases: int) -> None:
"""Adjust the sort order of the existing work phases for the new ones"""
db.session.query(WorkPhase).filter(
WorkPhase.sort_order
> source_event.event_configuration.work_phase.sort_order,
WorkPhase.work_id == source_event.work_id,
).update(
{WorkPhase.sort_order: WorkPhase.sort_order + number_of_new_work_phases}
)
WorkService.create_events_by_configuration(work_phase, new_event_configurations)

def get_additional_params(self, source_event, params):
"""Returns additional parameter"""
Expand All @@ -65,7 +82,7 @@ def get_additional_params(self, source_event, params):
"name": params.get("new_name"),
"legislated": params.get("legislated"),
"number_of_days": phase.number_of_days,
"visibility": PhaseVisibilityEnum.REGULAR.value,
"visibility": PhaseVisibilityEnum.REGULAR,
}
return work_phase_data

Expand All @@ -76,7 +93,7 @@ def get_configurations(self, source_event: Event, params) -> [EventConfiguration
.join(PhaseCode, WorkPhase.phase_id == PhaseCode.id)
.filter(
WorkPhase.work_id == source_event.work_id,
PhaseCode.name == params.get("phase_name"),
WorkPhase.name == params.get("phase_name"),
PhaseCode.work_type_id == params.get("work_type_id"),
PhaseCode.ea_act_id == params.get("ea_act_id"),
WorkPhase.is_active.is_(True),
Expand Down
2 changes: 1 addition & 1 deletion epictrack-api/src/api/actions/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def find_configuration(source_event: Event, params) -> int:
.join(PhaseCode, WorkPhase.phase_id == PhaseCode.id)
.filter(
WorkPhase.work_id == source_event.work_id,
PhaseCode.name == params.get("phase_name"),
WorkPhase.name == params.get("phase_name"),
PhaseCode.work_type_id == params.get("work_type_id"),
PhaseCode.ea_act_id == params.get("ea_act_id"),
WorkPhase.visibility == PhaseVisibilityEnum.REGULAR.value,
Expand Down
2 changes: 1 addition & 1 deletion epictrack-api/src/api/services/work.py
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ def create_events_by_configuration(
cls, work_phase: WorkPhase, event_configurations: [EventConfiguration]
) -> None:
"""Create events by given event configurations"""
if work_phase.visibility == PhaseVisibilityEnum.REGULAR:
if work_phase.visibility.value == PhaseVisibilityEnum.REGULAR.value:
parent_event_configs = list(
filter(
lambda x, _work_phase_id=work_phase.id: not x.parent_id and
Expand Down
56 changes: 44 additions & 12 deletions epictrack-api/src/api/services/work_phase.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import functools
from datetime import timezone

from api.models import PhaseCode, WorkPhase, db
from api.models import PhaseCode, WorkPhase, PRIMARY_CATEGORIES, db
from api.models.event_type import EventTypeEnum
from api.schemas.work_v2 import WorkPhaseSchema
from api.models.phase_code import PhaseVisibilityEnum
Expand Down Expand Up @@ -77,6 +77,21 @@ def get_template_upload_status(cls, work_phase_id: int) -> bool:
result["template_available"] = template_available
return result

@classmethod
def find_current_work_phase(cls, work_id: int) -> WorkPhase:
"""Find the current work phase which is in progress"""
work_phase = (
db.session.query(WorkPhase)
.filter(
WorkPhase.work_id == work_id,
WorkPhase.visibility == PhaseVisibilityEnum.REGULAR.value,
WorkPhase.is_completed.is_(False),
)
.order_by(WorkPhase.sort_order)
.first()
)
return work_phase

@classmethod
def find_work_phases_status(cls, work_id: int):
"""Return the work phases with additional information"""
Expand All @@ -87,13 +102,13 @@ def find_work_phases_status(cls, work_id: int):
WorkPhase.work_id == work_id,
WorkPhase.is_active.is_(True),
WorkPhase.is_deleted.is_(False),
WorkPhase.visibility != PhaseVisibilityEnum.HIDDEN.value
WorkPhase.visibility != PhaseVisibilityEnum.HIDDEN.value,
)
.order_by(WorkPhase.sort_order)
.all()
)
result = []
events = EventService.find_events(work_id)
events = EventService.find_events(work_id, event_categories=PRIMARY_CATEGORIES)
for work_phase in work_phases:
result_item = {}
result_item["work_phase"] = work_phase
Expand All @@ -102,33 +117,50 @@ def find_work_phases_status(cls, work_id: int):
).days
work_phase_events = list(
filter(
lambda x, _work_phase_id=work_phase.id: x.event_configuration.work_phase_id == _work_phase_id,
lambda x, _work_phase_id=work_phase.id: x.event_configuration.work_phase_id
== _work_phase_id,
events,
)
)
work_phase_events = sorted(work_phase_events, key=functools.cmp_to_key(EventService.event_compare_func))
work_phase_events = sorted(
work_phase_events,
key=functools.cmp_to_key(EventService.event_compare_func),
)
suspended_days = functools.reduce(
lambda x, y: x + y,
map(
lambda x: x.number_of_days
if x.event_configuration.event_type_id == EventTypeEnum.TIME_LIMIT_RESUMPTION.value and
x.actual_date is not None else 0,
if x.event_configuration.event_type_id
== EventTypeEnum.TIME_LIMIT_RESUMPTION.value
and x.actual_date is not None
else 0,
work_phase_events,
),
)
result_item["total_number_of_days"] = total_days - suspended_days
next_milestone_event = next((x for x in work_phase_events if x.actual_date is None), None)
next_milestone_event = next(
(x for x in work_phase_events if x.actual_date is None), None
)
if next_milestone_event:
result_item["next_milestone"] = next_milestone_event.name
total_number_of_milestones = len(work_phase_events)
completed_ones = len(list(filter(lambda x: x.actual_date is not None, work_phase_events)))
result_item["milestone_progress"] = (completed_ones / total_number_of_milestones) * 100
completed_ones = len(
list(filter(lambda x: x.actual_date is not None, work_phase_events))
)
result_item["milestone_progress"] = (
completed_ones / total_number_of_milestones
) * 100
days_passed = 0
if work_phase.work.current_work_phase_id == work_phase.id:
if work_phase.is_suspended:
days_passed = (work_phase.suspended_date.date() - work_phase.start_date.date()).days
days_passed = (
work_phase.suspended_date.date() - work_phase.start_date.date()
).days
else:
days_passed = (datetime.datetime.now(timezone.utc).date() - work_phase.start_date.date()).days
days_passed = (
datetime.datetime.now(timezone.utc).date()
- work_phase.start_date.date()
).days
days_passed = 0 if days_passed < 0 else days_passed
days_left = (total_days - suspended_days) - days_passed
else:
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading