from django.db import models from django.core.exceptions import ValidationError from django.core.validators import MinValueValidator, MaxValueValidator from modelcluster.fields import ParentalManyToManyField from wagtail.models import Page from wagtail.admin.panels import FieldPanel, MultiFieldPanel from wagtail.fields import RichTextField from wagtail.snippets.models import register_snippet class ScenarioIndexPage(Page): parent_page_types = ["home.HomePage"] subpage_types = ["core.ScenarioPage"] max_count = 1 content_panels = Page.content_panels class Meta: verbose_name = "Scenario Index" class ScenarioPage(Page): parent_page_types = ["core.ScenarioIndexPage"] subpage_types = [] content_panels = Page.content_panels class Meta: verbose_name = "Scenario" @register_snippet class Language(models.Model): name = models.CharField(max_length=50, unique=True) slug = models.SlugField(max_length=50, unique=True) panels = [ FieldPanel("name"), FieldPanel("slug"), ] class Meta: ordering = ["name"] verbose_name = "Language" verbose_name_plural = "Languages" def __str__(self): return self.name @register_snippet class Framework(models.Model): name = models.CharField(max_length=50, unique=True) slug = models.SlugField(max_length=50, unique=True) panels = [ FieldPanel("name"), FieldPanel("slug"), ] class Meta: ordering = ["name"] verbose_name = "Framework" verbose_name_plural = "Frameworks" def __str__(self): return self.name @register_snippet class DatabaseOption(models.Model): name = models.CharField(max_length=50, unique=True) slug = models.SlugField(max_length=50, unique=True) panels = [ FieldPanel("name"), FieldPanel("slug"), ] class Meta: ordering = ["name"] verbose_name = "Database Option" verbose_name_plural = "Database Options" def __str__(self): return self.name @register_snippet class SkeletonFeature(models.Model): name = models.CharField(max_length=50, unique=True) slug = models.SlugField(max_length=50, unique=True) panels = [ FieldPanel("name"), FieldPanel("slug"), ] class Meta: ordering = ["name"] verbose_name = "Skeleton Feature" verbose_name_plural = "Skeleton Features" def __str__(self): return self.name class ProjectIndexPage(Page): parent_page_types = ["home.HomePage"] subpage_types = ["core.SkeletonProjectPage"] max_count = 1 content_panels = Page.content_panels class Meta: verbose_name = "Project Index" class SkeletonProjectPage(Page): MATURITY_CHOICES = [ ("experimental", "Experimental"), ("stable", "Stable"), ("mature", "Mature"), ] MAINTENANCE_CHOICES = [ ("active", "Active"), ("slow", "Slow"), ("unknown", "Unknown"), ("archived", "Archived"), ] summary = models.TextField() github_url = models.URLField() official_url = models.URLField(blank=True) license_name = models.CharField(max_length=100, blank=True) scenarios = ParentalManyToManyField("core.ScenarioPage", blank=False) languages = ParentalManyToManyField("core.Language", blank=False) frameworks = ParentalManyToManyField("core.Framework", blank=True) databases = ParentalManyToManyField("core.DatabaseOption", blank=True) features = ParentalManyToManyField("core.SkeletonFeature", blank=True) maturity = models.CharField(max_length=20, choices=MATURITY_CHOICES) maintenance_status = models.CharField( max_length=20, choices=MAINTENANCE_CHOICES, default="unknown" ) structure_score = models.IntegerField( validators=[MinValueValidator(0), MaxValueValidator(5)] ) docs_score = models.IntegerField( validators=[MinValueValidator(0), MaxValueValidator(5)] ) tests_score = models.IntegerField( validators=[MinValueValidator(0), MaxValueValidator(5)] ) example_score = models.IntegerField( validators=[MinValueValidator(0), MaxValueValidator(5)] ) dependency_score = models.IntegerField( validators=[MinValueValidator(0), MaxValueValidator(5)] ) incremental_score = models.IntegerField( validators=[MinValueValidator(0), MaxValueValidator(5)] ) recommended_for = RichTextField() not_recommended_for = RichTextField(blank=True) review_notes = RichTextField(blank=True) is_featured = models.BooleanField(default=False) is_sponsored = models.BooleanField(default=False) parent_page_types = ["core.ProjectIndexPage"] subpage_types = [] @property def total_score(self): return ( self.structure_score + self.docs_score + self.tests_score + self.example_score + self.dependency_score + self.incremental_score ) content_panels = Page.content_panels + [ MultiFieldPanel( [ FieldPanel("summary"), FieldPanel("github_url"), FieldPanel("official_url"), FieldPanel("license_name"), ], heading="Basic Info", ), MultiFieldPanel( [ FieldPanel("scenarios"), FieldPanel("languages"), FieldPanel("frameworks"), FieldPanel("databases"), FieldPanel("features"), FieldPanel("maturity"), FieldPanel("maintenance_status"), ], heading="Classification", ), MultiFieldPanel( [ FieldPanel("structure_score"), FieldPanel("docs_score"), FieldPanel("tests_score"), FieldPanel("example_score"), FieldPanel("dependency_score"), FieldPanel("incremental_score"), ], heading="Scores", ), MultiFieldPanel( [ FieldPanel("recommended_for"), FieldPanel("not_recommended_for"), FieldPanel("review_notes"), ], heading="Review Content", ), MultiFieldPanel( [ FieldPanel("is_featured"), FieldPanel("is_sponsored"), ], heading="Display Options", ), ] def clean(self): super().clean() if self.id is not None: errors = {} if not self.scenarios.exists(): errors["scenarios"] = ValidationError( "At least one scenario is required." ) if not self.languages.exists(): errors["languages"] = ValidationError( "At least one language is required." ) if errors: raise ValidationError(errors) class Meta: verbose_name = "Skeleton Project" class ArticleIndexPage(Page): parent_page_types = ["home.HomePage"] subpage_types = ["core.ArticlePage"] max_count = 1 content_panels = Page.content_panels class Meta: verbose_name = "Article Index" class ArticlePage(Page): body = RichTextField() related_projects = ParentalManyToManyField( "core.SkeletonProjectPage", blank=True ) parent_page_types = ["core.ArticleIndexPage"] subpage_types = [] content_panels = Page.content_panels + [ FieldPanel("body"), FieldPanel("related_projects"), ] class Meta: verbose_name = "Article"