360 lines
10 KiB
Python
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"
|