Skip to content

Commit

Permalink
Update Sprint with extra attributes
Browse files Browse the repository at this point in the history
* Add `started_at` and `completed_at`
* Add `unplanned` flag to `SprintTask`
  • Loading branch information
AndreyMarkinPPC committed Feb 11, 2024
1 parent 47c6114 commit 2b36b1c
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 8 deletions.
5 changes: 4 additions & 1 deletion terka/adapters/orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,9 @@
Column("end_date", Date, nullable=False),
Column("status", Enum(sprint.SprintStatus)),
Column("capacity", Integer, nullable=False),
Column("goal", String(225), nullable=True))
Column("goal", String(225), nullable=True),
Column("started_at", DateTime, nullable=True),
Column("completed_at", DateTime, nullable=True))

sprint_tasks = Table(
"sprint_tasks",
Expand All @@ -232,6 +234,7 @@
Column("sprint", ForeignKey("sprints.id"), nullable=False),
Column("story_points", Integer, nullable=False),
Column("is_active_link", Boolean, nullable=False),
Column("unplanned", Boolean, nullable=False),
)

time_tracker_entries = Table(
Expand Down
23 changes: 19 additions & 4 deletions terka/domain/entities/sprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ def __init__(self,
status: str = "PLANNED",
capacity: int = 40,
goal: str | None = None,
started_at: datetime | None = None,
completed_at: datetime | None = None,
**kwargs) -> None:
if not start_date and not end_date:
raise ValueError("Please add start and end date of the sprint")
Expand All @@ -48,7 +50,8 @@ def __init__(self,
self.status = self._validate_status(status)
self.goal = goal
self.capacity = capacity
self.is_completed = False
self.started_at = started_at
self.completed_at = completed_at

def _validate_status(self, status):
if status and status not in [s.name for s in SprintStatus]:
Expand All @@ -64,17 +67,21 @@ def complete(self, tasks) -> None:
if incompleted_tasks:
logging.warning("[Sprint %d]: %d tasks haven't been completed",
self.id, len(incompleted_tasks))
self.is_completed = True

@property
def is_completed(self) -> bool:
return self.status == SprintStatus.COMPLETED

@property
def overplanned(self) -> bool:
return self.velocity > self.capacity

@property
def remaining_capatity(self) -> float:
def remaining_capacity(self) -> float:
if not self.overplanned:
return self.capacity - self.velocity
return 0

@property
def velocity(self) -> float:
return round(sum([t.story_points for t in self.tasks]), 1)
Expand All @@ -100,6 +107,14 @@ def total_time_spent(self):
total_time_spent_sprint += sprint_task.tasks.total_time_spent
return total_time_spent_sprint

@property
def unplanned_tasks(self) -> list[Task]:
tasks = []
for entity_task in self.tasks:
if entity_task.unplanned:
tasks.append(entity_task.tasks)
return tasks

@property
def open_tasks(self) -> list[Task]:
tasks = []
Expand All @@ -120,7 +135,6 @@ def completed_tasks(self) -> list[Task]:
tasks.append(task)
return tasks


@property
def pct_completed(self) -> float:
if (total_tasks := len(self.tasks)) > 0:
Expand Down Expand Up @@ -165,3 +179,4 @@ class SprintTask:
story_points: int = 0
#TODO: add actual_time_spent: int = 0
is_active_link: bool = True
unplanned: bool = False
11 changes: 11 additions & 0 deletions terka/presentations/text_ui/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -853,10 +853,21 @@ def compose(self) -> ComposeResult:
key=lambda x: x[1],
reverse=True):
sorted_projects += f" * {name}: {Formatter.format_time_spent(value)} \n"
if started_at := self.entity.started_at:
started_at_string = started_at.strftime("%Y-%m-%d %H:%M")
else:
if self.entity.status.name != "PLANNED":
started_at_string = self.entity.start_date.strftime(
"%Y-%m-%d %H:%M")
else:
started_at_string = "Not started"

yield MarkdownViewer(f"""
# Sprint details:
* Period: {self.entity.start_date} - {self.entity.end_date}
* Started: {started_at_string}
* Open tasks: {len(self.entity.open_tasks)} ({len(self.entity.tasks)})
* Share of unplanned tasks: {round(len(self.entity.unplanned_tasks) / len(self.entity.tasks), 2) :.0%}
* Pct Completed: {round(self.entity.pct_completed, 2) :.0%}
* Velocity: {self.entity.velocity} ({self.entity.capacity})
* Time spend: {Formatter.format_time_spent(self.entity.total_time_spent)}
Expand Down
9 changes: 6 additions & 3 deletions terka/service_layer/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def start(cmd: commands.StartSprint,
raise exceptions.TerkaSprintCompleted(
"Cannot start completed sprint")
uow.tasks.update(entities.sprint.Sprint, cmd.id,
{"status": "ACTIVE"})
{"status": "ACTIVE", "started": datetime.now()})
uow.commit()
for sprint_task in existing_sprint.tasks:
task = sprint_task.tasks
Expand Down Expand Up @@ -378,8 +378,11 @@ def _add(cmd: commands.AddTask, bus: "messagebus.MessageBus",
{"story_points": float(story_points)})
uow.commit()
else:
if entity_name == "sprint" and story_points:
entity_dict["story_points"] = story_points
if entity_name == "sprint":
if story_points:
entity_dict["story_points"] = story_points
entity_dict["unplanned"] = (existing_entity.started_at <
datetime.now())
entity_task = entity_task_type(**entity_dict)
uow.tasks.add(entity_task)
uow.commit()
Expand Down

0 comments on commit 2b36b1c

Please sign in to comment.