129 lines
4.4 KiB
Python
129 lines
4.4 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import date, datetime, timedelta
|
|
|
|
from sqlalchemy import select
|
|
|
|
from ..extensions import db
|
|
from ..models import TaskInstance, TaskTemplate
|
|
from .badges import evaluate_task_badges
|
|
from .dates import add_months, today_local
|
|
|
|
|
|
def refresh_task_status(task: TaskInstance, reference_date: date | None = None) -> bool:
|
|
status = task.compute_status(reference_date or today_local())
|
|
if task.status != status:
|
|
task.status = status
|
|
return True
|
|
return False
|
|
|
|
|
|
def refresh_task_statuses(tasks: list[TaskInstance]) -> None:
|
|
dirty = any(refresh_task_status(task) for task in tasks)
|
|
if dirty:
|
|
db.session.commit()
|
|
|
|
|
|
def create_task_template_and_instance(form) -> TaskInstance:
|
|
template = TaskTemplate(
|
|
title=form.title.data.strip(),
|
|
description=(form.description.data or "").strip(),
|
|
default_points=form.default_points.data,
|
|
default_assigned_user_id=form.assigned_user_id.data,
|
|
recurrence_interval_value=form.recurrence_interval_value.data if form.recurrence_interval_unit.data != "none" else None,
|
|
recurrence_interval_unit=form.recurrence_interval_unit.data,
|
|
active=form.active.data,
|
|
)
|
|
db.session.add(template)
|
|
db.session.flush()
|
|
|
|
task = TaskInstance(
|
|
task_template_id=template.id,
|
|
title=template.title,
|
|
description=template.description,
|
|
assigned_user_id=template.default_assigned_user_id,
|
|
due_date=form.due_date.data,
|
|
points_awarded=template.default_points,
|
|
status="open",
|
|
)
|
|
refresh_task_status(task, form.due_date.data)
|
|
db.session.add(task)
|
|
db.session.commit()
|
|
return task
|
|
|
|
|
|
def update_template_and_instance(task: TaskInstance, form) -> TaskInstance:
|
|
template = task.task_template
|
|
template.title = form.title.data.strip()
|
|
template.description = (form.description.data or "").strip()
|
|
template.default_points = form.default_points.data
|
|
template.default_assigned_user_id = form.assigned_user_id.data
|
|
template.recurrence_interval_unit = form.recurrence_interval_unit.data
|
|
template.recurrence_interval_value = (
|
|
form.recurrence_interval_value.data if form.recurrence_interval_unit.data != "none" else None
|
|
)
|
|
template.active = form.active.data
|
|
|
|
task.title = template.title
|
|
task.description = template.description
|
|
task.assigned_user_id = template.default_assigned_user_id
|
|
task.points_awarded = template.default_points
|
|
task.due_date = form.due_date.data
|
|
refresh_task_status(task, form.due_date.data)
|
|
db.session.commit()
|
|
return task
|
|
|
|
|
|
def _next_due_date(task: TaskInstance) -> date | None:
|
|
template = task.task_template
|
|
value = template.recurrence_interval_value
|
|
if template.recurrence_interval_unit == "none" or not value:
|
|
return None
|
|
if template.recurrence_interval_unit == "days":
|
|
return task.due_date + timedelta(days=value)
|
|
if template.recurrence_interval_unit == "weeks":
|
|
return task.due_date + timedelta(weeks=value)
|
|
if template.recurrence_interval_unit == "months":
|
|
return add_months(task.due_date, value)
|
|
return None
|
|
|
|
|
|
def ensure_next_recurring_task(task: TaskInstance) -> TaskInstance | None:
|
|
next_due = _next_due_date(task)
|
|
if not next_due or not task.task_template.active:
|
|
return None
|
|
|
|
existing = db.session.scalar(
|
|
select(TaskInstance).where(
|
|
TaskInstance.task_template_id == task.task_template_id,
|
|
TaskInstance.due_date == next_due,
|
|
)
|
|
)
|
|
if existing:
|
|
return existing
|
|
|
|
next_task = TaskInstance(
|
|
task_template_id=task.task_template_id,
|
|
title=task.task_template.title,
|
|
description=task.task_template.description,
|
|
assigned_user_id=task.task_template.default_assigned_user_id,
|
|
due_date=next_due,
|
|
points_awarded=task.task_template.default_points,
|
|
status="open",
|
|
)
|
|
refresh_task_status(next_task, today_local())
|
|
db.session.add(next_task)
|
|
return next_task
|
|
|
|
|
|
def complete_task(task: TaskInstance, completed_by_user_id: int) -> TaskInstance:
|
|
if not task.completed_at:
|
|
task.completed_at = datetime.utcnow()
|
|
task.completed_by_user_id = completed_by_user_id
|
|
task.status = "completed"
|
|
ensure_next_recurring_task(task)
|
|
db.session.commit()
|
|
if task.completed_by_user:
|
|
evaluate_task_badges(task.completed_by_user)
|
|
return task
|