angee.work.models
Operational queues, cycles, triage, and same-row task work mechanics.
Queue is a materialized child of :class:spaces.Group: its inherited group row remains the one roster and hierarchy owner. TaskWork is a table-less donor onto projects.Task; queue, stage, cycle, numbering, and triage columns therefore exist only when this addon is composed, on the task table itself.
QueueManager
class QueueManager(GroupManager)Own personal-queue identity and idempotent provisioning.
personal_slug
def personal_slug(user: models.Model) -> strReturn the unique stable slug of user's personal queue.
personal_key
def personal_key(user: models.Model) -> strReturn P<pk>, rejecting PKs above 11 digits rather than truncating.
personal_for
def personal_for(user: models.Model, *, provision: bool = False) -> Any | NoneReturn user's personal queue, optionally provisioning it.
provision_personal
def provision_personal(user: models.Model) -> AnyIdempotently create one private owner-rostered queue for user.
Queue
class Queue(AngeeModel)An ongoing operational context, materialized as a spaces.Group child.
EstimateScale
class EstimateScale(models.TextChoices)Estimation vocabulary configured per queue.
CycleStartDay
class CycleStartDay(models.IntegerChoices)ISO weekday on which generated cycles start.
Meta
class Meta()Django model options for the materialized queue child.
clean
def clean() -> NoneNormalize the queue key and validate its explicit default stage.
save
def save(*args: Any, **kwargs: Any) -> NonePersist the queue and atomically provision its stage/numbering substrate.
task_sequence_key
def task_sequence_key() -> strReturn the stable sequence lookup key for this queue's task numbers.
ensure_task_sequence
def ensure_task_sequence() -> models.ModelReturn this queue's task-number sequence, creating it if absent.
next_task_number
def next_task_number() -> intDraw the next gapless task number inside the caller's transaction.
Stage
class Stage(StagePrimitive, AuditMixin, AngeeDataModel)One ordered, user-named pipeline stage owned by a queue.
StageCategory
class StageCategory(models.TextChoices)Closed semantic categories projected onto coarse task status.
Meta
class Meta(StagePrimitive.Meta)Django model options for queue stages.
from_db
@classmethod
def from_db(cls, db: Any, field_names: Sequence[str],
values: Sequence[Any]) -> StageLoad a row and remember facts that identify a system stage.
save
def save(*args: Any, **kwargs: Any) -> NonePrevent user creation or renaming of triage/duplicate system stages.
delete
def delete(*args: Any, **kwargs: Any) -> tuple[int, dict[str, int]]Prevent users from deleting the two system-provisioned stages.
CycleManager
class CycleManager(AngeeManager)Own idempotent queue-cadence generation and due-cycle closure.
generate_for_queue
def generate_for_queue(
queue: models.Model,
*,
as_of: date | None = None,
window_end: date | None = None,
completed_at: datetime | None = None) -> tuple[Any, ...]Generate and close one queue's cadence, returning its ordered cycles.
Cycle windows are closed date intervals: both starts_on and ends_on belong to the cycle. The first empty-cadence window starts on the configured weekday at or before as_of (an exact start-day hit is included). Each next start is the day after the inclusive end plus exactly cycle_cooldown_weeks * 7 calendar dates, which belong to no cycle. A cycle closes only when as_of > ends_on; its end day remains active.
Passing window_end generates every cadence start through that inclusive boundary. Without it, generation maintains the queue's configured number of future cycles. The queue row is locked, existing windows are reused, and numbers advance from the last row, making reruns over the same window exact no-ops.
Cycle
class Cycle(AuditMixin, AngeeDataModel)One generated, queue-scoped planning window with immutable close evidence.
Meta
class Meta()Django model options for generated queue cycles.
display_name
@property
def display_name() -> strReturn the optional custom name or its stable Cycle N fallback.
__str__
def __str__() -> strReturn the custom or generated cycle label.
clean
def clean() -> NoneReject an inverted generated date window.
save
def save(*args: Any, **kwargs: Any) -> NonePersist while keeping close timestamp and snapshot immutable.
close
def close(*,
next_cycle: Any | None = None,
completed_at: datetime | None = None) -> CycleSnapshot open tasks and roll them into the next cycle atomically.
uncompleted_upon_close stores the sorted public task ids exactly as they stood immediately before rollover. Reinvoking a completed cycle is an exact no-op and cannot replace that evidence or move tasks again.
TaskWork
class TaskWork(StagedModelMixin, AngeeModel)Same-row work contribution folded into projects.Task.
Meta
class Meta()Abstract donor options folded into the concrete task table.
__init__
def __init__(*args: Any, **kwargs: Any) -> NoneTrack caller-authored status separately from Django row loading.
__setattr__
def __setattr__(name: str, value: Any) -> NoneRemember direct status assignment while allowing the projection writer.
refresh_from_db
def refresh_from_db(*args: Any, **kwargs: Any) -> NoneRefresh without misclassifying Django's field hydration as a direct write.
full_clean
def full_clean(*args: Any, **kwargs: Any) -> NoneLet Django normalize fields without inventing a direct status write.
Model.clean_fields() assigns every cleaned value back through the field descriptor. Preserve a status assignment already made by the caller, but do not treat that framework-owned normalization as a new authored assignment. This keeps resource loading compatible with the direct-status rejection enforced by :meth:clean and :meth:save.
apply_create_defaults
def apply_create_defaults() -> Mapping[str, Sequence[Any]]Default queue/stage from the actor and return their create relations.
clean
def clean() -> NoneProject a stage before base lifecycle validation and enforce scope.
save
def save(*args: Any, **kwargs: Any) -> NonePersist stage projection and queue numbering in one transaction.
complete
def complete() -> AnyMove to the first completed stage, or use the base verb without a queue.
drop
def drop(reason: Any) -> AnyMove to duplicate/canceled according to the base dropped reason.
reopen
def reopen() -> AnyMove to the queue-owned default stage, or use the base verb without a queue.
accept
def accept(stage: models.Model | None = None) -> AnyLeave triage for a same-queue, non-system stage, idempotently.
decline
def decline(reason: Any) -> AnyLeave triage for the canceled stage with a closed dropped reason.
snooze
def snooze(until: datetime) -> AnySnooze a triage task until an inclusive instant or new chatter activity.
wake_due_snoozes
@classmethod
def wake_due_snoozes(cls, *, now: datetime | None = None) -> intClear snoozes whose inclusive wake instant is at or before now.
wake_from_chatter_thread
@classmethod
def wake_from_chatter_thread(cls, thread_id: Any) -> intClear snooze state on the task whose chatter owns thread_id.
mark_duplicate
def mark_duplicate(canonical: models.Model) -> AnyMerge this duplicate into canonical in one row-locked transaction.
The atomic postcondition is one directional duplicate relation, this task in its system duplicate stage (and therefore dropped/duplicate), all source links and followers moved with collision deduplication, followed by every deterministic ANGEE_WORK_MERGE_CONTRIBUTORS mover. Any failure rolls every one of those writes back.
work_key
@property
def work_key() -> str | NoneReturn the queue-keyed task number (for example ENG-42).
validate_cycle_scope
def validate_cycle_scope() -> NoneReject a cycle outside the task's queue.
UserWork
class UserWork(AngeeModel)Provision personal queues after IAM user resources load.
Meta
class Meta()Abstract same-row provisioning donor for IAM users.
after_resource_load
@classmethod
def after_resource_load(cls,
instances: Iterable[Any],
*,
tier: str,
source: str,
publish: bool = False) -> NoneEnsure every loaded user has exactly one personal queue.