ismail c5f76b3855
Some checks are pending
Build and Push Docker Image / build (push) Waiting to run
updates
2026-05-11 14:45:30 +03:00

276 lines
8.0 KiB
Python

from django.conf import settings
from django.db import models
from django.utils.translation import gettext_lazy as _
from apps.core.models import TimeStampedModel, UUIDModel
class PresentationTheme(models.TextChoices):
HEALTHCARE_MODERN = 'healthcare_modern', _('Healthcare Modern')
CORPORATE_NAVY = 'corporate_navy', _('Corporate Navy')
DARK_COMMAND = 'dark_command', _('Dark Command Center')
class SlideLayout(models.TextChoices):
COVER = 'cover', _('Cover')
SECTION_DIVIDER = 'section_divider', _('Section Divider')
KPI_DASHBOARD = 'kpi_dashboard', _('KPI Dashboard')
FULL_CHART = 'full_chart', _('Full Chart')
CHART_METRICS = 'chart_metrics', _('Chart + Metrics')
DATA_TABLE = 'data_table', _('Data Table')
TWO_COLUMN = 'two_column', _('Two Column')
QUOTE = 'quote', _('Quote / Callout')
TIMELINE = 'timeline', _('Timeline')
COMPARISON = 'comparison', _('Comparison')
TEAM_GRID = 'team_grid', _('Team / Department Grid')
CLOSING = 'closing', _('Closing')
class PresentationStatus(models.TextChoices):
DRAFT = 'draft', _('Draft')
PUBLISHED = 'published', _('Published')
ARCHIVED = 'archived', _('Archived')
class Presentation(UUIDModel, TimeStampedModel):
title = models.CharField(max_length=300)
subtitle = models.CharField(max_length=500, blank=True)
description = models.TextField(blank=True)
theme = models.CharField(
max_length=30,
choices=PresentationTheme.choices,
default=PresentationTheme.HEALTHCARE_MODERN,
)
status = models.CharField(
max_length=20,
choices=PresentationStatus.choices,
default=PresentationStatus.DRAFT,
)
presentation_type = models.CharField(
max_length=50,
blank=True,
help_text=_('Type of report (e.g., quarterly, monthly, custom)'),
)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='presentations',
)
hospital = models.ForeignKey(
'organizations.Hospital',
on_delete=models.CASCADE,
related_name='presentations',
null=True,
blank=True,
)
thumbnail = models.ImageField(
upload_to='presentations/thumbnails/',
null=True,
blank=True,
)
presentation_date = models.DateField(null=True, blank=True)
is_shared = models.BooleanField(default=False)
class Meta:
ordering = ['-created_at']
indexes = [
models.Index(fields=['status', '-created_at']),
models.Index(fields=['hospital', '-created_at']),
models.Index(fields=['created_by', '-created_at']),
]
def __str__(self):
return self.title
@property
def slide_count(self):
return self.slides.count()
class Slide(UUIDModel, TimeStampedModel):
presentation = models.ForeignKey(
Presentation,
on_delete=models.CASCADE,
related_name='slides',
)
layout = models.CharField(
max_length=30,
choices=SlideLayout.choices,
default=SlideLayout.COVER,
)
order = models.PositiveIntegerField(default=0)
title = models.CharField(max_length=300, blank=True)
subtitle = models.CharField(max_length=500, blank=True)
content = models.JSONField(
default=dict,
blank=True,
help_text=_(
'Layout-specific content. Structure varies by slide type: '
'kpi_dashboard={metrics:[...]}, '
'full_chart={chart_config:{...}}, '
'data_table={headers:[...], rows:[...]}, etc.'
),
)
background_color = models.CharField(max_length=20, blank=True)
speaker_notes = models.TextField(blank=True)
class Meta:
ordering = ['order']
indexes = [
models.Index(fields=['presentation', 'order']),
]
def __str__(self):
return f'{self.presentation.title} — Slide {self.order}: {self.title or self.get_layout_display()}'
@property
def template_name(self):
return f'presentations/slides/_{self.layout}.html'
class ReportTemplate(UUIDModel, TimeStampedModel):
name = models.CharField(max_length=200)
slug = models.SlugField(unique=True, max_length=220)
description = models.TextField(blank=True)
data_source = models.CharField(
max_length=50,
help_text=_('Key in REPORT_DATA_SOURCES registry'),
)
reference_pdf = models.FileField(
upload_to='report_templates/',
null=True,
blank=True,
)
parsed_structure = models.JSONField(
default=dict,
blank=True,
help_text=_('Raw AI analysis of the reference PDF'),
)
style_config = models.JSONField(
default=dict,
blank=True,
help_text=_('Theme colors, row colors, fonts'),
)
ai_prompt_template = models.TextField(
blank=True,
help_text=_('Prompt template for AI insight generation. Use {{ data_summary }} placeholder.'),
)
active = models.BooleanField(default=True)
hospital = models.ForeignKey(
'organizations.Hospital',
on_delete=models.CASCADE,
related_name='report_templates',
null=True,
blank=True,
help_text=_('Null = available for all hospitals'),
)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='report_templates',
)
class Meta:
ordering = ['name']
indexes = [
models.Index(fields=['active', '-created_at']),
models.Index(fields=['data_source']),
]
def __str__(self):
return self.name
@property
def slide_count(self):
return self.template_slides.count()
class ReportTemplateSlide(UUIDModel, TimeStampedModel):
template = models.ForeignKey(
ReportTemplate,
on_delete=models.CASCADE,
related_name='template_slides',
)
order = models.PositiveIntegerField(default=0)
layout = models.CharField(
max_length=30,
choices=SlideLayout.choices,
default=SlideLayout.COVER,
)
section_label = models.CharField(
max_length=50,
blank=True,
help_text=_('For section dividers: e.g. "01", "02"'),
)
title_template = models.CharField(
max_length=300,
blank=True,
help_text=_('Supports {{ variable }} substitution'),
)
subtitle_template = models.CharField(
max_length=500,
blank=True,
help_text=_('Supports {{ variable }} substitution'),
)
content_mapping = models.JSONField(
default=dict,
blank=True,
help_text=_(
'How data maps to slide content. '
'Structure depends on layout type.'
),
)
repeat_source = models.CharField(
max_length=100,
blank=True,
help_text=_(
'Data key to repeat over, e.g. "by_department". '
'Creates one slide per item.'
),
)
repeat_title_key = models.CharField(
max_length=100,
blank=True,
default='name',
help_text=_('Key in repeat item for slide title, e.g. "department_name"'),
)
repeat_subtitle_template = models.CharField(
max_length=300,
blank=True,
help_text=_('Subtitle template for repeated slides. {{ item.X }} available.'),
)
max_rows = models.PositiveIntegerField(
default=18,
help_text=_('Max data rows per slide (tables split across slides)'),
)
style_overrides = models.JSONField(
default=dict,
blank=True,
help_text=_('Per-slide style overrides (row colors, etc.)'),
)
speaker_notes_template = models.TextField(
blank=True,
help_text=_('Speaker notes template with {{ variable }} support'),
)
class Meta:
ordering = ['order']
indexes = [
models.Index(fields=['template', 'order']),
]
def __str__(self):
return f'{self.template.name} — Slide {self.order}: {self.title_template or self.get_layout_display()}'