Skip to content
Snippets Groups Projects
Commit e7bac6e4 authored by quido.zientek's avatar quido.zientek
Browse files

Merge branch 'feature/pirati-cz' into feature/pirati-cz-quidecek

parents 07102aee 6702fee4
Branches
No related tags found
3 merge requests!607Pirati.cz,!598Feature/pirati cz quidecek,!575Feature/pirati cz
Pipeline #9546 passed
Showing
with 226 additions and 158 deletions
......@@ -173,6 +173,7 @@ Přes CRON je třeba na pozadí spouštět Django `manage.py` commandy:
* `clearsessions` - maže expirované sessions (denně až týdně)
* `publish_scheduled_pages` - publikuje naplánované stránky (každou hodinu)
* `update_callendars` - stáhne a aktualizuje kalendáře (několikrát denně)
* `update_main_timeline_articles` - aktualizuje články na `pirati.cz` z `https://piratipracuji.cz/api/`
* `update_redmine_issues` - aktualizuje programované body MS a KS stránek napojených na Redmine (několikrát denně)
* `update_tweets` - aktualizuje tweety podle nastavení na Homepage pirati.cz - vyžaduje mít v .env TWITTER_BEARER_TOKEN, parametr --days určuje stáří tweetů (default 1)
......
import json
import logging
from typing import TYPE_CHECKING
import requests
from article_import_utils.utils import create_image_from_url
from main.models import MainArticlePage, MainArticlesPage, ARTICLE_TYPES
if TYPE_CHECKING:
pass
logger = logging.getLogger()
class ArticleDownloadService:
api_url = 'https://piratipracuji.cz/api/'
@staticmethod
def get_existing_articles_slugs() -> list[str]:
return MainArticlePage.objects.filter(article_type=ARTICLE_TYPES.WORK_TIMELINE).values_list('slug', flat=True)
def get_articles_response(self):
response = requests.get(self.api_url)
data = json.loads(response.text)
return data
def perform_update(self):
existing_articles_slug_list = self.get_existing_articles_slugs()
for article in self.get_articles_response():
if article['slug'] not in existing_articles_slug_list:
if 'thumbnail' in article:
img_filename = 'article-' + str(article['id']) + '.jpg'
img_obj = create_image_from_url(url=article['thumbnail'], filename=img_filename)
else:
img_obj = None
article_to_save = MainArticlePage(article_type=ARTICLE_TYPES.WORK_TIMELINE,
title=article['title'],
slug=article['slug'],
perex=article['description'].replace(" ", ""),
author='ČESKÁ PIRÁTSKÁ STRANA',
date=article['start_date'],
image=img_obj,
content=json.dumps([
{'type': 'text',
'value': article['content'].replace("</p>", "</p><br>")}
])
)
parent = MainArticlesPage.objects.all().first()
parent.add_child(instance=article_to_save)
import urllib.request
from wagtail.images.models import Image
from django.core.files import File
def create_image_from_url(url, filename):
img_data = urllib.request.urlretrieve(url)
img_obj = Image(title=filename)
img_obj.file.save(
filename,
File(open(img_data[0], 'rb'))
)
img_obj.save()
return img_obj
......@@ -200,7 +200,8 @@ class OtherLinksBlock(StructBlock):
class SocialLinkBlock(LinkBlock):
icon = CharBlock(label="Ikona", help_text="Seznam ikon - https://styleguide.pirati.cz/latest/?p=viewall-atoms-icons") # TODO CSS class name or somthing better?
icon = CharBlock(label="Ikona", help_text="Seznam ikon - https://styleguide.pirati.cz/latest/?p=viewall-atoms-icons <br/>"
"Název ikony zadejte bez tečky na začátku") # TODO CSS class name or somthing better?
class Meta:
icon = "link"
......
......@@ -23,11 +23,7 @@ class JekyllImportForm(WagtailAdminPageForm):
jekyll_repo_url = forms.URLField(
max_length=512,
required=False,
help_text="V GitHubu tlačítko Code -> a odkaz z Download zip"
"např. 'https://github.com/pirati-web/cb.pirati.cz/archive/refs/heads/gh-pages.zip',"
"pokud máte nainstalovaný Git, zvolte Použít Git a vložte"
"jednoduše URL repozitáře "
"např. 'https://github.com/pirati-web/cb.pirati.cz'",
help_text="např. https://github.com/pirati-web/pirati.cz",
)
readonly_log = forms.CharField(
disabled=True,
......
from django.core.management.base import BaseCommand
from article_import_utils.services import ArticleDownloadService
from main.services import TimelineArticleDownloadService
class Command(BaseCommand):
class Command(BaseCommand):
def handle(self, *args, **options):
ads = ArticleDownloadService()
ads = TimelineArticleDownloadService()
ads.perform_update()
self.stdout.write("\nUpdate of articles finished!")
# Generated by Django 4.0.7 on 2022-08-26 13:17
from django.db import migrations, models
import wagtail.blocks
import wagtail.fields
class Migration(migrations.Migration):
dependencies = [
('main', '0024_mainhomepage_max_items_alter_mainhomepage_content_and_more'),
]
operations = [
migrations.AlterField(
model_name='mainhomepage',
name='social_links',
field=wagtail.fields.StreamField([('social_links', wagtail.blocks.StructBlock([('text', wagtail.blocks.CharBlock(label='Titulek odkazu (text, který se zobrazí místo dlouhého odkazu)')), ('link', wagtail.blocks.URLBlock(label='Odkaz')), ('icon', wagtail.blocks.CharBlock(help_text='Seznam ikon - https://styleguide.pirati.cz/latest/?p=viewall-atoms-icons <br/>Název ikony zadejte bez tečky na začátku', label='Ikona'))]))], blank=True, use_json_field=None, verbose_name='Odkazy na sociální sítě v zápatí webu'),
),
migrations.AlterField(
model_name='mainpersonpage',
name='before_name',
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='Tituly před jménem'),
),
migrations.AlterField(
model_name='mainpersonpage',
name='social_links',
field=wagtail.fields.StreamField([('social_links', wagtail.blocks.StructBlock([('text', wagtail.blocks.CharBlock(label='Titulek odkazu (text, který se zobrazí místo dlouhého odkazu)')), ('link', wagtail.blocks.URLBlock(label='Odkaz')), ('icon', wagtail.blocks.CharBlock(help_text='Seznam ikon - https://styleguide.pirati.cz/latest/?p=viewall-atoms-icons <br/>Název ikony zadejte bez tečky na začátku', label='Ikona'))]))], blank=True, use_json_field=None, verbose_name='Odkazy na sociální sítě'),
),
migrations.AlterField(
model_name='mainpersonpage',
name='twitter_username',
field=models.CharField(blank=True, help_text='Uživatelské jméno zadejte bez @ na začátku', max_length=32, null=True, verbose_name='Uživatelské jméno twitter pro získání příspěvků'),
),
]
......@@ -47,7 +47,6 @@ class ARTICLE_TYPES(models.IntegerChoices):
class MainHomePage(MenuMixin, ExtendedMetadataHomePageMixin, MetadataPageMixin, Page):
max_items = models.IntegerField("Počet tweetů na stránce", default=4)
# header
......@@ -164,14 +163,19 @@ class MainHomePage(MenuMixin, ExtendedMetadataHomePageMixin, MetadataPageMixin,
context["tweet_list"] = tweet_list[:4]
context["show_next_tweet"] = len(tweet_list) > 4
context["regions"] = REGION_CHOICES
context["article_data_list"] = self.get_sorted_article_qs()
return context
@staticmethod
def get_sorted_article_qs():
last_month = timezone.now().today().replace(day=1) - timedelta(days=1)
first_day_of_last_month = last_month.replace(day=1)
sorted_article_qs = MainArticlePage.objects.filter(
date__gt=first_day_of_last_month
).order_by("-date")
context["article_data_list"] = sorted_article_qs[:3]
return context
return sorted_article_qs[:3]
def get_region_response(self, request):
last_month = timezone.now().today().replace(day=1) - timedelta(days=1)
......@@ -515,7 +519,7 @@ class MainPersonPage(ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin,
related_name="+"
)
before_name = models.CharField(
"Tituly před jménem", max_length=16, blank=True, null=True
"Tituly před jménem", max_length=32, blank=True, null=True
)
after_name = models.CharField(
"Tituly za jménem", max_length=16, blank=True, null=True
......@@ -531,6 +535,7 @@ class MainPersonPage(ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin,
blank=True,
null=True,
max_length=32,
help_text="Uživatelské jméno zadejte bez @ na začátku"
)
social_links = StreamField(
......
import json
import logging
from typing import TYPE_CHECKING
import requests
from main.models import ARTICLE_TYPES, MainArticlePage, MainArticlesPage
from shared.utils import create_image_from_url
if TYPE_CHECKING:
pass
logger = logging.getLogger()
class TimelineArticleDownloadService:
api_url = "https://piratipracuji.cz/api/"
@staticmethod
def get_existing_articles_slugs() -> list[str]:
return MainArticlePage.objects.filter(
article_type=ARTICLE_TYPES.WORK_TIMELINE
).values_list("slug", flat=True)
def get_articles_response(self):
response = requests.get(self.api_url)
data = json.loads(response.text)
return data
def perform_update(self):
existing_articles_slug_list = self.get_existing_articles_slugs()
parent = MainArticlesPage.objects.all().first()
if not parent:
RuntimeError("No MainArticlesPage to import to")
for article in self.get_articles_response():
if article["slug"] not in existing_articles_slug_list:
if "thumbnail" in article:
img_filename = "article-" + str(article["id"]) + ".jpg"
img_obj = create_image_from_url(
url=article["thumbnail"], filename=img_filename
)
else:
img_obj = None
article_to_save = MainArticlePage(
article_type=ARTICLE_TYPES.WORK_TIMELINE,
title=article["title"],
slug=article["slug"],
perex=article["description"].replace("&nbsp;", ""),
author="ČESKÁ PIRÁTSKÁ STRANA",
date=article["start_date"],
image=img_obj,
content=json.dumps(
[
{
"type": "text",
"value": article["content"].replace("</p>", "</p><br>"),
}
]
),
)
parent.add_child(instance=article_to_save)
......@@ -2281,6 +2281,18 @@ p{
border-left: 1px solid green;
}
.quote-icon {
font-size: 7rem;
height: 1rem;
}
@media (min-width: 1200px) {
.quote-icon {
font-size: 15rem
}
}
.slick-slider .slick-arrow {
font-size: 0;
position: absolute;
......@@ -2539,7 +2551,7 @@ p{
}
.btn-carousel{
@aplly h-11;
height: 2.75rem;
top: 28%;
}
......@@ -2701,6 +2713,8 @@ p{
display: grid;
gap: 0.5rem;
margin-top: -20px;
grid-template-areas:
"left-article"
"right-article";
......@@ -2726,6 +2740,23 @@ p{
grid-area: timeline;
}
.article-timeline-grid__timeline:before {
content: '';
background: linear-gradient(180deg, rgba(2,0,36,0) 0%, rgba(255,255,255,1) 100%);
position: absolute;
bottom: -1px;
height: 20px;
z-index: 10;
width: 2px;
left: -1px;
}
.article-timeline-grid__timeline .article-timeline--month {
transform: translateX(-50%);
top: -1rem;
z-index: 100;
}
.footer-clip {
-webkit-clip-path: polygon(0 10px, 100% 0, 100% 100%, 0% 100%);
clip-path: polygon(0 10px, 100% 0, 100% 100%, 0% 100%);
......@@ -2778,6 +2809,21 @@ p{
}
}
.footer__main-links ul {
display: grid;
grid-auto-columns: minmax(160px, auto);
grid-auto-flow: column;
grid-gap: 10px;
grid-template-rows: repeat(12, 40px);
}
@media (min-width: 576px) {
.footer__main-links ul {
grid-template-rows: repeat(6, 40px)
}
}
.footer-collapsible__toggle{
display: flex;
cursor: pointer;
......@@ -3245,6 +3291,12 @@ p{
height: 100%;
}
.h-fit{
height: -webkit-fit-content;
height: -moz-fit-content;
height: fit-content;
}
.h-32{
height: 8rem;
}
......@@ -3265,12 +3317,6 @@ p{
height: 3rem;
}
.h-fit{
height: -webkit-fit-content;
height: -moz-fit-content;
height: fit-content;
}
.max-h-20{
max-height: 5rem;
}
......@@ -3335,19 +3381,10 @@ p{
flex-shrink: 0;
}
.transform{
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.resize{
resize: both;
}
.columns-2{
-moz-columns: 2;
columns: 2;
}
.grid-cols-4{
grid-template-columns: repeat(4, minmax(0, 1fr));
}
......@@ -3430,12 +3467,6 @@ p{
margin-bottom: calc(0.5rem * var(--tw-space-y-reverse));
}
.space-y-4 > :not([hidden]) ~ :not([hidden]){
--tw-space-y-reverse: 0;
margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(1rem * var(--tw-space-y-reverse));
}
.space-x-3 > :not([hidden]) ~ :not([hidden]){
--tw-space-x-reverse: 0;
margin-right: calc(0.75rem * var(--tw-space-x-reverse));
......@@ -4198,6 +4229,10 @@ a.icon-link:hover span{
flex-direction: row;
}
.lg\:flex-wrap{
flex-wrap: wrap;
}
.lg\:items-end{
align-items: flex-end;
}
......@@ -4378,11 +4413,6 @@ a.icon-link:hover span{
flex-shrink: 0;
}
.xl\:columns-3{
-moz-columns: 3;
columns: 3;
}
.xl\:flex-row{
flex-direction: row;
}
......@@ -4411,12 +4441,6 @@ a.icon-link:hover span{
gap: 1.25rem;
}
.xl\:space-y-8 > :not([hidden]) ~ :not([hidden]){
--tw-space-y-reverse: 0;
margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(2rem * var(--tw-space-y-reverse));
}
.xl\:justify-self-end{
justify-self: end;
}
......
Source diff could not be displayed: it is too large. Options to address this: view the blob.
......@@ -33,10 +33,10 @@
</a>
</div>
</section>
<section class="footer__main-links text-white lg:flex lg:justify-between gap-8">
<section class="footer__main-links text-white lg:flex lg:flex-wrap lg:justify-between gap-8">
<div class="pt-8 pb-4 lg:py-0">
<ui-footer-collapsible label="Navigace">
<ul class="text-white py-8 space-y-4 xl:space-y-8">
<ul class="text-white py-8">
<li>
<a href="#">Lorem ipsum</a>
</li>
......@@ -51,7 +51,7 @@
</div>
<div class="py-4 lg:py-0">
<ui-footer-collapsible label="Transparence">
<ul class="text-white py-8 space-y-4 xl:space-y-8">
<ul class="text-white py-8">
<li>
<a href="#">Lorem ipsum</a>
</li>
......@@ -66,7 +66,7 @@
</div>
<div class="py-4 lg:py-0">
<ui-footer-collapsible label="osobní stránky">
<ul class="text-white py-8 space-y-4 xl:space-y-8">
<ul class="text-white py-8">
<li>
<a href="#">Lorem ipsum</a>
</li>
......@@ -81,7 +81,7 @@
</div>
<div class="py-4 lg:py-0">
<ui-footer-collapsible label="další projekty">
<ul class="text-white py-8 space-y-4 xl:space-y-8 columns-2 xl:columns-3">
<ul class="text-white py-8">
<li>
<a href="#">Lorem ipsum</a>
</li>
......@@ -129,7 +129,7 @@
</div>
<div class="pt-4 lg:py-0">
<ui-footer-collapsible label="média">
<ul class="text-white py-8 space-y-4 xl:space-y-8">
<ul class="text-white py-8">
<li>
<a href="#">Foto</a>
</li>
......
......@@ -169,8 +169,9 @@
}
}
}
.btn-carousel{
@aplly h-11;
@apply h-11;
top: 28%;
}
......
......@@ -28,6 +28,18 @@
.footer__main--link {
@apply mt-6 w-fit xl:mr-2
}
ul {
display: grid;
grid-auto-columns: minmax(160px, auto);
grid-auto-flow: column;
grid-gap: 10px;
grid-template-rows: repeat(12, 40px);
@screen sm {
grid-template-rows: repeat(6, 40px);
}
}
}
.footer-collapsible__toggle {
......
{% load wagtailcore_tags wagtailimages_tags shared_filters %}
<div class="bg-cover bg-no-repeat section-clip mb-8 py-16 lg:py-36 lg:mb-16"
style="background-image: url('https://i.picsum.photos/id/630/1980/1400.jpg?hmac=WjDo021fzd9SaIlmsi9LtZJApZ02RMzdG0bYLx8iXOo')">
<div class="container--medium mx-auto px-4">
<h2 class="head-7xl text-center mb-6 xl:mb-28">
{{ self.title }}
</h2>
<div class="flex flex-wrap mb-5 lg:mb-10">
<img src="https://i.picsum.photos/id/361/576/576.jpg?hmac=kb62KiZkyDuigb46DUJQd7QvQM6LfYFolTdIMwEqei4" alt=""
class="lg:max-w-lg lg:mr-11">
{% image article_data_list.0.image original as article_img %}
<img src="{{ article_img.url }}" draggable="false" alt="" class="lg:max-w-lg lg:mr-11">
<div class="flex flex-col max-w-xl items-start">
<span class="text-green-500 head-3xl mt-10 mb-4 lg:mb-8">2.3.2022</span>
<h5 class="head-4xl mb-5 lg:mb-10">Senátoři vyzvali k ukončení veřejné podpory firmám z Ruska
a Běloruska, vkteré neodsoudí akt vojenské agrese na Ukrajině</h5>
<p class="leading-6 mb-4 lg:mb-8">
Senát chce v souvislosti s ruskou agresí zabránit některým firmám z Ruska a Běloruska čerpat dotace z
prostředků rozpočtu České republiky.
Plénum Senátu PČR dnes schválilo usnesení Stálé komise Senátu pro dohled nad poskytováním veřejných
prostředků k urychlené tvorbě zákona,
který zamezí poskytování dotací, pobídek, úvěrů a veřejných zakázek z rozpočtu ČR firmám a občanům Ruské
federace a Běloruska,
kteří neodsoudí akt vojenské agrese vůči Ukrajině.
</p>
{% include 'main/includes/button_animated.html' with btn_link=article_page.url btn_text="Číst dále" %}
<span class="text-green-500 head-3xl mt-10 mb-4 lg:mb-8">{{ article_data_list.0.date }}</span>
<h5 class="head-4xl mb-5 lg:mb-10">{{ article_data_list.0.title }}</h5>
<p class="leading-6 mb-4 lg:mb-8">{{ article_data_list.0.perex }}</p>
{% include 'main/includes/button_animated.html' with btn_link=article_data_list.0.url btn_text="Číst dále" %}
</div>
</div>
......
......@@ -26,11 +26,11 @@
</div>
{% endif %}
</section>
<section class="footer__main-links text-white lg:flex gap-8">
<section class="footer__main-links text-white lg:flex lg:flex-wrap lg:justify-between gap-8">
{% for other_link in page.root_page.footer_other_links %}
<div class="{% if forloop.first %}pt-8 pb-4 {% else %}pt-4 {% endif %}lg:py-0">
<ui-footer-collapsible label="{{ other_link.value.title }}">
<ul class="text-white py-8 space-y-4 xl:space-y-8 {% if other_link.value.list|length > 6 and other_link.value.list|length < 12 %}columns-2{% elif other_link.value.list|length > 12 %}xl:columns-3{% endif %}">
<ul class="text-white py-8">
{% for link_list in other_link.value.list %}
<li>
<a href="{{ link_list.link }}">{{ link_list.text }}</a>
......@@ -68,7 +68,9 @@
{% endfor %}
</div>
<div class="max-w-xs">
<span class="text-xs text-grey-350 ">© Piráti, 2022. Všechna práva vyhlazena. Sdílejte a nechte ostatní sdílet za stejných podmínek</span>
<span class="text-xs text-grey-350 ">
© Piráti, 2022. Všechna práva vyhlazena. Sdílejte a nechte ostatní sdílet za stejných podmínek
</span>
</div>
</div>
</section>
......
{% load wagtailimages_tags %}
{% for article_page in article_data_list %}
<div class="flex">
{% image article_page.image fill-128x128 as image %}
<img
src="https://i.picsum.photos/id/523/132/132.jpg?hmac=XjW7KxLZNbta7gMDNvldHNGwA9XyxpjBFUGkJqrMR4o"
alt=""
src='{{ image.url }}' alt=""
class="mr-4 h-32 w-32"
>
<div class="flex flex-col justify-between items-start">
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment