From 05feccb1e22ad1ec04252c89961b904a2ab1baca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bedna=C5=99=C3=ADk?= <jan.bednarik@gmail.com> Date: Mon, 31 May 2021 13:43:17 +0200 Subject: [PATCH] elections2021: Export program --- .isort.cfg | 2 +- elections2021/constants.py | 17 ++++ .../management/commands/export_program.py | 77 +++++++++++++++++++ elections2021/models.py | 7 +- .../elections2021/export_program_point.html | 56 ++++++++++++++ requirements/base.in | 1 + requirements/base.txt | 34 +++++++- 7 files changed, 185 insertions(+), 9 deletions(-) create mode 100644 elections2021/management/commands/export_program.py create mode 100644 elections2021/templates/elections2021/export_program_point.html diff --git a/.isort.cfg b/.isort.cfg index 98158d88..fa7bf911 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -4,4 +4,4 @@ line_length = 88 multi_line_output = 3 default_section = "THIRDPARTY" include_trailing_comma = true -known_third_party = arrow,bleach,bs4,captcha,django,environ,faker,ics,markdown,modelcluster,pirates,pytest,pytz,requests,sentry_sdk,snapshottest,taggit,wagtail,wagtailmetadata +known_third_party = arrow,bleach,bs4,captcha,django,environ,faker,ics,markdown,modelcluster,pirates,pytest,pytz,requests,sentry_sdk,snapshottest,taggit,wagtail,wagtailmetadata,weasyprint diff --git a/elections2021/constants.py b/elections2021/constants.py index e05e449f..46602324 100644 --- a/elections2021/constants.py +++ b/elections2021/constants.py @@ -244,6 +244,23 @@ MINISTRY_ARCHETYPES = { MINISTRY_ENVIRONMENT: "prostredi", } +MINISTRY_CODES = { + MINISTRY_TRANSPORT: "transport", + MINISTRY_FINANCES: "finances", + MINISTRY_CULTURE: "culture", + MINISTRY_DEFENSE: "defense", + MINISTRY_SOCIAL: "social", + MINISTRY_COUNTRYSIDE: "countryside", + MINISTRY_BUSINESS: "business", + MINISTRY_JUSTICE: "justice", + MINISTRY_SCHOOLS: "schools", + MINISTRY_INTERIOR: "interior", + MINISTRY_FOREIGN: "foreign", + MINISTRY_HEALTH: "health", + MINISTRY_AGRICULTURE: "agriculture", + MINISTRY_ENVIRONMENT: "environment", +} + BENEFITS = ( # (id, název, pracovní název) ("1", "Pro mladé", "mladí a bezdětní včetně studentů"), diff --git a/elections2021/management/commands/export_program.py b/elections2021/management/commands/export_program.py new file mode 100644 index 00000000..8e8c82ac --- /dev/null +++ b/elections2021/management/commands/export_program.py @@ -0,0 +1,77 @@ +import re + +from django.core.management.base import BaseCommand +from django.template.loader import render_to_string +from django.utils import timezone +from weasyprint import CSS, HTML + +from ...constants import MINISTRY_CHOICES, MINISTRY_CODES +from ...models import Elections2021ProgramPointPage + + +def get_ministry_points(ministry): + weight = f"weight_ministry_{MINISTRY_CODES[ministry]}" + return Elections2021ProgramPointPage.filter_by_weights([weight]) + + +class Command(BaseCommand): + def add_arguments(self, parser): + parser.add_argument("output", type=str, help=".pdf nebo .html soubor") + + def handle(self, *args, **options): + now = timezone.localtime().strftime("%d.%m.%Y %H:%M") + + toc = [] + body = [] + + for ministry, title in MINISTRY_CHOICES: + sub_toc = [] + + body.append( + f'<h2 id="{ministry}" class="ministry-title">Resort: {title}</h2>' + ) + for page in get_ministry_points(ministry): + value = render_to_string( + "elections2021/export_program_point.html", {"page": page} + ) + value = re.sub(r'href="#zdroje"', f'href="#zdroje_{page.id}"', value) + body.append(value) + + sub_toc.append(f'<li><a href="#{page.slug}">{page.title}</a></li>') + + sub_toc = "".join(sub_toc) + toc.append( + f'<li><a href="#{ministry}">{title}</a><br><ul>{sub_toc}</ul></li>' + ) + + style = """ + body { font-family: "Roboto Condensed", Helvetica, sans-serif; line-height: 1.5; } + .ministry-title { page-break-before: always; margin-bottom: 0; line-height: 1; } + .point-title { border-top: 5px double #AAAAAA; padding-top: 0.5rem; margin-top: 3rem; text-transform: uppercase; } + .section-title { border-top: 1px solid #AAAAAA; padding-top: 0.5rem; margin-top: 2rem; text-transform: uppercase; } + sup { font-size: 70%; } + """ + + toc = "".join(toc) + content = ( + [ + f'<html><head><style type="text/css">{style}</style></head><body>', + "<h1>Program koalice Piráti a Starostové</h1>", + f'<div><p>export z webu <a href="https://www.piratiastarostove.cz/">piratiastarostove.cz</a> z {now}</p></div>', + f"<div><p>obsah:</p><p><ul>{toc}</ul></p></div>", + ] + + body + + ["</body></html>"] + ) + + content = "".join(content) + + if options["output"].endswith(".pdf"): + html = HTML(string=content) + css = CSS(string="@page { size: A4; margin: 1cm; }") + html.write_pdf(options["output"], stylesheets=[css]) + elif options["output"].endswith(".html"): + with open(options["output"], "w") as file: + file.write(content) + else: + print("ERROR: Jako výstup zadej soubor .html nebo .pdf") diff --git a/elections2021/models.py b/elections2021/models.py index d680316e..1bcd1116 100644 --- a/elections2021/models.py +++ b/elections2021/models.py @@ -1226,12 +1226,7 @@ class Elections2021ProgramPage( if not ministry: return HttpResponseRedirect(self.url) - title = None - head_photo = None - - if ministry: - title = next(title for key, title in MINISTRY_CHOICES if key == ministry) - head_image = self.get_archetype_image(MINISTRY_ARCHETYPES[ministry]) + title = next(title for key, title in MINISTRY_CHOICES if key == ministry) if ministry == MINISTRY_TRANSPORT: weights = ["weight_ministry_transport"] diff --git a/elections2021/templates/elections2021/export_program_point.html b/elections2021/templates/elections2021/export_program_point.html new file mode 100644 index 00000000..df3f2c56 --- /dev/null +++ b/elections2021/templates/elections2021/export_program_point.html @@ -0,0 +1,56 @@ +{% load wagtailcore_tags wagtailimages_tags elections2021_extras %} +<h2 id="{{ page.slug }}" class="point-title">{{ page.title }}</h2> +<div>{{ page.annotation|richtext|format_sources }}</div> + +<h3 class="section-title">Takhle to dál nejde</h3> +<div>{{ page.problem|richtext|format_sources }}</div> + +<h3 class="section-title">V čem je problém</h3> +<div>{{ page.context|richtext|format_sources }}</div> + +<h3 class="section-title">Naše vize</h3> +<div>{{ page.ideal|richtext|format_sources }}</div> + +<h3 class="section-title">Jak to chceme udělat?</h3> +<div>{{ page.proposal|richtext|format_sources }}</div> + +<h3 class="section-title">Kolik na to chceme času?</h3> +<div> + {% if page.time_horizon_number %} + <p>{{ page.time_horizon_number }} {{ page.time_horizon_unit }}</p> + {% endif %} + {% if page.time_horizon_text %} + <p>{{ page.time_horizon_text|richtext|format_sources }}</p> + {% endif %} +</div> + +<h3 class="section-title">Pro koho to chceme hlavně</h3> +<div> + {% for block in page.benefits_main %} + <h4>{{ block.value.title }}</h4> + <div>{{ block.value.text|richtext|format_sources }}</div> + {% endfor %} + {% for block in page.benefits %} + <h4>{{ block.value.title }}</h4> + <div>{{ block.value.text|richtext|format_sources }}</div> + {% endfor %} +</div> + +{% if page.benefit_for_all %} + <h3 class="section-title">Benefit pro všechny</h3> + <div>{{ page.benefit_for_all|richtext|format_sources }}</div> +{% endif %} + +<h3 class="section-title">Co pro to už děláme</h3> +<div>{{ page.already_done|richtext|format_sources }}</div> + +<h3 class="section-title">Na co se nás často ptáte</h3> +<div> + {% for block in page.faq %} + <h4>{{ block.value.question }}</h4> + <div>{{ block.value.answer|richtext|format_sources }}</div> + {% endfor %} +</div> + +<h3 class="section-title" id="zdroje_{{ page.id }}">Klidně si to ověřte</h3> +<div>{{ page.sources|richtext }}</div> diff --git a/requirements/base.in b/requirements/base.in index a90545dd..06c40912 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -18,3 +18,4 @@ Markdown beautifulsoup4 bleach ipython +weasyprint diff --git a/requirements/base.txt b/requirements/base.txt index 79713326..638e3698 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -22,12 +22,21 @@ beautifulsoup4==4.9.3 # wagtail bleach==3.3.0 # via -r base.in +cairocffi==1.2.0 + # via + # cairosvg + # weasyprint +cairosvg==2.5.2 + # via weasyprint certifi==2020.12.5 # via # requests # sentry-sdk cffi==1.14.5 - # via cryptography + # via + # cairocffi + # cryptography + # weasyprint chardet==4.0.0 # via requests cryptography==3.4.7 @@ -35,8 +44,14 @@ cryptography==3.4.7 # josepy # mozilla-django-oidc # pyopenssl +cssselect2==0.4.1 + # via + # cairosvg + # weasyprint decorator==5.0.8 # via ipython +defusedxml==0.7.1 + # via cairosvg django-environ==0.4.5 # via -r base.in django-extensions==3.1.3 @@ -79,7 +94,9 @@ draftjs-exporter==2.1.7 et-xmlfile==1.1.0 # via openpyxl html5lib==1.1 - # via wagtail + # via + # wagtail + # weasyprint ics==0.7 # via -r base.in idna==2.10 @@ -116,8 +133,10 @@ pickleshare==0.7.5 # via ipython pillow==8.2.0 # via + # cairosvg # django-simple-captcha # wagtail + # weasyprint pirates==0.5.0 # via -r base.in prompt-toolkit==3.0.18 @@ -134,6 +153,8 @@ pyopenssl==20.0.1 # via josepy pyparsing==2.4.7 # via packaging +pyphen==0.10.0 + # via weasyprint python-dateutil==2.8.1 # via # arrow @@ -172,6 +193,11 @@ tatsu==5.6.1 # via ics telepath==0.1.1 # via wagtail +tinycss2==1.1.0 + # via + # cairosvg + # cssselect2 + # weasyprint traitlets==5.0.5 # via # ipython @@ -188,10 +214,14 @@ wagtail==2.13 # wagtail-metadata wcwidth==0.2.5 # via prompt-toolkit +weasyprint==52.5 + # via -r base.in webencodings==0.5.1 # via # bleach + # cssselect2 # html5lib + # tinycss2 whitenoise==5.2.0 # via -r base.in willow==1.4 -- GitLab