108 lines
4.0 KiB
Python
108 lines
4.0 KiB
Python
# Python
|
|
from django.db import models, transaction
|
|
from django_choices_field import TextChoicesField
|
|
|
|
from core.models.base import BaseModel
|
|
from core.models.account import Account, AccountAddress
|
|
from core.models.enums import TaskFrequencyChoices
|
|
from core.models.scope import Scope, Area, Task
|
|
|
|
|
|
class ScopeTemplate(BaseModel):
|
|
"""Reusable blueprint for creating a Scope with Areas and Tasks"""
|
|
name = models.CharField(max_length=255, unique=True)
|
|
description = models.TextField(blank=True)
|
|
is_active = models.BooleanField(default=True)
|
|
|
|
class Meta:
|
|
ordering = ['name']
|
|
verbose_name = "Scope Template"
|
|
verbose_name_plural = "Scope Templates"
|
|
indexes = [models.Index(fields=['is_active'])]
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
@transaction.atomic
|
|
def instantiate(
|
|
self,
|
|
*,
|
|
account: Account,
|
|
account_address: AccountAddress | None = None,
|
|
name: str | None = None,
|
|
description: str | None = None,
|
|
is_active: bool = True,
|
|
) -> Scope:
|
|
"""Create a Scope (and nested Areas/Tasks) from this template."""
|
|
scope = Scope.objects.create(
|
|
name=name or self.name,
|
|
account=account,
|
|
account_address=account_address,
|
|
description=description if description is not None else self.description,
|
|
is_active=is_active,
|
|
)
|
|
|
|
# Create Areas and Tasks preserving order
|
|
area_templates = self.area_templates.all().order_by('order', 'name')
|
|
for at in area_templates:
|
|
area = Area.objects.create(scope=scope, name=at.name, order=at.order)
|
|
task_templates = at.task_templates.all().order_by('order', 'id')
|
|
tasks_to_create = [
|
|
Task(
|
|
area=area,
|
|
description=tt.description,
|
|
checklist_description=tt.checklist_description,
|
|
frequency=tt.frequency,
|
|
order=tt.order,
|
|
is_conditional=tt.is_conditional,
|
|
estimated_minutes=tt.estimated_minutes,
|
|
)
|
|
for tt in task_templates
|
|
]
|
|
if tasks_to_create:
|
|
Task.objects.bulk_create(tasks_to_create)
|
|
|
|
return scope
|
|
|
|
|
|
class AreaTemplate(BaseModel):
|
|
"""Reusable area definition belonging to a ScopeTemplate"""
|
|
scope_template = models.ForeignKey(ScopeTemplate, on_delete=models.CASCADE, related_name='area_templates')
|
|
name = models.CharField(max_length=100)
|
|
order = models.PositiveIntegerField(default=0)
|
|
|
|
class Meta:
|
|
ordering = ['scope_template', 'order', 'name']
|
|
verbose_name = "Area Template"
|
|
verbose_name_plural = "Area Templates"
|
|
indexes = [models.Index(fields=['scope_template', 'order'])]
|
|
|
|
def __str__(self):
|
|
return f"{self.scope_template.name} - {self.name}"
|
|
|
|
|
|
class TaskTemplate(BaseModel):
|
|
"""Reusable task definition belonging to an AreaTemplate"""
|
|
area_template = models.ForeignKey(AreaTemplate, on_delete=models.CASCADE, related_name='task_templates')
|
|
description = models.TextField()
|
|
checklist_description = models.TextField(blank=True)
|
|
frequency = TextChoicesField(
|
|
choices_enum=TaskFrequencyChoices,
|
|
default=TaskFrequencyChoices.AS_NEEDED,
|
|
help_text="How often the task should be performed"
|
|
)
|
|
order = models.PositiveIntegerField(default=0)
|
|
is_conditional = models.BooleanField(default=False, help_text="Task marked 'where applicable'")
|
|
estimated_minutes = models.PositiveIntegerField(null=True, blank=True)
|
|
|
|
class Meta:
|
|
ordering = ['area_template', 'order']
|
|
verbose_name = "Task Template"
|
|
verbose_name_plural = "Task Templates"
|
|
indexes = [
|
|
models.Index(fields=['area_template', 'order']),
|
|
models.Index(fields=['frequency']),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"{self.area_template.name}: {self.description[:50]}..." |