from datetime import date, datetime from django.contrib import messages from django.core.mail import EmailMessage from django.db import models from django.shortcuts import render from modelcluster.contrib.taggit import ClusterTaggableManager from modelcluster.fields import ParentalKey, ParentalManyToManyField from taggit.models import TaggedItemBase from wagtail.admin.panels import FieldPanel, HelpPanel, MultiFieldPanel from wagtail.blocks import RichTextBlock from wagtail.contrib.routable_page.models import route from wagtail.fields import RichTextField, StreamField from wagtail.models import Page from wagtailmetadata.models import MetadataPageMixin from shared import blocks as shared_blocks from shared.const import RICH_TEXT_DEFAULT_FEATURES from shared.models import ( # MenuMixin, ArticleMixin, ExtendedMetadataHomePageMixin, ExtendedMetadataPageMixin, MainArticlePageMixin, MainArticlesPageMixin, MainContactPageMixin, MainHomePageMixin, MainPeoplePageMixin, MainPersonPageMixin, MainProgramPageMixin, MainSearchPageMixin, MainSimplePageMixin, PageInMenuMixin, SharedTaggedMainArticle, SubpageMixin, ) from shared.utils import make_promote_panels from . import blocks from .forms import CareerSubmissionForm, MainArticlesPageForm class MainHomePage(MainHomePageMixin): # content content = StreamField( [ ("carousel", blocks.HomePageCarouseSlideBlock()), ( "news", shared_blocks.NewsBlock( template="styleguide2/includes/organisms/articles/articles_section.html" ), ), # ("europarl_news", blocks.EuroparlNewsBlock()), ("people", shared_blocks.PeopleOverviewBlock()), ("regions", blocks.RegionsBlock()), ("boxes", blocks.BoxesBlock()), ], verbose_name="Hlavní obsah", blank=True, use_json_field=True, ) # footer footer_person_list = StreamField( [("person", blocks.PersonContactBlock())], verbose_name="Osoby v zápatí webu", blank=True, max_num=6, use_json_field=True, ) # settings gdpr_and_cookies_page = models.ForeignKey( "main.MainSimplePage", verbose_name="Stránka pro GDPR", on_delete=models.PROTECT, blank=True, null=True, ) subpage_types = [ "main.MainArticlesPage", "main.MainProgramPage", "main.MainPeoplePage", "main.MainPersonPage", "main.MainSimplePage", "main.MainContactPage", "main.MainCrossroadPage", "main.MainHoaxPage", "main.MainSearchPage", "main.MainResultsPage", "main.MainCareersPage", ] ### OTHERS class Meta: verbose_name = "HomePage pirati.cz" @property def careers_page(self): return self._first_subpage_of_type(MainCareersPage) @property def article_page_model(self): return MainArticlePage @property def articles_page_model(self): return MainArticlesPage @property def contact_page_model(self): return MainContactPage @property def search_page_model(self): return MainSearchPage @property def people_page_model(self): return MainPeoplePage @property def root_page(self): return self def _first_subpage_of_type(self, page_type) -> Page or None: try: return self.get_descendants().type(page_type).live().specific()[0] except IndexError: return None @route(r"^sdilene/$", name="shared") def shared(self, request): return self.setup_article_page_context(request) class MainArticleTag(TaggedItemBase): content_object = ParentalKey( "main.MainArticlePage", on_delete=models.CASCADE, related_name="main_tagged_items", ) class MainArticlesPage(MainArticlesPageMixin): base_form_class = MainArticlesPageForm displayed_tags = ParentalManyToManyField( "main.MainArticleTag", verbose_name="Z tohoto webu", related_name="+", blank=True, ) displayed_shared_tags = ParentalManyToManyField( "shared.SharedTag", verbose_name="Sdílecí", related_name="+", blank=True, ) parent_page_types = ["main.MainHomePage"] subpage_types = ["main.MainArticlePage"] class MainArticlePage(MainArticlePageMixin): author_page = models.ForeignKey( "main.MainPersonPage", on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Stránka autora (osoby)", ) tags = ClusterTaggableManager( through=MainArticleTag, related_name="main_tagged_articles", blank=True ) shared_tags = ClusterTaggableManager( verbose_name="Štítky pro sdílení mezi weby", through=SharedTaggedMainArticle, blank=True, ) parent_page_types = ["main.MainArticlesPage"] subpage_types = [] class MainProgramPage(MainProgramPageMixin): ### FIELDS program = StreamField( [ ("program_group", shared_blocks.ProgramGroupBlock()), ("program_group_crossroad", blocks.ProgramGroupBlockCrossroad()), ("program_group_popout", blocks.ProgramGroupBlockPopout()), ("program_group_with_candidates", blocks.ProgramGroupWithCandidatesBlock()), ("elections_program", blocks.ElectionsProgramBlock()), ], verbose_name="Programy", blank=True, use_json_field=True, ) ### RELATIONS parent_page_types = ["main.MainHomePage"] subpage_types = [] class MainPeoplePage(MainPeoplePageMixin): content = StreamField( [ ("people_group", blocks.PeopleGroupBlock(label="Seznam osob")), ("team_group", blocks.TeamBlock()), ], verbose_name="Lidé a týmy", blank=True, use_json_field=True, ) parent_page_types = ["main.MainHomePage"] subpage_types = [ "main.MainPersonPage", "main.MainSimplePage", ] class MainPersonPage(MainPersonPageMixin): ### RELATIONS parent_page_types = ["main.MainPeoplePage"] class MainSimplePage(MainSimplePageMixin): parent_page_types = [ "main.MainHomePage", "main.MainSimplePage", "main.MainCrossroadPage", "main.MainPeoplePage", ] subpage_types = ["main.MainSimplePage"] class MainContactPage(MainContactPageMixin): ### FIELDS contact_people = StreamField( [("item", blocks.PersonContactBlock())], verbose_name="Kontaktní osoby", blank=True, use_json_field=True, ) ### RELATIONS parent_page_types = ["main.MainHomePage"] subpage_types = [] class MainCrossroadPage( ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, PageInMenuMixin, Page ): ### FIELDS headlined_cards_content = StreamField( [(("headlined_cards"), blocks.CardLinkWithHeadlineBlock())], verbose_name="Karty rozcestníku s nadpisem", blank=True, use_json_field=True, ) cards_content = StreamField( [("cards", blocks.CardLinkBlock())], verbose_name="Karty rozcestníku", blank=True, use_json_field=True, ) ### PANELS content_panels = Page.content_panels + [ FieldPanel("headlined_cards_content"), FieldPanel("cards_content"), ] promote_panels = make_promote_panels() settings_panels = [] ### RELATIONS parent_page_types = [ "main.MainHomePage", "main.MainCrossroadPage", ] subpage_types = [ "main.MainSimplePage", "main.MainCrossroadPage", ] ### OTHERS class Meta: verbose_name = "Rozcestník s kartami" class MainHoaxPage( ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, PageInMenuMixin, Page ): ### FIELDS description = RichTextField( "Popis", blank=True, null=True, ) content = StreamField( [(("hoax"), blocks.HoaxBlock())], verbose_name="Hoaxy a jejich vysvětlení", blank=True, use_json_field=True, ) ### PANELS content_panels = Page.content_panels + [ FieldPanel("description"), FieldPanel("content"), ] promote_panels = make_promote_panels() settings_panels = [] ### RELATIONS parent_page_types = ["main.MainHomePage"] subpage_types = [] ### OTHERS class Meta: verbose_name = "Hoaxy" class MainResultsPage( ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, PageInMenuMixin, Page ): ### FIELDS content = StreamField( [ (("flip_cards"), shared_blocks.FlipCardsBlock()), ( "text", RichTextBlock( template="styleguide2/includes/atoms/text/prose_richtext.html" ), ), ], verbose_name="Obsah", blank=True, use_json_field=True, ) ### PANELS content_panels = Page.content_panels + [ FieldPanel("content"), ] parent_page_types = ["main.MainHomePage"] subpage_types = [] class Meta: verbose_name = "Výsledky" class MainCareersPage( ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, PageInMenuMixin, Page ): subheading = models.CharField( verbose_name="Podtitulek", help_text="Text pod hlavním nadpisem stránky", max_length=32, blank=True, null=True, ) perex_col_1 = models.TextField( verbose_name="Perex - první sloupec", blank=True, null=True, ) perex_col_2 = models.TextField( verbose_name="Perex - druhý sloupec", blank=True, null=True, ) content_panels = Page.content_panels + [ HelpPanel( "Pro přidání pracovních nabídek ulož tuto stránku a přidej ji podstránky." ), FieldPanel("subheading"), MultiFieldPanel( [ FieldPanel("perex_col_1", heading="První sloupec"), FieldPanel("perex_col_2", heading="Druhý sloupec"), ], "Perex", ), ] parent_page_types = ["main.MainHomePage"] subpage_types = ["main.MainCareerPage"] def get_career_categories(self) -> list[str]: return ( MainCareerPage.objects.child_of(self) .live() .distinct("category") .values_list("category", flat=True) .order_by("category") .all() ) def get_career_pages(self): return MainCareerPage.objects.child_of(self).live().all() class Meta: verbose_name = "Kariéry" class MainCareerPage( ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, PageInMenuMixin, Page ): recipient_emails = models.CharField( verbose_name="Příjemci emailů o nových přihláškách", help_text="Zadej buď jednu adresu, nebo víc, oddělených čárkami.", blank=False, null=False, ) category = models.CharField( verbose_name="Kategorie pracovní pozice", help_text="Např. 'Koordinátor/ka', 'Programátor/ka', 'Volební manažer/ka', ...", blank=False, null=False, ) location = models.CharField( verbose_name="Místo výkonu práce", help_text="Např. 'Středočeský kraj'", max_length=64, blank=False, null=False, ) time_cost = models.CharField( verbose_name="Časová náročnost", help_text="Např. '8h denně'", max_length=64, blank=False, null=False, ) employment_relationship = models.CharField( verbose_name="Poměr", help_text="Např. 'Rámcová smlouva na dobu určitou'", max_length=128, blank=False, null=False, ) pay_rate = models.CharField( verbose_name="Odměna", help_text="Např. '300-350 Kč/h'", max_length=64, blank=False, null=False, ) submission_end_date = models.DateField( verbose_name="Datum konce přihlášek", blank=False, null=False, ) created_date = models.DateField( verbose_name="Datum vytvoření", blank=False, null=False, default=date.today ) content = RichTextField( "Text nabídky", blank=True, features=RICH_TEXT_DEFAULT_FEATURES ) content_panels = Page.content_panels + [ MultiFieldPanel( [ FieldPanel("submission_end_date"), FieldPanel("created_date"), ], "Datumy", ), FieldPanel("recipient_emails"), FieldPanel("category"), FieldPanel("location"), FieldPanel("time_cost"), FieldPanel("employment_relationship"), FieldPanel("pay_rate"), FieldPanel("content"), ] parent_page_types = ["main.MainCareersPage"] def serve(self, request): form = None current_time = datetime.now() if request.method == "POST": form = CareerSubmissionForm(request.POST, request.FILES) if form.is_valid(): other_files_names = "" for file in form.cleaned_data["other_files"]: other_files_names += f" - {file.name}\n" email = EmailMessage( # Subject f"Nová přihláška k pracovní pozici {self.title} - {form.cleaned_data['name']} {form.cleaned_data['surname']}", # Message ( f""" K pracovní pozici {self.title} se {current_time} přihlásil nový zájemce. Vyplněné údaje: Jméno: {form.cleaned_data['name']} Příjmení: {form.cleaned_data['surname']} E-mail: {form.cleaned_data['email']} Telefon: {form.cleaned_data['phone']} Vlastní text: {form.cleaned_data['own_text'] if form.cleaned_data['own_text'] else '(nevyplněn)'} CV, motivační dopis a ostatní soubory jsou v přílohách. Názvy souborů: CV: {form.cleaned_data["cv_file"].name} Mot. dopis: {form.cleaned_data["cover_letter_file"].name} Ostatní soubory: {other_files_names} Při otevírání souborů buďte opatrní, virový sken neproběhl! """ ), # From email "vyberka@pirati.cz", # Recipient list self.recipient_emails.split(","), ) email.attach( form.cleaned_data["cv_file"].name, form.cleaned_data["cv_file"].read(), form.cleaned_data["cv_file"].content_type, ) email.attach( form.cleaned_data["cover_letter_file"].name, form.cleaned_data["cover_letter_file"].read(), form.cleaned_data["cover_letter_file"].content_type, ) for file in form.cleaned_data["other_files"]: email.attach(file.name, file.read(), file.content_type) sent_successfully = email.send(fail_silently=True) if sent_successfully: messages.add_message( request, messages.SUCCESS, "Přihláška odeslána." ) else: messages.add_message( request, messages.ERROR, "Chyba serveru při odesílání přihlášky.", ) else: messages.add_message( request, messages.ERROR, "Chyba při odeslání přihlášky - prohlížeč odeslal chybná data.", ) else: form = CareerSubmissionForm() return render( request, self.template, { "page": self, "self": self, "form": form, }, ) class Meta: verbose_name = "Pracovní nabídka" class MainSearchPage(MainSearchPageMixin): parent_page_types = ["main.MainHomePage"] subpage_types = [] @property def searchable_models(self) -> list: return [ MainArticlePage, MainPersonPage, MainSimplePage, ]