Skip to content
Snippets Groups Projects
Forked from TO / Maják
1843 commits behind the upstream repository.
models.py 13.00 KiB
from django.db import models
from django.utils.translation import gettext_lazy
from modelcluster.contrib.taggit import ClusterTaggableManager
from modelcluster.fields import ParentalKey
from taggit.models import TaggedItemBase
from wagtail.admin.edit_handlers import (
    FieldPanel,
    HelpPanel,
    InlinePanel,
    MultiFieldPanel,
    ObjectList,
    StreamFieldPanel,
    TabbedInterface,
)
from wagtail.core import blocks
from wagtail.core.fields import RichTextField, StreamField
from wagtail.core.models import Page
from wagtail.images.blocks import ImageChooserBlock
from wagtail.images.edit_handlers import ImageChooserPanel
from wagtailmetadata.models import MetadataPageMixin


class SearchCharacteristic(models.Model):
    """ Ciselnik charakteristik odesilanych uzivatelem, napriklad vek65+ """

    name = models.CharField("Název", max_length=200, blank=False, null=False)

    def __str__(self):
        return self.name


class CharacteristicWeight(models.Model):
    """ Prirazeni vahy jedntlivych charakteristik programovym blokum """

    search_characteristic = models.ForeignKey(
        SearchCharacteristic, on_delete=models.CASCADE, verbose_name="Zadáno"
    )
    program_point = ParentalKey(
        "Program2021PointPage",
        on_delete=models.CASCADE,
        related_name="program_point_link",
    )
    weight = models.IntegerField(verbose_name="Váha")

    panels = [
        FieldPanel("search_characteristic"),
        FieldPanel("weight"),
    ]


class Program2021HomePage(Page):
    ### FIELDS
    headline = models.CharField(
        "název programu",
        max_length=100,
        blank=True,
        null=True,
        help_text="Hlavní titulek domovské stránky",
    )
    perex = models.TextField(
        "perex", max_length=512, blank=True, null=True, help_text="Text pod titulkem",
    )

    ### PANELS

    content_panels = Page.content_panels + [
        MultiFieldPanel(
            [FieldPanel("headline"), FieldPanel("perex"),],
            heading="Vyhledávací formulář",
        ),
    ]

    settings_panels = []

    ### RELATIONS

    subpage_types = ["program2021.Program2021PointPage"]

    ### OTHERS

    class Meta:
        verbose_name = "Program 2021"

    def get_context(self, request):
        context = super().get_context(request)
        if request.method == "POST":

            # odeslany formular vyhledavani v programovych bodech.
            # Shromazdi vsechny tagy; neni-li nejaky ze skupiny zadan, dodej tag "<skupina>-every"
            # Logika: v programovem bodu pak zadas bud tag nepr "gender-male" pro muzska temata
            # nebo "gender-every" pro genderove nespecificka.
            required_tags = []

            # tohle jsou checkboxy, muze jich byt zaskrknuto zadny nebo nekolik
            for x in ["travel-bike", "travel-car", "travel-public"]:
                if request.POST.get(x, ""):
                    required_tags.append(x)
            if not required_tags:
                required_tags.append("travel-every")

            # selecty mohou mit vzdy jen jednu hodnotu
            for x in [
                "age",
                "gender",
                "kids",
                "occupation",
                "wealth",
                "housing",
                "education",
            ]:
                required_tags.append(x + "-" + (request.POST.get(x, "") or "every"))

            # TODO : rozpracovana vec, funguje spatne, vrati vsechny body co maji aspon jeden tag
            # bude treba prevest na vahy tagu
            context["points"] = (
                self.get_children()
                .live()
                .specific()
                .filter(program2021pointpage__search_tags__name__in=required_tags)
                .distinct()
            )
            raise NotImplementedError

        else:
            # neformular - vrat vsechny body
            context["points"] = self.get_children().live().specific()
        return context


class Program2021PointPageTag(TaggedItemBase):
    content_object = ParentalKey(
        "program2021.Program2021PointPage", on_delete=models.CASCADE
    )


class Program2021PointPageSearchTag(TaggedItemBase):
    content_object = ParentalKey(
        "program2021.Program2021PointPage", on_delete=models.CASCADE
    )


class Program2021PointPage(Page, MetadataPageMixin):
    RICH_TEXT_FEATURES = ["bold", "italic", "ol", "ul", "link", "document-link"]

    ### FIELDS
    public_title = models.CharField(
        "veřejný název",
        max_length=46,
        blank=True,
        null=True,
        help_text="Navrhují markeťáci. Max 46 znaků.",
    )
    code = models.CharField(
        "kód",
        max_length=20,
        blank=True,
        null=True,
        help_text="Ideálně jedno nebo dvě slova ke krátkému neformálnímu označení tématu. Max 20 znaků.",
    )
    annotation = models.TextField(
        "anotace",
        max_length=140,
        blank=True,
        null=True,
        help_text="Text anotace by měl jasně prezentovat přínos a být dostatečně konkrétní, a to i když konkrétní parametr může být předmětem podrobnějších analýz a diskuse. Anotace má až na výjimky obsahovat konkrétní číslo reprezentující rozsah problému nebo přínos jeho řešení (v tomto případě ušetření 4 miliard). Tato část bodu společně s veřejným názvem je schvalována republikovým výborem jako závazný program do voleb. Max 140 znaků.",
    )
    problem = models.TextField(
        "problém",
        max_length=280,
        blank=True,
        null=True,
        help_text="Stručné vystižení podstaty problému ve vztahu k celé společnosti a klíčové cílové skupině. Může jít o budoucí hrozbu, náklady na odkládání či dosud nevyužitou příležitost. Max 280 znaků",
    )
    ideal = models.TextField(
        "ideál", max_length=280, blank=True, null=True, help_text="Max 280 znaků."
    )
    benefits_self_employed = models.TextField(
        "benefity OSVČ",
        max_length=250,
        blank=True,
        null=True,
        help_text="Stručný popis dopadu na danou cílovou skupinu. Max 140 znaků.",
    )
    benefits_companies = models.TextField(
        "benefity firmy",
        max_length=250,
        blank=True,
        null=True,
        help_text="Stručný popis dopadu na danou cílovou skupinu. Max 140 znaků.",
    )
    benefits_public_money = models.TextField(
        "benefity veřejné finance",
        max_length=250,
        blank=True,
        null=True,
        help_text="Rozpracuje tým finance. Úkolem finančního týmu není zpracovat to, ale dát k tomu metodiku a metodickou pomoc. V případě žádosti o pomoc se obracejte prosím na finančního analytika poslaneckého klubu Tomáše Kopečného. Max 140 znaků.",
    )
    already_done = RichTextField(
        "co jsme už udělali",
        max_length=1800,
        blank=True,
        null=True,
        help_text="Max 1800 znaků.",
        features=RICH_TEXT_FEATURES,
    )
    proposals = RichTextField(
        "navrhovaná opatření",
        max_length=1800,
        blank=True,
        null=True,
        help_text="Konkrétní (!) body specifikující náš postup v chronologickém pořadí. Max 1800 znaků.",
        features=RICH_TEXT_FEATURES,
    )
    implementation_time = models.CharField(
        "časový horizont realizace",
        max_length=20,
        blank=True,
        null=True,
        help_text="Doba, po které se projekt převede do standardního procesního řízení a lze vyhodnotit přinášený efekt. Jde pouze o realizační fázi, nikoliv celý životní cyklus.",
    )
    faq = StreamField(
        [
            (
                "item",
                blocks.StructBlock(
                    [
                        ("question", blocks.CharBlock(label="otázka")),
                        ("answer", blocks.TextBlock(label="odpověď")),
                    ],
                    label="dotaz",
                ),
            ),
        ],
        verbose_name="často kladené dotazy",
        blank=True,
    )
    sources = RichTextField(
        "zdroje",
        blank=True,
        null=True,
        help_text="Naše vlastní údaje (odhady), které nevycházejí přímo z renomovaného zdroje, dokládáme vlastními analýzami na dané téma, které jsou v seznamu zdrojů a měly by procházet externí oponenturou.",
        features=RICH_TEXT_FEATURES,
    )
    related = StreamField(
        [
            (
                "point",
                blocks.PageChooserBlock(
                    label="programový bod", page_type="program2021.Program2021PointPage"
                ),
            ),
        ],
        verbose_name="související body",
        blank=True,
    )
    image_title = models.ForeignKey(
        "wagtailimages.Image",
        verbose_name="titulní obrázek",
        on_delete=models.PROTECT,
        null=True,
        blank=True,
    )
    images = StreamField(
        [("image", ImageChooserBlock(label="ilustrační obrázek"))],
        verbose_name="ilustrační obrázky",
        blank=True,
    )
    owner_name = models.TextField(
        "jméno garanta (zodpovědné osoby)", max_length=250, blank=True, null=True
    )
    owner_url = models.URLField("odkaz na garanta", blank=True, null=True)
    tags = ClusterTaggableManager(through=Program2021PointPageTag, blank=True)
    search_tags = ClusterTaggableManager(
        "Tagy pro vyhledávání",
        through=Program2021PointPageSearchTag,
        blank=True,
        related_name="search_tags",
        help_text="""Deleny do skupin se spolecnym prefixem, napr. "gender". Pokud je bod spolecny pro vsechny moznosti dane skupiny, pouzij tag se suffixem "-every".
Priklad: chces-li programovy bod pouze pro muze, zadej tag "gender-male", pokud je to jedno, musis zadat tag "gender-every".
Pro kazdou skupinu musis zadat aspon jeden tag, jinak se bod nedostane nikdy do vysledku vyhledavani. Seznam tagu po skupinach:
(gender-male gender-female gender-every)
(age-30 age-50 age-65 age-99 age-every)
(kids-yes kids-no kids-every)
(occupation-student occupation-employee-public occupation-employee-private occupation-entrepreneur occupation-maternity occupation-retired occupation-every)
(wealth-excelent wealth-good wealth-average wealth-bad wealth-every)
(travel-bike travel-car travel-public travel-every)
(housing-metropolis housing-town housing-village housing-every)
(education-zs education-ss education-maturita education-vs education-every)
         """,
    )

    ### PANELS

    content_panels = Page.content_panels + [
        MultiFieldPanel(
            [
                FieldPanel("public_title"),
                FieldPanel("annotation"),
                FieldPanel("tags"),
                FieldPanel("search_tags"),
                FieldPanel("problem"),
                FieldPanel("ideal"),
                FieldPanel("already_done"),
                FieldPanel("proposals"),
                FieldPanel("sources"),
                FieldPanel("implementation_time"),
                FieldPanel("code"),
                ImageChooserPanel("image_title"),
            ],
            heading="základní informace",
        ),
        MultiFieldPanel(
            [FieldPanel("owner_name"), FieldPanel("owner_url")], heading="garant",
        ),
        StreamFieldPanel("images"),
        StreamFieldPanel("related"),
    ]

    benefits_content_panels = [
        HelpPanel(
            "V této části rozpracováváme benefity tak, abychom je mohli souhrnně nabídnout dané cílové skupině. Negativní věci zpracováváme typicky do často kladených otázek dole. U negativ se však vždy prezentuje i související přínos (tzv. balíčkovací zásada, např. snížíme daně z práce díky lepšímu zdanění zisků dnes vyváděných do zahraničí), aby nikdo nemohl takový bod vytrhnout z kontextu a prezentovat proti nám."
        ),
        MultiFieldPanel(
            [
                FieldPanel("benefits_self_employed"),
                FieldPanel("benefits_companies"),
                FieldPanel("benefits_public_money"),
            ]
        ),
    ]

    faq_content_panels = [
        StreamFieldPanel("faq"),
    ]

    search_panels = [InlinePanel("program_point_link", label="Váhy vyhledávání")]

    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("slug"),
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("search_image"),
            ],
            gettext_lazy("Common page configuration"),
        ),
    ]

    edit_handler = TabbedInterface(
        [
            ObjectList(content_panels, heading=gettext_lazy("Content")),
            ObjectList(benefits_content_panels, heading="Benefity"),
            ObjectList(search_panels, heading="Hledání"),
            ObjectList(faq_content_panels, heading="FAQ"),
            ObjectList(promote_panels, heading=gettext_lazy("Promote")),
        ]
    )

    ### RELATIONS

    parent_page_types = ["program2021.Program2021HomePage"]
    subpage_types = []

    ### OTHERS

    class Meta:
        verbose_name = "Programový bod"