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()}'