mirror of
https://github.com/lucaspalomodevelop/eventcally.git
synced 2026-03-13 00:07:22 +00:00
Add sharing options #184
This commit is contained in:
parent
739ca190e2
commit
4419ea1b93
@ -1,10 +1,12 @@
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import icalendar
|
||||
import pytz
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from dateutil.rrule import rrulestr
|
||||
|
||||
berlin_tz = pytz.timezone("Europe/Berlin")
|
||||
gmt_tz = pytz.timezone("GMT")
|
||||
|
||||
|
||||
def get_now():
|
||||
@ -195,3 +197,34 @@ def calculate_occurrences(start_date, date_format, rrule_str, start, batch_size)
|
||||
}
|
||||
|
||||
return {"occurrences": occurrences, "batch": batch_data}
|
||||
|
||||
|
||||
def create_icalendar() -> icalendar.Calendar:
|
||||
cal = icalendar.Calendar()
|
||||
cal.add("prodid", "-//Oveda//oveda.de//")
|
||||
cal.add("version", "2.0")
|
||||
cal.add("x-wr-timezone", berlin_tz.zone)
|
||||
|
||||
tzc = icalendar.Timezone()
|
||||
tzc.add("tzid", berlin_tz.zone)
|
||||
tzc.add("x-lic-location", berlin_tz.zone)
|
||||
|
||||
tzs = icalendar.TimezoneStandard()
|
||||
tzs.add("tzname", "CET")
|
||||
tzs.add("dtstart", datetime(1970, 10, 25, 3, 0, 0))
|
||||
tzs.add("rrule", {"freq": "yearly", "bymonth": 10, "byday": "-1su"})
|
||||
tzs.add("TZOFFSETFROM", timedelta(hours=2))
|
||||
tzs.add("TZOFFSETTO", timedelta(hours=1))
|
||||
|
||||
tzd = icalendar.TimezoneDaylight()
|
||||
tzd.add("tzname", "CEST")
|
||||
tzd.add("dtstart", datetime(1970, 3, 29, 2, 0, 0))
|
||||
tzd.add("rrule", {"freq": "yearly", "bymonth": 3, "byday": "-1su"})
|
||||
tzd.add("TZOFFSETFROM", timedelta(hours=1))
|
||||
tzd.add("TZOFFSETTO", timedelta(hours=2))
|
||||
|
||||
tzc.add_component(tzs)
|
||||
tzc.add_component(tzd)
|
||||
cal.add_component(tzc)
|
||||
|
||||
return cal
|
||||
|
||||
@ -6,6 +6,8 @@ from project.utils import (
|
||||
get_event_category_name,
|
||||
get_localized_enum_name,
|
||||
get_localized_scope,
|
||||
get_location_str,
|
||||
get_place_str,
|
||||
)
|
||||
|
||||
|
||||
@ -36,6 +38,8 @@ app.jinja_env.filters["quote_plus"] = lambda u: quote_plus(u)
|
||||
app.jinja_env.filters["is_list"] = is_list
|
||||
app.jinja_env.filters["any_dict_value_true"] = any_dict_value_true
|
||||
app.jinja_env.filters["ensure_link_scheme"] = lambda s: ensure_link_scheme(s)
|
||||
app.jinja_env.filters["place_str"] = lambda p: get_place_str(p)
|
||||
app.jinja_env.filters["location_str"] = lambda l: get_location_str(l)
|
||||
|
||||
|
||||
@app.context_processor
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
from datetime import datetime
|
||||
|
||||
import icalendar
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from flask import url_for
|
||||
from flask_babelex import format_date, format_time
|
||||
@ -8,7 +9,12 @@ from sqlalchemy.orm import contains_eager, defaultload, joinedload, lazyload
|
||||
from sqlalchemy.sql import extract
|
||||
|
||||
from project import db
|
||||
from project.dateutils import date_add_time, dates_from_recurrence_rule, get_today
|
||||
from project.dateutils import (
|
||||
berlin_tz,
|
||||
date_add_time,
|
||||
dates_from_recurrence_rule,
|
||||
get_today,
|
||||
)
|
||||
from project.models import (
|
||||
AdminUnit,
|
||||
Event,
|
||||
@ -22,7 +28,7 @@ from project.models import (
|
||||
Image,
|
||||
Location,
|
||||
)
|
||||
from project.utils import get_pending_changes
|
||||
from project.utils import get_pending_changes, get_place_str
|
||||
from project.views.utils import truncate
|
||||
|
||||
|
||||
@ -350,3 +356,31 @@ def get_meta_data(event: Event, event_date: EventDate = None) -> dict:
|
||||
meta["image"] = url_for("image", id=event.photo_id, _external=True)
|
||||
|
||||
return meta
|
||||
|
||||
|
||||
def create_ical_event_for_date(event_date: EventDate) -> icalendar.Event:
|
||||
url = url_for("event_date", id=event_date.id, _external=True)
|
||||
|
||||
event = icalendar.Event()
|
||||
event.add("summary", event_date.event.name)
|
||||
event.add("url", url)
|
||||
event.add("description", url)
|
||||
event.add("uid", url)
|
||||
event.add("dtstart", event_date.start.astimezone(berlin_tz))
|
||||
|
||||
if event_date.end:
|
||||
event.add("dtend", event_date.end.astimezone(berlin_tz))
|
||||
|
||||
if event_date.event.created_at:
|
||||
event.add("dtstamp", event_date.event.created_at)
|
||||
|
||||
if event_date.event.updated_at:
|
||||
event.add("last-modified", event_date.event.updated_at)
|
||||
|
||||
if (
|
||||
event_date.event.attendance_mode
|
||||
and event_date.event.attendance_mode != EventAttendanceMode.online
|
||||
):
|
||||
event.add("location", get_place_str(event_date.event.event_place))
|
||||
|
||||
return event
|
||||
|
||||
@ -194,6 +194,16 @@ $( function() {
|
||||
return false;
|
||||
});
|
||||
|
||||
$("#copy_input_button").click(function () {
|
||||
$("#copy_input").select();
|
||||
document.execCommand("copy");
|
||||
$(this).tooltip('show');
|
||||
});
|
||||
|
||||
$('#copy_input_button').mouseleave(function () {
|
||||
$(this).tooltip('hide');
|
||||
});
|
||||
|
||||
$("#geolocation_btn").click(function () {
|
||||
if ("geolocation" in navigator){
|
||||
navigator.geolocation.getCurrentPosition(function(position){
|
||||
|
||||
@ -89,21 +89,8 @@
|
||||
{{ organizer.name }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_location(location) %}
|
||||
{%- if location.street -%}
|
||||
{{ location.street }}, {{ location.postalCode }} {{ location.city }}
|
||||
{%- elif location.postalCode or location.city -%}
|
||||
{{ location.postalCode }} {{ location.city }}
|
||||
{%- endif -%}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_place(place) %}
|
||||
{%- if place.location -%}
|
||||
{{ place.name }}, {{render_location(place.location)}}
|
||||
{%- else -%}
|
||||
{{ place.name }}
|
||||
{%- endif -%}
|
||||
{% endmacro %}
|
||||
{% macro render_location(location) %}{{ location | location_str }}{% endmacro %}
|
||||
{% macro render_place(place) %}{{ place | place_str }}{% endmacro %}
|
||||
|
||||
{% macro render_events_sub_menu() %}
|
||||
{% endmacro %}
|
||||
@ -511,7 +498,7 @@
|
||||
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_event_props_seo(event, start, end, dates = None, show_rating = False, show_admin_unit = True, user_rights=None) %}
|
||||
{% macro render_event_props_seo(event, start, end, dates = None, show_rating = False, show_admin_unit = True, user_rights=None, share_links=None, calendar_links=None) %}
|
||||
<div class="w-normal mx-auto">
|
||||
|
||||
{% if event.photo_id %}
|
||||
@ -563,7 +550,20 @@
|
||||
<div class="mt-4" style="white-space:pre-wrap;">{{ event.description|urlize(nofollow=True, target='_blank', rel="nofollow") }}</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="small mt-4">
|
||||
{% if share_links or calendar_links %}
|
||||
<div class="mt-4">
|
||||
{% if share_links %}
|
||||
<button type="button" class="btn btn-outline-secondary mr-1 mb-1" data-toggle="modal" data-target="#shareModal"><i class="fa fa-share-alt"></i> {{ _('Share') }}</button>
|
||||
{{ render_share_modal(share_links) }}
|
||||
{% endif %}
|
||||
{% if calendar_links %}
|
||||
<button type="button" class="btn btn-outline-secondary mb-1" data-toggle="modal" data-target="#calendarExportModal"><i class="fa fa-calendar"></i> {{ _('Add to calendar') }}</button>
|
||||
{{ render_calendar_export_modal(calendar_links) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="small mt-2">
|
||||
{{ render_audit(event, show_rating) }}
|
||||
</div>
|
||||
</div>
|
||||
@ -588,7 +588,7 @@
|
||||
|
||||
{% if event.attendance_mode and event.attendance_mode.value != 2 %}
|
||||
<div class="mt-2">
|
||||
<a href="http://www.google.com/maps?q={{ render_place(event.event_place) | quote_plus }}" class="btn btn-secondary" target="_blank" rel="noopener noreferrer">{{ _('Show directions') }}</a>
|
||||
<a href="http://www.google.com/maps?q={{ render_place(event.event_place) | quote_plus }}" class="btn btn-outline-secondary" target="_blank" rel="noopener noreferrer"><i class="fa fa-directions"></i> {{ _('Show directions') }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@ -1248,4 +1248,58 @@ if (URL) {
|
||||
{% endif %}
|
||||
|
||||
</style>
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_share_modal(share_links) %}
|
||||
<div class="modal fade" id="shareModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{{ _('Share event') }}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="input-group mb-3">
|
||||
<input id="copy_input" type="text" class="form-control" value="{{ share_links["url"] }}" />
|
||||
<div class="input-group-append">
|
||||
<button id="copy_input_button" class="btn btn-outline-secondary" type="button" data-toggle="tooltip" data-trigger="manual" data-title="{{ _('Link copied') }}">{{ _('Copy link') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group">
|
||||
<a class="list-group-item list-group-item-action" href="{{ share_links["facebook"] }}" target="_blank" rel="noopener noreferrer"><i class="fab fa-facebook"></i> Facebook</a>
|
||||
<a class="list-group-item list-group-item-action" href="{{ share_links["twitter"] }}" target="_blank" rel="noopener noreferrer"><i class="fab fa-twitter"></i> Twitter</a>
|
||||
<a class="list-group-item list-group-item-action" href="{{ share_links["email"] }}" target="_blank" rel="noopener noreferrer"><i class="fa fa-envelope"></i> {{ _('Email') }}</a>
|
||||
<a class="list-group-item list-group-item-action" href="{{ share_links["whatsapp"] }}" target="_blank" rel="noopener noreferrer" data-action="share/whatsapp/share"><i class="fab fa-whatsapp"></i> Whatsapp</a>
|
||||
<a class="list-group-item list-group-item-action" href="{{ share_links["telegram"] }}" target="_blank" rel="noopener noreferrer"><i class="fab fa-telegram"></i> Telegram</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_calendar_export_modal(calendar_links) %}
|
||||
<div class="modal fade" id="calendarExportModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{{ _('Add to calendar') }}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="list-group">
|
||||
<a class="list-group-item list-group-item-action" href="{{ calendar_links["ics"] }}"><i class="fab fa-microsoft"></i> Outlook</a>
|
||||
<a class="list-group-item list-group-item-action" href="{{ calendar_links["google"] }}" target="_blank" rel="noopener noreferrer"><i class="fab fa-google"></i> {{ _('Google calendar') }}</a>
|
||||
<a class="list-group-item list-group-item-action" href="{{ calendar_links["ics"] }}"><i class="fab fa-apple"></i> {{ _('Apple calendar') }}</a>
|
||||
<a class="list-group-item list-group-item-action" href="{{ calendar_links["ics"] }}"><i class="fab fa-yahoo"></i> {{ _('Yahoo calendar') }}</a>
|
||||
<a class="list-group-item list-group-item-action" href="{{ calendar_links["ics"] }}"><i class="fa fa-calendar"></i> {{ _('Other calendar') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{% extends "layout.html" %}
|
||||
{% from "_macros.html" import render_event_props %}
|
||||
{% from "_macros.html" import render_share_modal %}
|
||||
{%- block title -%}
|
||||
{{ _('Actions for event') }}
|
||||
{%- endblock -%}
|
||||
@ -28,6 +28,12 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="list-group mt-4">
|
||||
<button type="button" class="list-group-item list-group-item-action" data-toggle="modal" data-target="#shareModal"><i class="fa fa-share-alt"></i> {{ _('Share event') }}</button>
|
||||
</div>
|
||||
|
||||
{{ render_share_modal(share_links) }}
|
||||
|
||||
<div class="list-group mt-4">
|
||||
{% if user_rights['can_duplicate_event'] %}
|
||||
<a class="list-group-item list-group-item-action" href="{{ url_for('event_create_for_admin_unit_id', id=event.admin_unit_id, template_id=event.id) }}"><i class="fa fa-copy"></i> {{ _('Duplicate event') }}</a>
|
||||
@ -42,7 +48,6 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@ -6,6 +6,6 @@
|
||||
{% block content_container_attribs %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{{ render_event_props_seo(event, event.start, event.end, dates, user_rights['can_update_event'], user_rights=user_rights) }}
|
||||
{{ render_event_props_seo(event, event.start, event.end, dates, user_rights['can_update_event'], user_rights=user_rights, share_links=share_links) }}
|
||||
|
||||
{% endblock %}
|
||||
@ -7,6 +7,6 @@
|
||||
{% block content_container_attribs %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{{ render_event_props_seo(event, event_date.start, event_date.end, dates, user_rights=user_rights) }}
|
||||
{{ render_event_props_seo(event, event_date.start, event_date.end, dates, user_rights=user_rights, share_links=share_links, calendar_links=calendar_links) }}
|
||||
|
||||
{% endblock %}
|
||||
@ -20,6 +20,29 @@ def get_localized_scope(scope: str) -> str:
|
||||
return lazy_gettext(loc_key)
|
||||
|
||||
|
||||
def get_location_str(location) -> str:
|
||||
if not location:
|
||||
return ""
|
||||
|
||||
if location.street and not location.street.isspace():
|
||||
return f"{location.street}, {location.postalCode} {location.city}"
|
||||
|
||||
if location.postalCode or location.city:
|
||||
return f"{location.postalCode} {location.city}".strip()
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
def get_place_str(place) -> str:
|
||||
if not place:
|
||||
return ""
|
||||
|
||||
if place.location:
|
||||
return f"{place.name}, {get_location_str(place.location)}"
|
||||
|
||||
return place.name
|
||||
|
||||
|
||||
def make_dir(path):
|
||||
try:
|
||||
original_umask = os.umask(0)
|
||||
|
||||
@ -43,6 +43,7 @@ from project.views.event_suggestion import send_event_suggestion_review_status_m
|
||||
from project.views.utils import (
|
||||
flash_errors,
|
||||
flash_message,
|
||||
get_share_links,
|
||||
handleSqlError,
|
||||
non_match_for_deletion,
|
||||
send_mail,
|
||||
@ -54,6 +55,8 @@ def event(event_id):
|
||||
event = get_event_with_details_or_404(event_id)
|
||||
user_rights = get_menu_user_rights(event)
|
||||
dates = get_upcoming_event_dates(event.id)
|
||||
url = url_for("event", event_id=event_id, _external=True)
|
||||
share_links = get_share_links(url, event.name)
|
||||
|
||||
structured_datas = list()
|
||||
for event_date in dates:
|
||||
@ -70,6 +73,7 @@ def event(event_id):
|
||||
meta=get_meta_data(event),
|
||||
user_rights=user_rights,
|
||||
canonical_url=url_for("event", event_id=event_id, _external=True),
|
||||
share_links=share_links,
|
||||
)
|
||||
|
||||
|
||||
@ -77,8 +81,15 @@ def event(event_id):
|
||||
def event_actions(event_id):
|
||||
event = Event.query.get_or_404(event_id)
|
||||
user_rights = get_user_rights(event)
|
||||
url = url_for("event", event_id=event_id, _external=True)
|
||||
share_links = get_share_links(url, event.name)
|
||||
|
||||
return render_template("event/actions.html", event=event, user_rights=user_rights)
|
||||
return render_template(
|
||||
"event/actions.html",
|
||||
event=event,
|
||||
user_rights=user_rights,
|
||||
share_links=share_links,
|
||||
)
|
||||
|
||||
|
||||
@app.route("/admin_unit/<int:id>/events/create", methods=("GET", "POST"))
|
||||
|
||||
@ -1,18 +1,26 @@
|
||||
import json
|
||||
|
||||
from flask import redirect, render_template, request, url_for
|
||||
from flask.wrappers import Response
|
||||
|
||||
from project import app
|
||||
from project.dateutils import create_icalendar
|
||||
from project.forms.event_date import FindEventDateForm
|
||||
from project.jsonld import DateTimeEncoder, get_sd_for_event_date
|
||||
from project.services.event import (
|
||||
create_ical_event_for_date,
|
||||
get_event_date_with_details_or_404,
|
||||
get_meta_data,
|
||||
get_upcoming_event_dates,
|
||||
)
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.views.event import get_event_category_choices, get_menu_user_rights
|
||||
from project.views.utils import flash_errors, track_analytics
|
||||
from project.views.utils import (
|
||||
flash_errors,
|
||||
get_calendar_links,
|
||||
get_share_links,
|
||||
track_analytics,
|
||||
)
|
||||
|
||||
|
||||
def prepare_event_date_form(form):
|
||||
@ -53,6 +61,10 @@ def event_date(id):
|
||||
get_sd_for_event_date(event_date), indent=2, cls=DateTimeEncoder
|
||||
)
|
||||
|
||||
url = url_for("event_date", id=id, _external=True)
|
||||
share_links = get_share_links(url, event_date.event.name)
|
||||
calendar_links = get_calendar_links(event_date)
|
||||
|
||||
return render_template(
|
||||
"event_date/read.html",
|
||||
event_date=event_date,
|
||||
@ -61,4 +73,21 @@ def event_date(id):
|
||||
canonical_url=url_for("event_date", id=id, _external=True),
|
||||
user_rights=get_menu_user_rights(event_date.event),
|
||||
dates=get_upcoming_event_dates(event_date.event_id),
|
||||
share_links=share_links,
|
||||
calendar_links=calendar_links,
|
||||
)
|
||||
|
||||
|
||||
@app.route("/eventdate/<int:id>/ical")
|
||||
def event_date_ical(id):
|
||||
event_date = get_event_date_with_details_or_404(id)
|
||||
event = create_ical_event_for_date(event_date)
|
||||
|
||||
cal = create_icalendar()
|
||||
cal.add_component(event)
|
||||
|
||||
return Response(
|
||||
cal.to_ical(),
|
||||
mimetype="text/calendar",
|
||||
headers={"Content-disposition": f"attachment; filename=eventdate_{id}.ics"},
|
||||
)
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
from flask import Markup, flash, redirect, render_template, request, url_for
|
||||
from flask_babelex import gettext
|
||||
from flask_mail import Message
|
||||
@ -5,7 +7,9 @@ from psycopg2.errorcodes import UNIQUE_VIOLATION
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from project import app, db, mail
|
||||
from project.models import Analytics
|
||||
from project.dateutils import gmt_tz
|
||||
from project.models import Analytics, EventAttendanceMode, EventDate
|
||||
from project.utils import get_place_str
|
||||
|
||||
|
||||
def track_analytics(key, value1, value2):
|
||||
@ -112,3 +116,52 @@ def truncate(data: str, length: int) -> str:
|
||||
return data
|
||||
|
||||
return (data[: length - 2] + "..") if len(data) > length else data
|
||||
|
||||
|
||||
def get_share_links(url: str, title: str) -> dict:
|
||||
share_links = dict()
|
||||
encoded_url = quote_plus(url)
|
||||
encoded_title = quote_plus(title)
|
||||
|
||||
share_links[
|
||||
"facebook"
|
||||
] = f"https://www.facebook.com/sharer/sharer.php?u={encoded_url}"
|
||||
share_links[
|
||||
"twitter"
|
||||
] = f"https://twitter.com/intent/tweet?url={encoded_url}&text={encoded_title}"
|
||||
share_links["email"] = f"mailto:?subject={encoded_title}&body={encoded_url}"
|
||||
share_links["whatsapp"] = f"whatsapp://send?text={encoded_url}"
|
||||
share_links["telegram"] = f"https://t.me/share/url?url={encoded_url}"
|
||||
share_links["url"] = url
|
||||
|
||||
return share_links
|
||||
|
||||
|
||||
def get_calendar_links(event_date: EventDate) -> dict:
|
||||
calendar_links = dict()
|
||||
|
||||
url = url_for("event_date", id=event_date.id, _external=True)
|
||||
encoded_url = quote_plus(url)
|
||||
encoded_title = quote_plus(event_date.event.name)
|
||||
start = event_date.start.astimezone(gmt_tz).strftime("%Y%m%dT%H%M%SZ")
|
||||
if event_date.end:
|
||||
end = event_date.end.astimezone(gmt_tz).strftime("%Y%m%dT%H%M%SZ")
|
||||
else:
|
||||
end = start
|
||||
|
||||
if (
|
||||
event_date.event.attendance_mode
|
||||
and event_date.event.attendance_mode != EventAttendanceMode.online
|
||||
):
|
||||
location = get_place_str(event_date.event.event_place)
|
||||
locationParam = f"&location={quote_plus(location)}"
|
||||
else:
|
||||
locationParam = ""
|
||||
|
||||
calendar_links[
|
||||
"google"
|
||||
] = f"http://www.google.com/calendar/event?action=TEMPLATE&text={encoded_title}&dates={start}/{end}&details={encoded_url}{locationParam}"
|
||||
|
||||
calendar_links["ics"] = url_for("event_date_ical", id=event_date.id, _external=True)
|
||||
|
||||
return calendar_links
|
||||
|
||||
@ -4,6 +4,7 @@ apispec==4.0.0
|
||||
apispec-webframeworks==0.5.2
|
||||
appdirs==1.4.4
|
||||
argh==0.26.2
|
||||
arrow==0.14.7
|
||||
attrs==20.3.0
|
||||
Authlib==0.15.3
|
||||
Babel==2.9.0
|
||||
@ -46,6 +47,7 @@ Flask-SQLAlchemy==2.4.4
|
||||
Flask-WTF==0.14.3
|
||||
GeoAlchemy2==0.8.4
|
||||
gunicorn==20.0.4
|
||||
icalendar==4.0.7
|
||||
identify==1.5.10
|
||||
idna==2.10
|
||||
importlib-metadata==3.1.1
|
||||
@ -97,6 +99,7 @@ speaklater==1.3
|
||||
SQLAlchemy==1.3.20
|
||||
SQLAlchemy-Utils==0.36.8
|
||||
swagger-spec-validator==2.7.3
|
||||
TatSu==4.4.0
|
||||
toml==0.10.2
|
||||
typed-ast==1.4.1
|
||||
typing-extensions==3.7.4.3
|
||||
|
||||
@ -176,8 +176,10 @@ class Seeder(object):
|
||||
category = get_event_category(category_name)
|
||||
return category.id
|
||||
|
||||
def create_event(self, admin_unit_id, recurrence_rule="", external_link=""):
|
||||
from project.models import Event
|
||||
def create_event(
|
||||
self, admin_unit_id, recurrence_rule="", external_link="", end=None
|
||||
):
|
||||
from project.models import Event, EventAttendanceMode
|
||||
from project.services.event import insert_event, upsert_event_category
|
||||
|
||||
with self._app.app_context():
|
||||
@ -187,6 +189,7 @@ class Seeder(object):
|
||||
event.name = "Name"
|
||||
event.description = "Beschreibung"
|
||||
event.start = self.get_now_by_minute()
|
||||
event.end = end
|
||||
event.event_place_id = self.upsert_default_event_place(admin_unit_id)
|
||||
event.organizer_id = self.upsert_default_event_organizer(admin_unit_id)
|
||||
event.recurrence_rule = recurrence_rule
|
||||
@ -194,6 +197,7 @@ class Seeder(object):
|
||||
event.ticket_link = ""
|
||||
event.tags = ""
|
||||
event.price_info = ""
|
||||
event.attendance_mode = EventAttendanceMode.offline
|
||||
insert_event(event)
|
||||
self._db.session.commit()
|
||||
event_id = event.id
|
||||
|
||||
73
tests/test_utils.py
Normal file
73
tests/test_utils.py
Normal file
@ -0,0 +1,73 @@
|
||||
def test_get_location_str_none(client, seeder, app, utils):
|
||||
from project.utils import get_location_str
|
||||
|
||||
location_str = get_location_str(None)
|
||||
assert location_str == ""
|
||||
|
||||
|
||||
def test_get_location_str_empty(client, seeder, app, utils):
|
||||
from project.models import Location
|
||||
from project.utils import get_location_str
|
||||
|
||||
location = Location()
|
||||
|
||||
location_str = get_location_str(location)
|
||||
assert location_str == ""
|
||||
|
||||
|
||||
def test_get_location_str_full(client, seeder, app, utils):
|
||||
from project.models import Location
|
||||
from project.utils import get_location_str
|
||||
|
||||
location = Location()
|
||||
location.street = "Strasse"
|
||||
location.postalCode = "PLZ"
|
||||
location.city = "Ort"
|
||||
|
||||
location_str = get_location_str(location)
|
||||
assert location_str == "Strasse, PLZ Ort"
|
||||
|
||||
|
||||
def test_get_location_str_no_street(client, seeder, app, utils):
|
||||
from project.models import Location
|
||||
from project.utils import get_location_str
|
||||
|
||||
location = Location()
|
||||
location.postalCode = "PLZ"
|
||||
location.city = "Ort"
|
||||
|
||||
location_str = get_location_str(location)
|
||||
assert location_str == "PLZ Ort"
|
||||
|
||||
|
||||
def test_get_place_str_full(client, seeder, app, utils):
|
||||
from project.models import EventPlace, Location
|
||||
from project.utils import get_place_str
|
||||
|
||||
place = EventPlace()
|
||||
place.name = "Name"
|
||||
place.location = Location()
|
||||
place.location.street = "Strasse"
|
||||
place.location.postalCode = "PLZ"
|
||||
place.location.city = "Ort"
|
||||
|
||||
place_str = get_place_str(place)
|
||||
assert place_str == "Name, Strasse, PLZ Ort"
|
||||
|
||||
|
||||
def test_get_place_str_none(client, seeder, app, utils):
|
||||
from project.utils import get_place_str
|
||||
|
||||
place_str = get_place_str(None)
|
||||
assert place_str == ""
|
||||
|
||||
|
||||
def test_get_place_str_no_location(client, seeder, app, utils):
|
||||
from project.models import EventPlace
|
||||
from project.utils import get_place_str
|
||||
|
||||
place = EventPlace()
|
||||
place.name = "Name"
|
||||
|
||||
place_str = get_place_str(place)
|
||||
assert place_str == "Name"
|
||||
@ -1,6 +1,6 @@
|
||||
def test_read(client, seeder, utils):
|
||||
user_id, admin_unit_id = seeder.setup_base()
|
||||
seeder.create_event(admin_unit_id)
|
||||
seeder.create_event(admin_unit_id, end=seeder.get_now_by_minute())
|
||||
|
||||
url = utils.get_url("event_date", id=1)
|
||||
utils.get_ok(url)
|
||||
@ -10,6 +10,14 @@ def test_read(client, seeder, utils):
|
||||
utils.assert_response_redirect(response, "event_date", id=1)
|
||||
|
||||
|
||||
def test_ical(client, seeder, utils):
|
||||
user_id, admin_unit_id = seeder.setup_base()
|
||||
seeder.create_event(admin_unit_id, end=seeder.get_now_by_minute())
|
||||
|
||||
url = utils.get_url("event_date_ical", id=1)
|
||||
utils.get_ok(url)
|
||||
|
||||
|
||||
def test_list(client, seeder, utils):
|
||||
user_id, admin_unit_id = seeder.setup_base()
|
||||
seeder.create_event(admin_unit_id)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user