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"