Files

360 lines
10 KiB
Python

from django.core.paginator import Paginator, EmptyPage
from django.db import models
from django.db.models import Q
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
template = "core/scenario_page.html"
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
projects = (
SkeletonProjectPage.objects.live()
.filter(scenarios=self)
.prefetch_related("languages", "scenarios")
.order_by("-first_published_at")
)
paginator = Paginator(projects, 20)
page = request.GET.get("page", "1")
try:
context["projects"] = paginator.page(int(page))
except (ValueError, EmptyPage):
context["projects"] = paginator.page(1)
return context
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
template = "core/project_index_page.html"
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
projects = (
SkeletonProjectPage.objects.live()
.prefetch_related("languages", "scenarios")
.order_by("-first_published_at")
)
language = request.GET.get("language", "")
framework = request.GET.get("framework", "")
database = request.GET.get("database", "")
min_score = request.GET.get("min_score", "")
q = request.GET.get("q", "")
if language:
projects = projects.filter(languages__slug=language)
if framework:
projects = projects.filter(frameworks__slug=framework)
if database:
projects = projects.filter(databases__slug=database)
if q:
projects = projects.filter(
Q(title__icontains=q) | Q(summary__icontains=q)
)
try:
score_val = int(min_score)
if 0 <= score_val <= 30:
projects = [
p for p in projects if p.total_score >= score_val
]
except (ValueError, TypeError):
pass
paginator = Paginator(projects, 20)
page = request.GET.get("page", "1")
try:
context["projects"] = paginator.page(int(page))
except (ValueError, EmptyPage):
context["projects"] = paginator.page(1)
context["current_language"] = language
context["current_framework"] = framework
context["current_database"] = database
context["current_min_score"] = min_score
context["current_q"] = q
context["languages"] = Language.objects.all()
context["frameworks"] = Framework.objects.all()
context["databases"] = DatabaseOption.objects.all()
return context
class Meta:
verbose_name = "Project Index"
class SkeletonProjectPage(Page):
template = "core/skeleton_project_page.html"
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):
template = "core/article_index_page.html"
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):
template = "core/article_page.html"
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"