Skip to content

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

python
class QueueManager(GroupManager)

Own personal-queue identity and idempotent provisioning.

personal_slug

python
def personal_slug(user: models.Model) -> str

Return the unique stable slug of user's personal queue.

personal_key

python
def personal_key(user: models.Model) -> str

Return P<pk>, rejecting PKs above 11 digits rather than truncating.

personal_for

python
def personal_for(user: models.Model, *, provision: bool = False) -> Any | None

Return user's personal queue, optionally provisioning it.

provision_personal

python
def provision_personal(user: models.Model) -> Any

Idempotently create one private owner-rostered queue for user.

Queue

python
class Queue(AngeeModel)

An ongoing operational context, materialized as a spaces.Group child.

EstimateScale

python
class EstimateScale(models.TextChoices)

Estimation vocabulary configured per queue.

CycleStartDay

python
class CycleStartDay(models.IntegerChoices)

ISO weekday on which generated cycles start.

Meta

python
class Meta()

Django model options for the materialized queue child.

clean

python
def clean() -> None

Normalize the queue key and validate its explicit default stage.

save

python
def save(*args: Any, **kwargs: Any) -> None

Persist the queue and atomically provision its stage/numbering substrate.

task_sequence_key

python
def task_sequence_key() -> str

Return the stable sequence lookup key for this queue's task numbers.

ensure_task_sequence

python
def ensure_task_sequence() -> models.Model

Return this queue's task-number sequence, creating it if absent.

next_task_number

python
def next_task_number() -> int

Draw the next gapless task number inside the caller's transaction.

Stage

python
class Stage(StagePrimitive, AuditMixin, AngeeDataModel)

One ordered, user-named pipeline stage owned by a queue.

StageCategory

python
class StageCategory(models.TextChoices)

Closed semantic categories projected onto coarse task status.

Meta

python
class Meta(StagePrimitive.Meta)

Django model options for queue stages.

from_db

python
@classmethod
def from_db(cls, db: Any, field_names: Sequence[str],
            values: Sequence[Any]) -> Stage

Load a row and remember facts that identify a system stage.

save

python
def save(*args: Any, **kwargs: Any) -> None

Prevent user creation or renaming of triage/duplicate system stages.

delete

python
def delete(*args: Any, **kwargs: Any) -> tuple[int, dict[str, int]]

Prevent users from deleting the two system-provisioned stages.

CycleManager

python
class CycleManager(AngeeManager)

Own idempotent queue-cadence generation and due-cycle closure.

generate_for_queue

python
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

python
class Cycle(AuditMixin, AngeeDataModel)

One generated, queue-scoped planning window with immutable close evidence.

Meta

python
class Meta()

Django model options for generated queue cycles.

display_name

python
@property
def display_name() -> str

Return the optional custom name or its stable Cycle N fallback.

__str__

python
def __str__() -> str

Return the custom or generated cycle label.

clean

python
def clean() -> None

Reject an inverted generated date window.

save

python
def save(*args: Any, **kwargs: Any) -> None

Persist while keeping close timestamp and snapshot immutable.

close

python
def close(*,
          next_cycle: Any | None = None,
          completed_at: datetime | None = None) -> Cycle

Snapshot 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

python
class TaskWork(StagedModelMixin, AngeeModel)

Same-row work contribution folded into projects.Task.

Meta

python
class Meta()

Abstract donor options folded into the concrete task table.

__init__

python
def __init__(*args: Any, **kwargs: Any) -> None

Track caller-authored status separately from Django row loading.

__setattr__

python
def __setattr__(name: str, value: Any) -> None

Remember direct status assignment while allowing the projection writer.

refresh_from_db

python
def refresh_from_db(*args: Any, **kwargs: Any) -> None

Refresh without misclassifying Django's field hydration as a direct write.

full_clean

python
def full_clean(*args: Any, **kwargs: Any) -> None

Let 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

python
def apply_create_defaults() -> Mapping[str, Sequence[Any]]

Default queue/stage from the actor and return their create relations.

clean

python
def clean() -> None

Project a stage before base lifecycle validation and enforce scope.

save

python
def save(*args: Any, **kwargs: Any) -> None

Persist stage projection and queue numbering in one transaction.

complete

python
def complete() -> Any

Move to the first completed stage, or use the base verb without a queue.

drop

python
def drop(reason: Any) -> Any

Move to duplicate/canceled according to the base dropped reason.

reopen

python
def reopen() -> Any

Move to the queue-owned default stage, or use the base verb without a queue.

accept

python
def accept(stage: models.Model | None = None) -> Any

Leave triage for a same-queue, non-system stage, idempotently.

decline

python
def decline(reason: Any) -> Any

Leave triage for the canceled stage with a closed dropped reason.

snooze

python
def snooze(until: datetime) -> Any

Snooze a triage task until an inclusive instant or new chatter activity.

wake_due_snoozes

python
@classmethod
def wake_due_snoozes(cls, *, now: datetime | None = None) -> int

Clear snoozes whose inclusive wake instant is at or before now.

wake_from_chatter_thread

python
@classmethod
def wake_from_chatter_thread(cls, thread_id: Any) -> int

Clear snooze state on the task whose chatter owns thread_id.

mark_duplicate

python
def mark_duplicate(canonical: models.Model) -> Any

Merge 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

python
@property
def work_key() -> str | None

Return the queue-keyed task number (for example ENG-42).

validate_cycle_scope

python
def validate_cycle_scope() -> None

Reject a cycle outside the task's queue.

UserWork

python
class UserWork(AngeeModel)

Provision personal queues after IAM user resources load.

Meta

python
class Meta()

Abstract same-row provisioning donor for IAM users.

after_resource_load

python
@classmethod
def after_resource_load(cls,
                        instances: Iterable[Any],
                        *,
                        tier: str,
                        source: str,
                        publish: bool = False) -> None

Ensure every loaded user has exactly one personal queue.

Released under the AGPL-3.0 License.