Skip to content
Snippets Groups Projects
Verified Commit 7263a6c7 authored by jindra12's avatar jindra12
Browse files

Merge remote-tracking branch 'origin/test' into features/create-shared-tags

parents 5013eb43 317fec1c
No related branches found
No related tags found
2 merge requests!816Release,!801Prepare basic shared tags
Pipeline #13882 passed
Showing
with 135 additions and 300 deletions
...@@ -178,7 +178,7 @@ Přes CRON je třeba na pozadí spouštět Django `manage.py` commandy: ...@@ -178,7 +178,7 @@ Přes CRON je třeba na pozadí spouštět Django `manage.py` commandy:
* `update_main_timeline_articles` - aktualizuje články na `pirati.cz` z `https://piratipracuji.cz/api/` * `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_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) * `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)
* `update_instagram` - aktualizuje Instagramové posty na Homepage pirati.cz - vyžaduje mít v .env `INSTAGRAM_APP_ID` a `INSTAGRAM_APP_SECRET`. * `update_instagram` - aktualizuje Instagramové posty na Homepage pirati.cz.
### Fulltextové vyhledávání v češtině ### Fulltextové vyhledávání v češtině
......
...@@ -11,11 +11,15 @@ class Command(BaseCommand): ...@@ -11,11 +11,15 @@ class Command(BaseCommand):
def handle(self, *args, **options): def handle(self, *args, **options):
self.stdout.write("Removing orphaned calendars...") self.stdout.write("Removing orphaned calendars...")
for cal in Calendar.objects.filter( for cal in Calendar.objects.filter(
districtcalendarpage=None,
districtcenterpage=None, districtcenterpage=None,
districthomepage=None, districthomepage=None,
districtpersonpage=None,
elections2021calendarpage=None, elections2021calendarpage=None,
mainpersonpage=None,
senatcampaignhomepage=None, senatcampaignhomepage=None,
uniwebhomepage=None, uniwebhomepage=None,
uniwebcalendarpage=None,
): ):
try: try:
self.stdout.write(f"- {cal.id} | {cal.url}") self.stdout.write(f"- {cal.id} | {cal.url}")
......
import json import json
import logging import logging
from datetime import date, timedelta from datetime import timedelta
from pathlib import Path
import arrow import arrow
from django.core.serializers.json import DjangoJSONEncoder from django.core.serializers.json import DjangoJSONEncoder
from django.core.validators import URLValidator, ValidationError from django.core.validators import URLValidator, ValidationError
from django.db import models from django.db import models
from django.utils.timezone import now
from icalevents import icalevents from icalevents import icalevents
from wagtail.admin.panels import FieldPanel
from .parser import process_event_list from .parser import process_event_list
...@@ -69,8 +68,8 @@ class Calendar(models.Model): ...@@ -69,8 +68,8 @@ class Calendar(models.Model):
def update_source(self): def update_source(self):
event_list = icalevents.events( event_list = icalevents.events(
url=self.url, url=self.url,
start=date.today() - timedelta(days=30), start=now() - timedelta(days=30),
end=date.today() + timedelta(days=60), end=now() + timedelta(days=60),
) )
self.handle_event_list(event_list) self.handle_event_list(event_list)
...@@ -131,7 +130,7 @@ class CalendarMixin(models.Model): ...@@ -131,7 +130,7 @@ class CalendarMixin(models.Model):
self.calendar.url = self.calendar_url self.calendar.url = self.calendar_url
self.calendar.save() self.calendar.save()
else: else:
self.calendar = Calendar.objects.create(url=self.calendar_url) self.calendar, _ = Calendar.objects.get_or_create(url=self.calendar_url)
try: try:
self.calendar.update_source() self.calendar.update_source()
......
...@@ -5,6 +5,7 @@ from zoneinfo import ZoneInfo ...@@ -5,6 +5,7 @@ from zoneinfo import ZoneInfo
import arrow import arrow
import bleach import bleach
from django.conf import settings from django.conf import settings
from django.utils.timezone import is_naive
if TYPE_CHECKING: if TYPE_CHECKING:
from icalevents.icalparser import Event from icalevents.icalparser import Event
...@@ -49,12 +50,7 @@ def set_event_duration(event: "Event") -> "Event": ...@@ -49,12 +50,7 @@ def set_event_duration(event: "Event") -> "Event":
def set_event_timezone(event: "Event") -> "Event": def set_event_timezone(event: "Event") -> "Event":
"""Sets default project timezone for event if missing.""" """Sets default project timezone for event if missing."""
if ( if is_naive(event.start) or is_naive(event.end):
not event.start.tzinfo
or not event.start.tzinfo.utcoffset(event.start)
or not event.end.tzinfo
or not event.end.tzinfo.utcoffset(event.end)
):
event.start = event.start.replace(tzinfo=ZoneInfo(settings.TIME_ZONE)) event.start = event.start.replace(tzinfo=ZoneInfo(settings.TIME_ZONE))
event.end = event.end.replace(tzinfo=ZoneInfo(settings.TIME_ZONE)) event.end = event.end.replace(tzinfo=ZoneInfo(settings.TIME_ZONE))
return event return event
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
<!-- Bootstrap CSS --> <!-- Bootstrap CSS -->
<!-- Styles --> <!-- Styles -->
<link rel="stylesheet" href="https://styleguide.pirati.cz/2.13.x/css/styles.css"> <link rel="stylesheet" href="https://styleguide.pirati.cz/2.14.x/css/styles.css">
<link href="{% static "shared/vendor/fancybox/jquery.fancybox.min.css" %}" rel="stylesheet"> <link href="{% static "shared/vendor/fancybox/jquery.fancybox.min.css" %}" rel="stylesheet">
<link rel="stylesheet" href="{% static "shared/vendor/vue-formulate-2.5.3/css/snow.min.css" %}"> <link rel="stylesheet" href="{% static "shared/vendor/vue-formulate-2.5.3/css/snow.min.css" %}">
<link rel="stylesheet" href="{% static "shared/css/helpers.css" %}"> <link rel="stylesheet" href="{% static "shared/css/helpers.css" %}">
...@@ -134,7 +134,7 @@ ...@@ -134,7 +134,7 @@
<div class="btn__body-wrap"> <div class="btn__body-wrap">
<div class="btn__body">Darovat</div> <div class="btn__body">Darovat</div>
<div class="btn__icon"> <div class="btn__icon">
<i class="ico--pig"></i> <i class="ico--donation-full"></i>
</div> </div>
</div> </div>
</a> </a>
...@@ -348,7 +348,7 @@ ...@@ -348,7 +348,7 @@
</footer> </footer>
<script src="{% static "styleguide291/assets/js/vue.2.6.11.js" %}"></script> <script src="{% static "styleguide291/assets/js/vue.2.6.11.js" %}"></script>
<script src="https://styleguide.pirati.cz/2.13.x/js/main.bundle.js"></script> <script src="https://styleguide.pirati.cz/2.14.x/js/main.bundle.js"></script>
<script src="{% static "shared/vendor/jquery/jquery-3.4.1.min.js" %}"></script> <script src="{% static "shared/vendor/jquery/jquery-3.4.1.min.js" %}"></script>
<script src="{% static "shared/vendor/lazysizes/lazysizes.min.js" %}"></script> <script src="{% static "shared/vendor/lazysizes/lazysizes.min.js" %}"></script>
<script src="{% static "shared/vendor/fancybox/jquery.fancybox.min.js" %}"></script> <script src="{% static "shared/vendor/fancybox/jquery.fancybox.min.js" %}"></script>
......
...@@ -6,10 +6,7 @@ from ...services import InstagramDownloadService ...@@ -6,10 +6,7 @@ from ...services import InstagramDownloadService
class Command(BaseCommand): class Command(BaseCommand):
def handle(self, *args, **options): def handle(self, *args, **options):
service = InstagramDownloadService( service = InstagramDownloadService()
app_id=settings.INSTAGRAM_APP_ID,
app_secret=settings.INSTAGRAM_APP_SECRET,
)
service.perform_update() service.perform_update()
self.stdout.write("\nInstagram post update finished.") self.stdout.write("\nInstagram post update finished.")
...@@ -6,6 +6,8 @@ import uuid ...@@ -6,6 +6,8 @@ import uuid
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models
logger = logging.getLogger()
def get_current_datetime() -> datetime.datetime: def get_current_datetime() -> datetime.datetime:
return datetime.datetime.now(tz=datetime.timezone.utc) return datetime.datetime.now(tz=datetime.timezone.utc)
...@@ -90,10 +92,7 @@ class InstagramMixin(models.Model): ...@@ -90,10 +92,7 @@ class InstagramMixin(models.Model):
from .services import InstagramDownloadService from .services import InstagramDownloadService
try: try:
service = InstagramDownloadService( service = InstagramDownloadService()
app_id=settings.INSTAGRAM_APP_ID,
app_secret=settings.INSTAGRAM_APP_SECRET,
)
service.perform_update() service.perform_update()
except Exception: except Exception:
logger.error("Instagram post update failed", exc_info=True) logger.error("Instagram post update failed", exc_info=True)
......
...@@ -5,6 +5,7 @@ import os ...@@ -5,6 +5,7 @@ import os
import instaloader import instaloader
import requests import requests
from django.conf import settings
from django.core.files import File from django.core.files import File
from main.models import MainHomePage, MainPersonPage from main.models import MainHomePage, MainPersonPage
...@@ -15,14 +16,6 @@ logger = logging.getLogger() ...@@ -15,14 +16,6 @@ logger = logging.getLogger()
class InstagramDownloadService: class InstagramDownloadService:
"""
TODO
"""
def __init__(self, app_id: int, app_secret: str):
self.app_id = app_id
self.app_secret = app_secret
def get_usernames(self) -> list[str]: def get_usernames(self) -> list[str]:
access_block = MainHomePage.objects.first().instagram_access access_block = MainHomePage.objects.first().instagram_access
...@@ -52,11 +45,26 @@ class InstagramDownloadService: ...@@ -52,11 +45,26 @@ class InstagramDownloadService:
def parse_media_for_user(self, username: str) -> None: def parse_media_for_user(self, username: str) -> None:
loader = instaloader.Instaloader() loader = instaloader.Instaloader()
if settings.INSTAGRAM_SESSION and settings.INSTAGRAM_USERNAME:
loader.load_session(settings.INSTAGRAM_USERNAME, settings.INSTAGRAM_SESSION)
profile = instaloader.Profile.from_username(loader.context, username) profile = instaloader.Profile.from_username(loader.context, username)
post_position = 0
for remote_post in profile.get_posts(): for remote_post in profile.get_posts():
# Don't recreate existing posts if post_position == 64:
# Don't go past 64 saved posts
return
post_position += 1
if remote_post.is_video:
logger.info(
"Instagram post ID %s is a video, skipping.", remote_post.shortcode
)
# Don't recreate existing posts
if InstagramPost.objects.filter(remote_id=remote_post.shortcode).exists(): if InstagramPost.objects.filter(remote_id=remote_post.shortcode).exists():
logging.info( logging.info(
"Skipping Instagram post ID %s, already exists", "Skipping Instagram post ID %s, already exists",
...@@ -65,12 +73,17 @@ class InstagramDownloadService: ...@@ -65,12 +73,17 @@ class InstagramDownloadService:
continue continue
caption = remote_post.caption
if len(caption) > 255:
caption = caption[:255] + "..."
local_post_instance = InstagramPost( local_post_instance = InstagramPost(
remote_id=remote_post.shortcode, remote_id=remote_post.shortcode,
author_name=profile.full_name, author_name=profile.full_name,
author_username=profile.username, author_username=profile.username,
timestamp=remote_post.date_local, timestamp=remote_post.date_local,
caption=remote_post.caption, caption=caption,
url=f"https://instagram.com/p/{remote_post.shortcode}", url=f"https://instagram.com/p/{remote_post.shortcode}",
) )
...@@ -83,7 +96,7 @@ class InstagramDownloadService: ...@@ -83,7 +96,7 @@ class InstagramDownloadService:
logger.info( logger.info(
"Saved Instagram post ID %s", "Saved Instagram post ID %s",
remote_post.mediaid, remote_post.shortcode,
) )
def perform_update(self) -> None: def perform_update(self) -> None:
......
...@@ -360,27 +360,6 @@ class CardLinkWithHeadlineBlock(CardLinkWithHeadlineBlockMixin): ...@@ -360,27 +360,6 @@ class CardLinkWithHeadlineBlock(CardLinkWithHeadlineBlockMixin):
label = "Karty odkazů s nadpisem" label = "Karty odkazů s nadpisem"
class InstagramAccessBlock(StructBlock):
username = CharBlock(
label="Uživatelské jméno", help_text="Např. pirati.cz, bez @ na začátku!"
)
class Meta:
label = "Synchronizace s Instagramem"
class InstagramPostsBlock(StructBlock):
title = CharBlock(
label="Titulek",
help_text="Instagramové posty budou načteny pro všechny profily uvedené v nastavení webu automaticky",
)
class Meta:
template = "main/blocks/instagram_block.html"
icon = "openquote"
label = "Instagramové posty"
class HoaxBlock(StructBlock): class HoaxBlock(StructBlock):
title = CharBlock(label="Titulek") title = CharBlock(label="Titulek")
hoax = RichTextBlock(label="Hoax") hoax = RichTextBlock(label="Hoax")
......
# Generated by Django 4.1.10 on 2023-07-19 10:30
from django.db import migrations
import main.blocks
import wagtail.blocks
import wagtail.fields
import wagtail.images.blocks
class Migration(migrations.Migration):
dependencies = [
('main', '0057_remove_mainpersonpage_instagram_access_and_more'),
]
operations = [
migrations.RemoveField(
model_name='mainhomepage',
name='instagram_access',
),
migrations.RemoveField(
model_name='mainpersonpage',
name='instagram_username',
),
migrations.AlterField(
model_name='mainhomepage',
name='content',
field=wagtail.fields.StreamField([('carousel', wagtail.blocks.StructBlock([('slides', wagtail.blocks.ListBlock(main.blocks.HomePageCarouseSlideBlock, label='Obrázky s nadpisy - carouselu'))])), ('news', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(help_text='Nejnovější články se načtou automaticky', label='Titulek')), ('image', wagtail.images.blocks.ImageChooserBlock(label='Obrázek pozadí', required=False))])), ('people', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(label='Hlavní titulek')), ('list', wagtail.blocks.ListBlock(main.blocks.BoxBlock, label='Boxíky'))])), ('regions', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(help_text='Články pro regiony se načtou automaticky', label='Titulek')), ('image', wagtail.images.blocks.ImageChooserBlock(label='Obrázek pozadí', required=False))])), ('boxes', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(label='Nadpis')), ('list', wagtail.blocks.ListBlock(main.blocks.BoxBlock, label='Boxíky')), ('image', wagtail.images.blocks.ImageChooserBlock(label='Obrázek pozadí', required=False))]))], blank=True, use_json_field=True, verbose_name='Hlavní obsah'),
),
]
...@@ -28,7 +28,6 @@ from wagtailmetadata.models import MetadataPageMixin ...@@ -28,7 +28,6 @@ from wagtailmetadata.models import MetadataPageMixin
from calendar_utils.models import CalendarMixin from calendar_utils.models import CalendarMixin
from elections2021.constants import REGION_CHOICES # pozor, import ze sousedního modulu from elections2021.constants import REGION_CHOICES # pozor, import ze sousedního modulu
from instagram_utils.models import InstagramMixin, InstagramPost
from shared.forms import SubscribeForm from shared.forms import SubscribeForm
from shared.models import ( # MenuMixin, from shared.models import ( # MenuMixin,
ArticleMixin, ArticleMixin,
...@@ -57,7 +56,6 @@ class MainHomePage( ...@@ -57,7 +56,6 @@ class MainHomePage(
RoutablePageMixin, RoutablePageMixin,
ExtendedMetadataHomePageMixin, ExtendedMetadataHomePageMixin,
MetadataPageMixin, MetadataPageMixin,
InstagramMixin,
ArticlesMixin, ArticlesMixin,
Page, Page,
): ):
...@@ -96,7 +94,6 @@ class MainHomePage( ...@@ -96,7 +94,6 @@ class MainHomePage(
("news", blocks.NewsBlock()), ("news", blocks.NewsBlock()),
("people", blocks.PeopleOverviewBlock()), ("people", blocks.PeopleOverviewBlock()),
("regions", blocks.RegionsBlock()), ("regions", blocks.RegionsBlock()),
("instagram_posts", blocks.InstagramPostsBlock()),
("boxes", blocks.BoxesBlock()), ("boxes", blocks.BoxesBlock()),
], ],
verbose_name="Hlavní obsah", verbose_name="Hlavní obsah",
...@@ -144,14 +141,6 @@ class MainHomePage( ...@@ -144,14 +141,6 @@ class MainHomePage(
use_json_field=True, use_json_field=True,
) )
instagram_access = StreamField(
[("instagram_access", blocks.InstagramAccessBlock())],
verbose_name="Uživatelská jména synchronizovaných Instagram účtů",
blank=True,
max_num=64,
use_json_field=True,
)
content_panels = Page.content_panels + [ content_panels = Page.content_panels + [
FieldPanel("content"), FieldPanel("content"),
*ArticlesMixin.content_panels, *ArticlesMixin.content_panels,
...@@ -169,7 +158,6 @@ class MainHomePage( ...@@ -169,7 +158,6 @@ class MainHomePage(
FieldPanel("donation_page_text"), FieldPanel("donation_page_text"),
FieldPanel("social_links"), FieldPanel("social_links"),
FieldPanel("matomo_id"), FieldPanel("matomo_id"),
FieldPanel("instagram_access"),
] ]
### EDIT HANDLERS ### EDIT HANDLERS
...@@ -214,18 +202,6 @@ class MainHomePage( ...@@ -214,18 +202,6 @@ class MainHomePage(
def get_context(self, request, *args, **kwargs): def get_context(self, request, *args, **kwargs):
context = super().get_context(request, args, kwargs) context = super().get_context(request, args, kwargs)
instagram_username_list = [
access_data["value"]["username"]
for access_data in self.instagram_access.raw_data
]
instagram_post_list = InstagramPost.objects.filter(
author_username__in=instagram_username_list
).order_by("-timestamp")
context["instagram_post_list"] = instagram_post_list[:4]
context["show_next_instagram_post"] = len(instagram_post_list) > 4
context["regions"] = REGION_CHOICES context["regions"] = REGION_CHOICES
context["article_data_list"] = ( context["article_data_list"] = (
...@@ -265,40 +241,10 @@ class MainHomePage( ...@@ -265,40 +241,10 @@ class MainHomePage(
} }
return JsonResponse(data=data, safe=False) return JsonResponse(data=data, safe=False)
def get_instagram_response(self, request):
instagram_username_list = [
access_data["value"]["username"]
for access_data in self.instagram_access.raw_data
]
instagram_post_list_queryset = InstagramPost.objects.filter(
author_username__in=instagram_username_list
).order_by("-timestamp")
instagram_post_paginator = Paginator(instagram_post_list_queryset, 4)
instagram_post_page = instagram_post_paginator.get_page(
request.GET.get("page", 1)
)
context = {"instagram_post_list": instagram_post_page.object_list}
html_content = render(
request, "main/includes/instagram_widget.html", context
).content
data = {
"html": html_content.decode("utf-8"),
"has_next": instagram_post_page.has_next(),
}
return JsonResponse(data=data, safe=False)
def serve(self, request, *args, **kwargs): def serve(self, request, *args, **kwargs):
if request.META.get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest": if request.META.get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest":
if "region" in request.GET: if "region" in request.GET:
return self.get_region_response(request) return self.get_region_response(request)
else:
return self.get_instagram_response(request)
return super().serve(request, *args, **kwargs) return super().serve(request, *args, **kwargs)
...@@ -793,10 +739,6 @@ class MainPersonPage( ...@@ -793,10 +739,6 @@ class MainPersonPage(
perex = models.TextField() perex = models.TextField()
text = RichTextField() text = RichTextField()
instagram_username = models.CharField(
"Uživatelské jméno na Instagramu", max_length=64, blank=True, null=True
)
social_links = StreamField( social_links = StreamField(
[ [
("social_links", blocks.SocialLinkBlock()), ("social_links", blocks.SocialLinkBlock()),
...@@ -835,7 +777,6 @@ class MainPersonPage( ...@@ -835,7 +777,6 @@ class MainPersonPage(
FieldPanel("email"), FieldPanel("email"),
FieldPanel("phone"), FieldPanel("phone"),
FieldPanel("calendar_url"), FieldPanel("calendar_url"),
FieldPanel("instagram_username"),
FieldPanel("social_links"), FieldPanel("social_links"),
FieldPanel("people"), FieldPanel("people"),
] ]
...@@ -843,13 +784,6 @@ class MainPersonPage( ...@@ -843,13 +784,6 @@ class MainPersonPage(
def get_context(self, request) -> dict: def get_context(self, request) -> dict:
context = super().get_context(request) context = super().get_context(request)
if self.instagram_username:
context["instagram_post_list"] = (
InstagramPost.objects.filter(
author_username=self.instagram_username
).order_by("-timestamp")
)[:20]
context["article_page_list"] = MainArticlePage.objects.filter( context["article_page_list"] = MainArticlePage.objects.filter(
author_page=self.id author_page=self.id
) )
......
<div class="container--wide mx-auto mb-8 lg:mb-16">
<div class="flex flex-wrap justify-center items-center">
<h2 class="w-full head-7xl xl:text-center mb-6 xl:mb-28">
{{ self.title }}
</h2>
</div>
<ul class="flex flex-wrap justify-center gap-3" id="instagram-posts-list">
{% include 'main/includes/instagram_widget.html' with instagram_post_list=instagram_post_list %}
</ul>
{% if show_next_instagram_post %}
<div class="flex justify-center mt-8 lg:mt-24">
<a
onclick="showMorePosts(event, this)"
href="#"
data-url="{{ page_url }}?page="
data-page="2"
class="btn btn--black btn--to-yellow-500 btn--hoveractive uppercase"
>
<span class="btn__body-wrap">
<span class="btn__body text-lg lg:text-base">
Zobrazit starší
</span>
</span>
</a>
</div>
{% endif %}
</div>
<script type="text/javascript">
async function showMorePosts(event, btn) {
event.preventDefault()
const postList = document.getElementById("instagram-posts-list");
const url = btn.getAttribute("data-url") + btn.getAttribute("data-page")
const response = await fetch(url, {
method: "GET",
headers: {
"X-Requested-With": "XMLHttpRequest",
},
})
const data = await response.json()
postList.innerHTML += data.html;
if (!data.has_next) { btn.style.display = "none"; }
const dataPage = parseInt(btn.getAttribute("data-page")) + 1
btn.setAttribute("data-page", dataPage)
}
</script>
{% for post in instagram_post_list %}
<li class="flex max-w-sm max-w-xs w-full h-[20rem]">
<a
href="{{ post.url }}"
class="group h-full w-full flex flex-col align-center overflow-hidden text-center border border-grey-100 relative hover:no-underline"
>
<div class="md:min-h-[20rem] p-4 opacity-0 group-focus:opacity-100 group-hover:opacity-100 duration-150 z-10">
<div class="flex flex-col items-center">
<div class="mb-4 flex items-center justify-between xl:flex-col gap-3">
<div class="flex flex-col">
<h5 class="font-alt text-xl mt-3 mb-1 text-left sm:text-center">
{{ post.author_name }}
</h5>
<small class="text-brands-instagram text-left sm:text-center">
@{{ post.author_username }}
</small>
</div>
</div>
<p class="text-small sm:text-base leading-6 mb-2">
{{ post.caption }}
</p>
</div>
</div>
<div class="absolute inset-0 flex-shrink-0 z-0 duration-150 group-focus:blur-lg group-focus:opacity-25 group-hover:blur-lg group-hover:opacity-25">
<div class="relative">
<div class="absolute left-4 top-4 bg-white rounded-lg p-1.5 drop-shadow-md">
<i class="ico--instagram text-brands-instagram text-2xl"></i>
</div>
<img
class="h-[20rem] object-cover"
src="{{ post.image.url }}"
alt="Obrázek v Instagramovém postu, popis „{{ post.caption }}“"
>
</div>
</div>
</a>
</li>
{% endfor %}
...@@ -55,58 +55,6 @@ ...@@ -55,58 +55,6 @@
</div> </div>
</section> </section>
</div> </div>
{% if instagram_post_list %}
<section class="grid-container no-max mr-0 person-instagram-section mb-4 xl:mb-20">
<div class="grid-content-with-right-side">
<h2 class="head-4xl text-left">
Aktuálně na Instagramu
</h2>
<div class="__js-root instagram-carousel-root xl:max-w-[1145px]">
<ui-instagram-carousel>
{% for post in instagram_post_list %}
<div class="flex max-w-sm max-w-xs w-full h-[20rem]">
<a
href="{{ post.url }}"
class="group h-full w-full flex flex-col align-center overflow-hidden text-center border border-grey-100 relative hover:no-underline"
>
<div class="md:min-h-[20rem] p-4 opacity-0 group-focus:opacity-100 group-hover:opacity-100 duration-150 z-10">
<div class="flex flex-col items-center">
<div class="mb-4 flex items-center justify-between xl:flex-col gap-3">
<div class="flex flex-col">
<h5 class="font-alt text-xl mt-3 mb-1 text-left sm:text-center">
{{ post.author_name }}
</h5>
<small class="text-brands-instagram text-left sm:text-center">
@{{ post.author_username }}
</small>
</div>
</div>
<p class="text-small sm:text-base leading-6 mb-2">
{{ post.caption }}
</p>
</div>
</div>
<div class="absolute inset-0 flex-shrink-0 z-0 duration-150 group-focus:blur-lg group-focus:opacity-25 group-hover:blur-lg group-hover:opacity-25">
<div class="relative">
<div class="absolute left-4 top-4 bg-white rounded-lg p-1.5 drop-shadow-md">
<i class="ico--instagram text-brands-instagram text-2xl"></i>
</div>
<img
class="h-[20rem] object-cover"
src="{{ post.image.url }}"
alt="Obrázek v Instagramovém postu, popis „{{ post.caption }}“"
>
</div>
</div>
</a>
</div>
{% endfor %}
</ui-twitter-carousel>
</div>
</div>
</section>
{% endif %} {% endif %}
{% if page.calendar %} {% if page.calendar %}
......
import json
from os.path import join from os.path import join
from pathlib import Path from pathlib import Path
...@@ -47,7 +48,6 @@ INSTALLED_APPS = [ ...@@ -47,7 +48,6 @@ INSTALLED_APPS = [
"calendar_utils", "calendar_utils",
"maps_utils", "maps_utils",
"redmine_utils", "redmine_utils",
"instagram_utils",
"users", "users",
"pirates", "pirates",
"tuning", "tuning",
...@@ -307,6 +307,3 @@ MAPS_UTILS_MAPPROXY_URL = env.str( ...@@ -307,6 +307,3 @@ MAPS_UTILS_MAPPROXY_URL = env.str(
) )
TWITTER_BEARER_TOKEN = env.str("TWITTER_BEARER_TOKEN", default="") TWITTER_BEARER_TOKEN = env.str("TWITTER_BEARER_TOKEN", default="")
INSTAGRAM_APP_ID = env.str("INSTAGRAM_APP_ID", default="")
INSTAGRAM_APP_SECRET = env.str("INSTAGRAM_APP_SECRET", default="")
...@@ -100,7 +100,7 @@ ...@@ -100,7 +100,7 @@
<div class="btn__body-wrap"> <div class="btn__body-wrap">
<div class="btn__body">Darovat</div> <div class="btn__body">Darovat</div>
<div class="btn__icon"> <div class="btn__icon">
<i class="ico--pig"></i> <i class="ico--donation-full"></i>
</div> </div>
</div> </div>
</a> </a>
......
...@@ -21,6 +21,7 @@ def sample_response(): ...@@ -21,6 +21,7 @@ def sample_response():
event.all_day = event_dict["all_day"] event.all_day = event_dict["all_day"]
event.description = event_dict["description"] event.description = event_dict["description"]
event.location = event_dict["location"] event.location = event_dict["location"]
event.url = event_dict["url"]
event_list.append(event) event_list.append(event)
return event_list return event_list
......
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
"all_day": false, "all_day": false,
"summary": "P8 - RR (Redak\u010dn\u00ed rada \u010dasopisu Osmi\u010dka)", "summary": "P8 - RR (Redak\u010dn\u00ed rada \u010dasopisu Osmi\u010dka)",
"description": "kontaktn\u00ed osoba: Martin \u0160t\u011brba (<a href=\"mailto:martin.sterba@pirati.cz\">martin.sterba@pirati.cz</a>)<br><a href=\"https://www.praha8.cz/Komise-Redakcni-rada-casopisu-Osmicka-2018-2022.html\">web</a>", "description": "kontaktn\u00ed osoba: Martin \u0160t\u011brba (<a href=\"mailto:martin.sterba@pirati.cz\">martin.sterba@pirati.cz</a>)<br><a href=\"https://www.praha8.cz/Komise-Redakcni-rada-casopisu-Osmicka-2018-2022.html\">web</a>",
"location": "" "location": "",
"url": ""
}, },
{ {
"start": "2022-05-19T17:00:00+02:00", "start": "2022-05-19T17:00:00+02:00",
...@@ -13,7 +14,8 @@ ...@@ -13,7 +14,8 @@
"all_day": false, "all_day": false,
"summary": "Proch\u00e1zka s architekty - Vize s\u00eddli\u0161t\u011b \u010e\u00e1blice", "summary": "Proch\u00e1zka s architekty - Vize s\u00eddli\u0161t\u011b \u010e\u00e1blice",
"description": "Kr\u00e1sn\u00e9 Kobylisy por\u00e1daji tak\u00e9 proch\u00e1zku po s\u00eddlisti D\u00e1blice, na niz architekti a urbanist\u00e9 predstav\u00ed moznou budouci podobu ieho neizaj\u00edmav\u00e8 sich mist. Akci zakonc\u00ed pr\u00e1telsk\u00e9 posezen\u00ed v pivovaru Cobolis. Bud'te u toho!", "description": "Kr\u00e1sn\u00e9 Kobylisy por\u00e1daji tak\u00e9 proch\u00e1zku po s\u00eddlisti D\u00e1blice, na niz architekti a urbanist\u00e9 predstav\u00ed moznou budouci podobu ieho neizaj\u00edmav\u00e8 sich mist. Akci zakonc\u00ed pr\u00e1telsk\u00e9 posezen\u00ed v pivovaru Cobolis. Bud'te u toho!",
"location": "Kulturn\u00ed d\u016fm L\u00e1dv\u00ed\nBure\u0161ova 2, 182 00 Praha, \u010cesk\u00e1 republika" "location": "Kulturn\u00ed d\u016fm L\u00e1dv\u00ed\nBure\u0161ova 2, 182 00 Praha, \u010cesk\u00e1 republika",
"url": ""
}, },
{ {
"start": "2022-05-23T16:00:00+02:00", "start": "2022-05-23T16:00:00+02:00",
...@@ -21,7 +23,8 @@ ...@@ -21,7 +23,8 @@
"all_day": false, "all_day": false,
"summary": "P8 - RR (Redak\u010dn\u00ed rada \u010dasopisu Osmi\u010dka)", "summary": "P8 - RR (Redak\u010dn\u00ed rada \u010dasopisu Osmi\u010dka)",
"description": "kontaktn\u00ed osoba: Martin \u0160t\u011brba (<a href=\"mailto:martin.sterba@pirati.cz\">martin.sterba@pirati.cz</a>)<br><a href=\"https://www.praha8.cz/Komise-Redakcni-rada-casopisu-Osmicka-2018-2022.html\">web</a>", "description": "kontaktn\u00ed osoba: Martin \u0160t\u011brba (<a href=\"mailto:martin.sterba@pirati.cz\">martin.sterba@pirati.cz</a>)<br><a href=\"https://www.praha8.cz/Komise-Redakcni-rada-casopisu-Osmicka-2018-2022.html\">web</a>",
"location": "" "location": "",
"url": ""
}, },
{ {
"start": "2022-05-24T18:00:00+02:00", "start": "2022-05-24T18:00:00+02:00",
...@@ -29,7 +32,8 @@ ...@@ -29,7 +32,8 @@
"all_day": false, "all_day": false,
"summary": "Beseda o budouc\u00ed podob\u011b s\u00eddli\u0161t\u011b \u010e\u00e1blice", "summary": "Beseda o budouc\u00ed podob\u011b s\u00eddli\u0161t\u011b \u010e\u00e1blice",
"description": "Spolek Kr\u00e1sn\u00e9 Kobylisy a M\u00a2 Praha 8 v\u00e1s prostrednictvim radn\u00edho Tom\u00e1Se Hrebika zvou na besedu v Kulturn\u00edm dom\u00e8 L\u00e1dvi.\nCek\u00e1 s\u00eddlist\u00e9 promysleny rozvoj, nebo prom\u00e9na v betonovou dZungli? Jak\u00e9 se tvori pl\u00e1ny na ochranu s\u00eddlist\u00e9 pred nez\u00e1douci\nvystavbou? Pozv\u00e1n\u00ed prijali odborn\u00edci a architekturu a urbanistiku, z\u00e1stupci M\u00e8stsk\u00e9 c\u00e1sti Praha 8 a Magistr\u00e1tu hlavn\u00edho m\u00e8sta Prahy.", "description": "Spolek Kr\u00e1sn\u00e9 Kobylisy a M\u00a2 Praha 8 v\u00e1s prostrednictvim radn\u00edho Tom\u00e1Se Hrebika zvou na besedu v Kulturn\u00edm dom\u00e8 L\u00e1dvi.\nCek\u00e1 s\u00eddlist\u00e9 promysleny rozvoj, nebo prom\u00e9na v betonovou dZungli? Jak\u00e9 se tvori pl\u00e1ny na ochranu s\u00eddlist\u00e9 pred nez\u00e1douci\nvystavbou? Pozv\u00e1n\u00ed prijali odborn\u00edci a architekturu a urbanistiku, z\u00e1stupci M\u00e8stsk\u00e9 c\u00e1sti Praha 8 a Magistr\u00e1tu hlavn\u00edho m\u00e8sta Prahy.",
"location": "Kulturn\u00ed d\u016fm L\u00e1dv\u00ed\nBure\u0161ova 2, 182 00 Praha, \u010cesk\u00e1 republika" "location": "Kulturn\u00ed d\u016fm L\u00e1dv\u00ed\nBure\u0161ova 2, 182 00 Praha, \u010cesk\u00e1 republika",
"url": ""
}, },
{ {
"start": "2022-05-27T09:30:00+02:00", "start": "2022-05-27T09:30:00+02:00",
...@@ -37,7 +41,8 @@ ...@@ -37,7 +41,8 @@
"all_day": false, "all_day": false,
"summary": "Anthropoid80 - Rekonstrukce atent\u00e1tu", "summary": "Anthropoid80 - Rekonstrukce atent\u00e1tu",
"description": "", "description": "",
"location": "Pam\u00e1tn\u00edk Operace Anthropoid\nV Hole\u0161ovi\u010dk\u00e1ch, 182 00 Praha, \u010cesk\u00e1 republika" "location": "Pam\u00e1tn\u00edk Operace Anthropoid\nV Hole\u0161ovi\u010dk\u00e1ch, 182 00 Praha, \u010cesk\u00e1 republika",
"url": ""
}, },
{ {
"start": "2022-05-27T14:00:00+02:00", "start": "2022-05-27T14:00:00+02:00",
...@@ -45,7 +50,8 @@ ...@@ -45,7 +50,8 @@
"all_day": false, "all_day": false,
"summary": "Anthropoid80", "summary": "Anthropoid80",
"description": "", "description": "",
"location": "Thomayerovy sady\nThomayerovy sady, 180 00 Praha, \u010cesk\u00e1 republika" "location": "Thomayerovy sady\nThomayerovy sady, 180 00 Praha, \u010cesk\u00e1 republika",
"url": ""
}, },
{ {
"start": "2022-05-28T11:00:00+02:00", "start": "2022-05-28T11:00:00+02:00",
...@@ -53,7 +59,8 @@ ...@@ -53,7 +59,8 @@
"all_day": false, "all_day": false,
"summary": "Anthropoid80", "summary": "Anthropoid80",
"description": "", "description": "",
"location": "Thomayerovy sady\nThomayerovy sady, 180 00 Praha, \u010cesk\u00e1 republika" "location": "Thomayerovy sady\nThomayerovy sady, 180 00 Praha, \u010cesk\u00e1 republika",
"url": ""
}, },
{ {
"start": "2022-06-02T18:00:00+02:00", "start": "2022-06-02T18:00:00+02:00",
...@@ -61,7 +68,8 @@ ...@@ -61,7 +68,8 @@
"all_day": false, "all_day": false,
"summary": "Krajsk\u00e1 sch\u016fze pra\u017esk\u00fdch Pir\u00e1t\u016f", "summary": "Krajsk\u00e1 sch\u016fze pra\u017esk\u00fdch Pir\u00e1t\u016f",
"description": "Op\u011bt tu m\u00e1me prvn\u00ed \u010dtvrtek v m\u011bs\u00edci a s n\u00edm pravidelnou sch\u016fzi kraje Praha. Dozv\u00edte se zpr\u00e1vy se Sn\u011bmovny, z Magistr\u00e1tu, z m\u011bstsk\u00fdch \u010d\u00e1st\u00ed, p\u0159edstav\u00ed se z\u00e1jemci o \u010dlenstv\u00ed a z\u00e1jemci o spolupr\u00e1ci s n\u00e1mi v\u016fbec.&nbsp;<br><br>", "description": "Op\u011bt tu m\u00e1me prvn\u00ed \u010dtvrtek v m\u011bs\u00edci a s n\u00edm pravidelnou sch\u016fzi kraje Praha. Dozv\u00edte se zpr\u00e1vy se Sn\u011bmovny, z Magistr\u00e1tu, z m\u011bstsk\u00fdch \u010d\u00e1st\u00ed, p\u0159edstav\u00ed se z\u00e1jemci o \u010dlenstv\u00ed a z\u00e1jemci o spolupr\u00e1ci s n\u00e1mi v\u016fbec.&nbsp;<br><br>",
"location": "Pir\u00e1tsk\u00e9 centrum Praha - PiCe, Na Mor\u00e1ni 360/3, 120 00 Praha-Nov\u00e9 M\u011bsto, \u010cesko" "location": "Pir\u00e1tsk\u00e9 centrum Praha - PiCe, Na Mor\u00e1ni 360/3, 120 00 Praha-Nov\u00e9 M\u011bsto, \u010cesko",
"url": ""
}, },
{ {
"start": "2022-06-06T17:00:00+02:00", "start": "2022-06-06T17:00:00+02:00",
...@@ -69,7 +77,8 @@ ...@@ -69,7 +77,8 @@
"all_day": false, "all_day": false,
"summary": "P8 - KDP (Komise pro dopravu)", "summary": "P8 - KDP (Komise pro dopravu)",
"description": "kontaktn\u00ed osoba: Michal Nov\u00e1k (Michal.Novak@pirati.cz)<br><a href=\"https://www.praha8.cz/Komise-pro-dopravu-2018-2022.html\">web</a>", "description": "kontaktn\u00ed osoba: Michal Nov\u00e1k (Michal.Novak@pirati.cz)<br><a href=\"https://www.praha8.cz/Komise-pro-dopravu-2018-2022.html\">web</a>",
"location": "" "location": "",
"url": ""
}, },
{ {
"start": "2022-06-20T16:00:00+02:00", "start": "2022-06-20T16:00:00+02:00",
...@@ -77,7 +86,8 @@ ...@@ -77,7 +86,8 @@
"all_day": false, "all_day": false,
"summary": "P8 - RR (Redak\u010dn\u00ed rada \u010dasopisu Osmi\u010dka)", "summary": "P8 - RR (Redak\u010dn\u00ed rada \u010dasopisu Osmi\u010dka)",
"description": "kontaktn\u00ed osoba: Martin \u0160t\u011brba (<a href=\"mailto:martin.sterba@pirati.cz\">martin.sterba@pirati.cz</a>)<br><a href=\"https://www.praha8.cz/Komise-Redakcni-rada-casopisu-Osmicka-2018-2022.html\">web</a>", "description": "kontaktn\u00ed osoba: Martin \u0160t\u011brba (<a href=\"mailto:martin.sterba@pirati.cz\">martin.sterba@pirati.cz</a>)<br><a href=\"https://www.praha8.cz/Komise-Redakcni-rada-casopisu-Osmicka-2018-2022.html\">web</a>",
"location": "" "location": "",
"url": ""
}, },
{ {
"start": "2022-06-22T14:00:00+02:00", "start": "2022-06-22T14:00:00+02:00",
...@@ -85,7 +95,8 @@ ...@@ -85,7 +95,8 @@
"all_day": false, "all_day": false,
"summary": "Zastupitelstvo M\u010c Praha 8", "summary": "Zastupitelstvo M\u010c Praha 8",
"description": "https://www.praha8.cz/Harmonogram-a-program-zasedani-zastupitelstva.html", "description": "https://www.praha8.cz/Harmonogram-a-program-zasedani-zastupitelstva.html",
"location": "U Meteoru 6\nPraha, \u010cesk\u00e1 republika" "location": "U Meteoru 6\nPraha, \u010cesk\u00e1 republika",
"url": ""
}, },
{ {
"start": "2022-07-07T18:00:00+02:00", "start": "2022-07-07T18:00:00+02:00",
...@@ -93,6 +104,7 @@ ...@@ -93,6 +104,7 @@
"all_day": false, "all_day": false,
"summary": "Krajsk\u00e1 sch\u016fze pra\u017esk\u00fdch Pir\u00e1t\u016f", "summary": "Krajsk\u00e1 sch\u016fze pra\u017esk\u00fdch Pir\u00e1t\u016f",
"description": "Op\u011bt tu m\u00e1me prvn\u00ed \u010dtvrtek v m\u011bs\u00edci a s n\u00edm pravidelnou sch\u016fzi kraje Praha. Dozv\u00edte se zpr\u00e1vy se Sn\u011bmovny, z Magistr\u00e1tu, z m\u011bstsk\u00fdch \u010d\u00e1st\u00ed, p\u0159edstav\u00ed se z\u00e1jemci o \u010dlenstv\u00ed a z\u00e1jemci o spolupr\u00e1ci s n\u00e1mi v\u016fbec.&nbsp;<br><br>", "description": "Op\u011bt tu m\u00e1me prvn\u00ed \u010dtvrtek v m\u011bs\u00edci a s n\u00edm pravidelnou sch\u016fzi kraje Praha. Dozv\u00edte se zpr\u00e1vy se Sn\u011bmovny, z Magistr\u00e1tu, z m\u011bstsk\u00fdch \u010d\u00e1st\u00ed, p\u0159edstav\u00ed se z\u00e1jemci o \u010dlenstv\u00ed a z\u00e1jemci o spolupr\u00e1ci s n\u00e1mi v\u016fbec.&nbsp;<br><br>",
"location": "Pir\u00e1tsk\u00e9 centrum Praha - PiCe, Na Mor\u00e1ni 360/3, 120 00 Praha-Nov\u00e9 M\u011bsto, \u010cesko" "location": "Pir\u00e1tsk\u00e9 centrum Praha - PiCe, Na Mor\u00e1ni 360/3, 120 00 Praha-Nov\u00e9 M\u011bsto, \u010cesko",
"url": ""
} }
] ]
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
"all_day": false, "all_day": false,
"summary": "P8: Sch\u016fzka MS", "summary": "P8: Sch\u016fzka MS",
"description": "V\u00edce informac\u00ed na: \nhttps://forum.pirati.cz/viewtopic.php?p=807442#p807442", "description": "V\u00edce informac\u00ed na: \nhttps://forum.pirati.cz/viewtopic.php?p=807442#p807442",
"location": "Rezidence ROSA\nSt\u0159elni\u010dn\u00e1 1680/8, 182 00 Praha, \u010cesk\u00e1 republika" "location": "Rezidence ROSA\nSt\u0159elni\u010dn\u00e1 1680/8, 182 00 Praha, \u010cesk\u00e1 republika",
"url": ""
}, },
{ {
"start": "2022-05-09T17:00:00+02:00", "start": "2022-05-09T17:00:00+02:00",
...@@ -13,7 +14,8 @@ ...@@ -13,7 +14,8 @@
"all_day": false, "all_day": false,
"summary": "P8 - KDP (Komise pro dopravu)", "summary": "P8 - KDP (Komise pro dopravu)",
"description": "kontaktn\u00ed osoba: Michal Nov\u00e1k (Michal.Novak@pirati.cz)<br><a href=\"https://www.praha8.cz/Komise-pro-dopravu-2018-2022.html\">web</a>", "description": "kontaktn\u00ed osoba: Michal Nov\u00e1k (Michal.Novak@pirati.cz)<br><a href=\"https://www.praha8.cz/Komise-pro-dopravu-2018-2022.html\">web</a>",
"location": "" "location": "",
"url": ""
}, },
{ {
"start": "2022-05-09T00:00:00+02:00", "start": "2022-05-09T00:00:00+02:00",
...@@ -21,7 +23,8 @@ ...@@ -21,7 +23,8 @@
"all_day": true, "all_day": true,
"summary": "Volba 13.\u201320. pozice - MS Praha 8 (1. kolo)", "summary": "Volba 13.\u201320. pozice - MS Praha 8 (1. kolo)",
"description": "", "description": "",
"location": "" "location": "",
"url": ""
}, },
{ {
"start": "2022-05-06T14:00:00+02:00", "start": "2022-05-06T14:00:00+02:00",
...@@ -29,7 +32,8 @@ ...@@ -29,7 +32,8 @@
"all_day": false, "all_day": false,
"summary": "Pietn\u00ed akt", "summary": "Pietn\u00ed akt",
"description": "https://www.facebook.com/csol.cz/posts/4955450444502712.", "description": "https://www.facebook.com/csol.cz/posts/4955450444502712.",
"location": "Kobylisk\u00e1 st\u0159elnice\nBojasova 13, 182 00 Praha, \u010cesk\u00e1 republika" "location": "Kobylisk\u00e1 st\u0159elnice\nBojasova 13, 182 00 Praha, \u010cesk\u00e1 republika",
"url": ""
}, },
{ {
"start": "2022-05-05T18:00:00+02:00", "start": "2022-05-05T18:00:00+02:00",
...@@ -37,7 +41,8 @@ ...@@ -37,7 +41,8 @@
"all_day": false, "all_day": false,
"summary": "Krajsk\u00e1 sch\u016fze pra\u017esk\u00fdch Pir\u00e1t\u016f", "summary": "Krajsk\u00e1 sch\u016fze pra\u017esk\u00fdch Pir\u00e1t\u016f",
"description": "Op\u011bt tu m\u00e1me prvn\u00ed \u010dtvrtek v m\u011bs\u00edci a s n\u00edm pravidelnou sch\u016fzi kraje Praha. Dozv\u00edte se zpr\u00e1vy se Sn\u011bmovny, z Magistr\u00e1tu, z m\u011bstsk\u00fdch \u010d\u00e1st\u00ed, p\u0159edstav\u00ed se z\u00e1jemci o \u010dlenstv\u00ed a z\u00e1jemci o spolupr\u00e1ci s n\u00e1mi v\u016fbec.&nbsp;<br><br>", "description": "Op\u011bt tu m\u00e1me prvn\u00ed \u010dtvrtek v m\u011bs\u00edci a s n\u00edm pravidelnou sch\u016fzi kraje Praha. Dozv\u00edte se zpr\u00e1vy se Sn\u011bmovny, z Magistr\u00e1tu, z m\u011bstsk\u00fdch \u010d\u00e1st\u00ed, p\u0159edstav\u00ed se z\u00e1jemci o \u010dlenstv\u00ed a z\u00e1jemci o spolupr\u00e1ci s n\u00e1mi v\u016fbec.&nbsp;<br><br>",
"location": "Pir\u00e1tsk\u00e9 centrum Praha - PiCe, Na Mor\u00e1ni 360/3, 120 00 Praha-Nov\u00e9 M\u011bsto, \u010cesko" "location": "Pir\u00e1tsk\u00e9 centrum Praha - PiCe, Na Mor\u00e1ni 360/3, 120 00 Praha-Nov\u00e9 M\u011bsto, \u010cesko",
"url": ""
}, },
{ {
"start": "2022-05-04T16:00:00+02:00", "start": "2022-05-04T16:00:00+02:00",
...@@ -45,7 +50,8 @@ ...@@ -45,7 +50,8 @@
"all_day": false, "all_day": false,
"summary": "P8 - KKV\u010c (Komise pro kulturu a voln\u00fd \u010das)", "summary": "P8 - KKV\u010c (Komise pro kulturu a voln\u00fd \u010das)",
"description": "Sch\u016fze b\u00fdvaj\u00ed: Libe\u0148sk\u00fd z\u00e1mek, m\u00edstnost \u010d. 2, v dob\u011b pandemie online<br><br>kontaktn\u00ed osoba: <a href=\"https://praha8.pirati.cz/lide/martin-stanek/\">Martin Stan\u011bk</a>\u00a0-\u00a0<a href=\"mailto:Martin.Stanek@pirati.cz\">Martin.Stanek@pirati.cz</a>\u00a0- 775058555<br><br><a href=\"https://www.praha8.cz/Komise-pro-kulturu-a-volny-cas-2018-2022.html\">Str\u00e1nka komise na webu m\u011bstsk\u00e9 \u010d\u00e1sti</a><br><br><a href=\"https://forum.pirati.cz/viewtopic.php?f=943&amp;t=46454\">Z\u00e1pisy a jmenn\u00e9 hlasov\u00e1n\u00ed na f\u00f3ru</a>", "description": "Sch\u016fze b\u00fdvaj\u00ed: Libe\u0148sk\u00fd z\u00e1mek, m\u00edstnost \u010d. 2, v dob\u011b pandemie online<br><br>kontaktn\u00ed osoba: <a href=\"https://praha8.pirati.cz/lide/martin-stanek/\">Martin Stan\u011bk</a>\u00a0-\u00a0<a href=\"mailto:Martin.Stanek@pirati.cz\">Martin.Stanek@pirati.cz</a>\u00a0- 775058555<br><br><a href=\"https://www.praha8.cz/Komise-pro-kulturu-a-volny-cas-2018-2022.html\">Str\u00e1nka komise na webu m\u011bstsk\u00e9 \u010d\u00e1sti</a><br><br><a href=\"https://forum.pirati.cz/viewtopic.php?f=943&amp;t=46454\">Z\u00e1pisy a jmenn\u00e9 hlasov\u00e1n\u00ed na f\u00f3ru</a>",
"location": "Libe\u0148sk\u00fd z\u00e1mek, 180 00 Prague-Prague 8, Czechia" "location": "Libe\u0148sk\u00fd z\u00e1mek, 180 00 Prague-Prague 8, Czechia",
"url": ""
}, },
{ {
"start": "2022-04-27T14:00:00+02:00", "start": "2022-04-27T14:00:00+02:00",
...@@ -53,7 +59,8 @@ ...@@ -53,7 +59,8 @@
"all_day": false, "all_day": false,
"summary": "Zastupitelstvo M\u010c Praha 8", "summary": "Zastupitelstvo M\u010c Praha 8",
"description": "https://www.praha8.cz/Harmonogram-a-program-zasedani-zastupitelstva.html", "description": "https://www.praha8.cz/Harmonogram-a-program-zasedani-zastupitelstva.html",
"location": "U Meteoru 6\nPraha, \u010cesk\u00e1 republika" "location": "U Meteoru 6\nPraha, \u010cesk\u00e1 republika",
"url": ""
}, },
{ {
"start": "2022-04-25T00:00:00+02:00", "start": "2022-04-25T00:00:00+02:00",
...@@ -61,7 +68,8 @@ ...@@ -61,7 +68,8 @@
"all_day": true, "all_day": true,
"summary": "Volba m\u00edstop\u0159edsed\u016f MS Praha 8 (1. kolo)", "summary": "Volba m\u00edstop\u0159edsed\u016f MS Praha 8 (1. kolo)",
"description": "", "description": "",
"location": "" "location": "",
"url": ""
}, },
{ {
"start": "2022-04-21T00:00:00+02:00", "start": "2022-04-21T00:00:00+02:00",
...@@ -69,7 +77,8 @@ ...@@ -69,7 +77,8 @@
"all_day": true, "all_day": true,
"summary": "Volba 7.\u201312. pozice - MS Praha 8 (2. kolo)", "summary": "Volba 7.\u201312. pozice - MS Praha 8 (2. kolo)",
"description": "", "description": "",
"location": "" "location": "",
"url": ""
}, },
{ {
"start": "2022-04-20T19:00:00+02:00", "start": "2022-04-20T19:00:00+02:00",
...@@ -77,7 +86,8 @@ ...@@ -77,7 +86,8 @@
"all_day": false, "all_day": false,
"summary": "P8 - Sch\u016fze ZK", "summary": "P8 - Sch\u016fze ZK",
"description": "kontaktn\u00ed osoba: Michal Nov\u00e1k michal.novak@pirati.cz (mailto:michal.novak@pirati.cz)", "description": "kontaktn\u00ed osoba: Michal Nov\u00e1k michal.novak@pirati.cz (mailto:michal.novak@pirati.cz)",
"location": "JITSI" "location": "JITSI",
"url": ""
}, },
{ {
"start": "2022-04-20T18:00:00+02:00", "start": "2022-04-20T18:00:00+02:00",
...@@ -85,7 +95,8 @@ ...@@ -85,7 +95,8 @@
"all_day": false, "all_day": false,
"summary": "Sch\u016fzka k volb\u00e1m (KS Praha)", "summary": "Sch\u016fzka k volb\u00e1m (KS Praha)",
"description": "", "description": "",
"location": "" "location": "",
"url": ""
}, },
{ {
"start": "2022-04-19T16:00:00+02:00", "start": "2022-04-19T16:00:00+02:00",
...@@ -93,7 +104,8 @@ ...@@ -93,7 +104,8 @@
"all_day": false, "all_day": false,
"summary": "P8 - RR (Redak\u010dn\u00ed rada \u010dasopisu Osmi\u010dka)", "summary": "P8 - RR (Redak\u010dn\u00ed rada \u010dasopisu Osmi\u010dka)",
"description": "kontaktn\u00ed osoba: Martin \u0160t\u011brba (<a href=\"mailto:martin.sterba@pirati.cz\">martin.sterba@pirati.cz</a>)<br><a href=\"https://www.praha8.cz/Komise-Redakcni-rada-casopisu-Osmicka-2018-2022.html\">web</a>", "description": "kontaktn\u00ed osoba: Martin \u0160t\u011brba (<a href=\"mailto:martin.sterba@pirati.cz\">martin.sterba@pirati.cz</a>)<br><a href=\"https://www.praha8.cz/Komise-Redakcni-rada-casopisu-Osmicka-2018-2022.html\">web</a>",
"location": "" "location": "",
"url": ""
}, },
{ {
"start": "2022-04-19T00:00:00+02:00", "start": "2022-04-19T00:00:00+02:00",
...@@ -101,7 +113,8 @@ ...@@ -101,7 +113,8 @@
"all_day": true, "all_day": true,
"summary": "Volba 7.\u201312. pozice - MS Praha 8 (1. kolo)", "summary": "Volba 7.\u201312. pozice - MS Praha 8 (1. kolo)",
"description": "", "description": "",
"location": "" "location": "",
"url": ""
}, },
{ {
"start": "2022-04-18T16:00:00+02:00", "start": "2022-04-18T16:00:00+02:00",
...@@ -109,7 +122,8 @@ ...@@ -109,7 +122,8 @@
"all_day": false, "all_day": false,
"summary": "P8 - RR (Redak\u010dn\u00ed rada \u010dasopisu Osmi\u010dka)", "summary": "P8 - RR (Redak\u010dn\u00ed rada \u010dasopisu Osmi\u010dka)",
"description": "kontaktn\u00ed osoba: Martin \u0160t\u011brba (<a href=\"mailto:martin.sterba@pirati.cz\">martin.sterba@pirati.cz</a>)<br><a href=\"https://www.praha8.cz/Komise-Redakcni-rada-casopisu-Osmicka-2018-2022.html\">web</a>", "description": "kontaktn\u00ed osoba: Martin \u0160t\u011brba (<a href=\"mailto:martin.sterba@pirati.cz\">martin.sterba@pirati.cz</a>)<br><a href=\"https://www.praha8.cz/Komise-Redakcni-rada-casopisu-Osmicka-2018-2022.html\">web</a>",
"location": "" "location": "",
"url": ""
}, },
{ {
"start": "2022-04-18T00:00:00+02:00", "start": "2022-04-18T00:00:00+02:00",
...@@ -117,6 +131,7 @@ ...@@ -117,6 +131,7 @@
"all_day": true, "all_day": true,
"summary": "Volba p\u0159edsedy MS Praha 8 (1. kolo)", "summary": "Volba p\u0159edsedy MS Praha 8 (1. kolo)",
"description": "", "description": "",
"location": "" "location": "",
"url": ""
} }
] ]
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment