This commit is contained in:
Daniel Grams 2020-06-23 14:17:00 +02:00
parent 38ae395bef
commit 34f07ae562
12 changed files with 329 additions and 109 deletions

87
app.py
View File

@ -1,5 +1,6 @@
import os
from flask import Flask, render_template, request, url_for, redirect, abort, flash
from base64 import b64decode
from flask import Flask, render_template, request, url_for, redirect, abort, flash, current_app
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import joinedload
from sqlalchemy.sql import asc, func
@ -39,7 +40,7 @@ db = SQLAlchemy(app)
# Setup Flask-Security
# Define models
from models import EventSuggestion, EventSuggestionDate, OrgOrAdminUnit, Actor, Place, Location, User, Role, AdminUnit, AdminUnitMember, AdminUnitMemberRole, OrgMember, OrgMemberRole, Organization, AdminUnitOrg, AdminUnitOrgRole, Event, EventDate
from models import Image, EventSuggestion, EventSuggestionDate, OrgOrAdminUnit, Actor, Place, Location, User, Role, AdminUnit, AdminUnitMember, AdminUnitMemberRole, OrgMember, OrgMemberRole, Organization, AdminUnitOrg, AdminUnitOrgRole, Event, EventDate
user_datastore = SQLAlchemySessionUserDatastore(db.session, User, Role)
security = Security(app, user_datastore)
@ -47,6 +48,10 @@ security = Security(app, user_datastore)
def get_locale():
return request.accept_languages.best_match(app.config['LANGUAGES'])
def get_img_resource(res):
with current_app.open_resource('static/img/' + res) as f:
return f.read()
# Create a user to test with
def upsert_user(email, password="password"):
result = user_datastore.find_user(email=email)
@ -140,12 +145,39 @@ def add_role_to_org_member(org_member, role):
if OrgMemberRole.query.with_parent(org_member).filter_by(name = role.name).first() is None:
org_member.roles.append(role)
def upsert_organization(org_name):
def upsert_image_with_data(image, data):
if image is None:
image = Image()
image.data = b64decode(data)
image.encoding_format = "image/jpeg"
return image
def upsert_image_with_res(image, res):
if image is None:
image = Image()
image.data = get_img_resource(res)
image.encoding_format = "image/jpeg"
return image
def upsert_organization(org_name, street = None, postalCode = None, city = None, latitude = 0, longitude = 0, legal_name = None, url=None, logo_res=None):
result = Organization.query.filter_by(name = org_name).first()
if result is None:
result = Organization(name = org_name)
db.session.add(result)
result.legal_name = legal_name
result.url = url
if city is not None:
result.location = upsert_location(street, postalCode, city, latitude, longitude)
if logo_res is not None:
result.logo = upsert_image_with_res(result.logo, logo_res)
upsert_org_or_admin_unit_for_organization(result)
return result
@ -184,15 +216,24 @@ def upsert_location(street, postalCode, city, latitude = 0, longitude = 0):
return result
def upsert_place(name, street = None, postalCode = None, city = None, latitude = 0, longitude = 0):
def upsert_place(name, street = None, postalCode = None, city = None, latitude = 0, longitude = 0, url=None, description=None, photo_res=None):
result = Place.query.filter_by(name = name).first()
if result is None:
result = Place(name = name)
db.session.add(result)
if url:
result.url=url
if description:
result.description=description
if city is not None:
result.location = upsert_location(street, postalCode, city, latitude, longitude)
if photo_res is not None:
result.photo = upsert_image_with_res(result.photo, photo_res)
return result
def upsert_event_suggestion(event_name, host_name, place_name, start, description, link = None, admin_unit = None):
@ -222,7 +263,7 @@ def upsert_event_suggestion(event_name, host_name, place_name, start, descriptio
return result
def upsert_event(event_name, host, location_name, start, description, link = None, verified = False, admin_unit = None):
def upsert_event(event_name, host, location_name, start, description, link = None, verified = False, admin_unit = None, ticket_link=None, photo_res=None):
if admin_unit is None:
admin_unit = get_admin_unit('Stadt Goslar')
place = upsert_place(location_name)
@ -239,11 +280,15 @@ def upsert_event(event_name, host, location_name, start, description, link = Non
result.admin_unit = admin_unit
result.host = host
result.place = place
result.ticket_link = ticket_link
eventDate = EventDate(event_id = result.id, start=start)
result.dates = []
result.dates.append(eventDate)
if photo_res is not None:
result.photo = upsert_image_with_res(result.photo, photo_res)
return result
def get_event_hosts():
@ -544,6 +589,7 @@ def create_user():
gz = upsert_organization("Goslarsche Zeitung")
celtic_inn = upsert_organization("Celtic Inn")
kloster_woelteringerode = upsert_organization("Kloster Wöltingerode")
miners_rock = upsert_organization("Miner's Rock", "Kuhlenkamp 36", "38640", "Goslar", legal_name="Miner's Rock UG (haftungsbeschränkt)", url="https://www.miners-rock.de/", logo_res="minersrock.jpeg")
gmg_admin_unit_org = add_organization_to_admin_unit(gmg, goslar)
add_role_to_admin_unit_org(gmg_admin_unit_org, admin_unit_org_event_verifier_role)
@ -552,6 +598,7 @@ def create_user():
add_role_to_admin_unit_org(gz_admin_unit_org, admin_unit_org_event_verifier_role)
add_organization_to_admin_unit(celtic_inn, goslar)
add_organization_to_admin_unit(miners_rock, goslar)
add_organization_to_admin_unit(upsert_organization("Aids-Hilfe Goslar"), goslar)
add_organization_to_admin_unit(upsert_organization("Akademie St. Jakobushaus"), goslar)
add_organization_to_admin_unit(upsert_organization("Aktiv für Hahndorf e. V."), goslar)
@ -610,7 +657,6 @@ def create_user():
add_organization_to_admin_unit(upsert_organization("Marktkirche Goslar"), goslar)
add_organization_to_admin_unit(upsert_organization("Media Markt "), goslar)
add_organization_to_admin_unit(upsert_organization("MGV Juventa von 1877 e. V."), goslar)
add_organization_to_admin_unit(upsert_organization("Miners' Rock!"), goslar)
add_organization_to_admin_unit(upsert_organization("Mönchehaus Museum"), goslar)
add_organization_to_admin_unit(upsert_organization("MTV Goslar e. V."), goslar)
add_organization_to_admin_unit(upsert_organization("Museumsverein Goslar e. V."), goslar)
@ -650,7 +696,8 @@ def create_user():
upsert_place("Marktplatz Goslar", 'Markt 6', '38640', 'Goslar', 51.9063601, 10.4249433)
upsert_place("Burg Vienenburg", 'Burgweg 2', '38690', 'Goslar', 51.9476558, 10.5617368)
upsert_place("Kurhaus Bad Harzburg", 'Kurhausstraße 11', '38667', 'Bad Harzburg', 51.8758165, 10.5593392)
upsert_place("Goslarsche Höfe", 'Okerstraße 32', '38640', 'Goslar', 51.911571, 10.4391331)
upsert_place("Goslarsche Höfe", 'Okerstraße 32', '38640', 'Goslar', 51.911571, 10.4391331, 'https://www.goslarsche-hoefe.de/', 'Dir Rosserei', photo_res="schlosserei.jpeg")
upsert_place("Schlosserei im Rammelsberg", 'Bergtal 19', '38640', 'Goslar', 51.890527, 10.418880, 'http://www.rammelsberg.de/', 'Die "Schlosserei" ist erprobter Veranstaltungsort und bietet Platz für ca. 700 Besucher. Das Ambiente ist technisch gut ausgestattet und flexibel genug, für jeden Künstler individuell wandelbar zu sein. Dabei lebt nicht nur der Veranstaltungsraum, es wirkt der gesamte Komplex des Rammelsberges und macht den Besuch zu einem unvergeßlichen Erlebnis.', photo_res="schlosserei.jpeg")
# Org or admins
goslar_ooa = upsert_org_or_admin_unit_for_admin_unit(goslar)
@ -694,6 +741,10 @@ def create_user():
jason_celtic_inn_member = add_user_to_organization(jason, celtic_inn)
add_role_to_org_member(jason_celtic_inn_member, org_member_event_creator_role)
grzno = upsert_user("grzno@test.de")
grzno_miners_rock_member = add_user_to_organization(grzno, miners_rock)
add_role_to_org_member(grzno_miners_rock_member, org_member_event_creator_role)
# Events
berlin = pytz.timezone('Europe/Berlin')
upsert_event("Vienenburger Seefest",
@ -757,6 +808,15 @@ def create_user():
'Zum letzten Mal in dieser Saison gibt es einen Hof-Flohmarkt. Wir bieten zwar nicht den größten, aber vielleicht den gemütlichsten Flohmarkt in der Region. Frei von gewerblichen Anbietern, dafür mit Kaffee, Kuchen, Bier und Bratwurst, alles auf unserem schönen Hofgelände.',
'https://www.goslarsche-hoefe.de/veranstaltungen/10/2175252/2020/10/10/herbst-flohmarkt.html')
upsert_event('"MINER\'S ROCK" Schickt XVI - Lotte',
miners_rock.org_or_adminunit,
"Schlosserei im Rammelsberg",
create_berlin_date(2020, 10, 31, 19, 0),
'Auch im Jahr 2020 wagt sich das MINERS ROCK wieder an eine Doppel-Schicht. LOTTE wird bei uns das Wochenende am Berg abrunden! Nach der bereits ausverkauften Schicht am 30. Oktober mit Subway to Sally, wird Lotte den Samstagabend zu einem Pop-Erlebnis machen.\nAb Anfang Februar ist sie in den Konzerthallen in Deutschland unterwegs und wird ihr neues Album „Glück“ vorstellen. Glück ist der langersehnte Nachfolger von LOTTEs Debütalbum „Querfeldein". Mit Songs wie der ersten Single „Schau mich nicht so an" oder dem Duett mit Max Giesinger „Auf das was da noch kommt“, durchmisst LOTTE dabei die Höhen und Tiefen des menschlichen Glücksstrebens. Und auch wenn jeder der zwölf Songs seine eigene Geschichte erzählt sie alle eint die Suche nach der ganz persönlichen Bedeutung dieses großen Wortes. Glück ist kein Werk über einen abgeschlossenen Prozess, sondern ein beeindruckend ehrliches und facettenreiches Album über eine menschliche Suche. „Auf das was da noch kommt“ läuft derzeit in den Radiostationen auf und ab und macht einfach Spaß.\n\nWichtig zu wissen:\n\nEinlass: 19:00 Uhr\nBeginn des Musikprogramms: 20:00 Uhr\nTickets gibt es ab sofort im Shop des MINERS ROCK unter www.miners-rock.de und in den Geschäftsstellen der Goslarschen Zeitung.',
'https://www.miners-rock.de/xvi-lotte',
ticket_link='https://www.regiolights.de/tickets/product/schicht-xvi-lotte',
photo_res="lotte.jpeg")
db.session.commit()
# Views
@ -790,7 +850,7 @@ def organizations():
@app.route('/organization/<int:organization_id>')
def organization(organization_id):
organization = Organization.query.filter_by(id = organization_id).first()
organization = Organization.query.get_or_404(organization_id)
current_user_member = OrgMember.query.with_parent(organization).filter_by(user_id = current_user.id).first() if current_user.is_authenticated else None
ooa = upsert_org_or_admin_unit_for_organization(organization)
@ -802,11 +862,22 @@ def organization(organization_id):
can_list_members=can_list_org_members(organization),
events=events)
@app.route('/image/<int:id>')
def image(id):
image = Image.query.get_or_404(id)
return app.response_class(image.data, mimetype=image.encoding_format)
@app.route("/profile")
@auth_required()
def profile():
return render_template('profile.html')
@app.route("/places")
def places():
places = Place.query.order_by(asc(func.lower(Place.name))).all()
return render_template('place/list.html',
places=places)
@app.route('/place/<int:place_id>')
def place(place_id):
place = Place.query.filter_by(id = place_id).first()

View File

@ -1,8 +1,8 @@
"""empty message
Revision ID: 93158b40fde6
Revision ID: ea09dc1839df
Revises:
Create Date: 2020-06-21 15:45:24.988479
Create Date: 2020-06-23 12:04:01.423454
"""
from alembic import op
@ -10,7 +10,7 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '93158b40fde6'
revision = 'ea09dc1839df'
down_revision = None
branch_labels = None
depends_on = None
@ -75,6 +75,15 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name')
)
op.create_table('image',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('data', sa.LargeBinary(), nullable=True),
sa.Column('encoding_format', sa.String(length=80), nullable=True),
sa.Column('created_by_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('location',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
@ -89,15 +98,6 @@ def upgrade():
sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('organization',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.Unicode(length=255), nullable=True),
sa.Column('created_by_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name')
)
op.create_table('roles_users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=True),
@ -106,17 +106,6 @@ def upgrade():
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('actor',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('organization_id', sa.Integer(), nullable=True),
sa.Column('admin_unit_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['admin_unit_id'], ['adminunit.id'], ),
sa.ForeignKeyConstraint(['organization_id'], ['organization.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('user_id', 'organization_id', 'admin_unit_id')
)
op.create_table('adminunitmember',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('admin_unit_id', sa.Integer(), nullable=False),
@ -125,14 +114,6 @@ def upgrade():
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('adminunitorg',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('admin_unit_id', sa.Integer(), nullable=False),
sa.Column('organization_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['admin_unit_id'], ['adminunit.id'], ),
sa.ForeignKeyConstraint(['organization_id'], ['organization.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('eventsuggestion',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
@ -152,6 +133,70 @@ def upgrade():
sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('organization',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.Unicode(length=255), nullable=True),
sa.Column('legal_name', sa.Unicode(length=255), nullable=True),
sa.Column('location_id', sa.Integer(), nullable=True),
sa.Column('logo_id', sa.Integer(), nullable=True),
sa.Column('url', sa.String(length=255), nullable=True),
sa.Column('created_by_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ),
sa.ForeignKeyConstraint(['location_id'], ['location.id'], ),
sa.ForeignKeyConstraint(['logo_id'], ['image.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name')
)
op.create_table('place',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.Unicode(length=255), nullable=False),
sa.Column('location_id', sa.Integer(), nullable=True),
sa.Column('photo_id', sa.Integer(), nullable=True),
sa.Column('url', sa.String(length=255), nullable=True),
sa.Column('description', sa.UnicodeText(), nullable=True),
sa.Column('created_by_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ),
sa.ForeignKeyConstraint(['location_id'], ['location.id'], ),
sa.ForeignKeyConstraint(['photo_id'], ['image.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name')
)
op.create_table('actor',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('organization_id', sa.Integer(), nullable=True),
sa.Column('admin_unit_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['admin_unit_id'], ['adminunit.id'], ),
sa.ForeignKeyConstraint(['organization_id'], ['organization.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('user_id', 'organization_id', 'admin_unit_id')
)
op.create_table('adminunitmemberroles_members',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('member_id', sa.Integer(), nullable=True),
sa.Column('role_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['member_id'], ['adminunitmember.id'], ),
sa.ForeignKeyConstraint(['role_id'], ['adminunitmemberrole.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('adminunitorg',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('admin_unit_id', sa.Integer(), nullable=False),
sa.Column('organization_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['admin_unit_id'], ['adminunit.id'], ),
sa.ForeignKeyConstraint(['organization_id'], ['organization.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('eventsuggestiondate',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('event_suggestion_id', sa.Integer(), nullable=False),
sa.Column('start', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['event_suggestion_id'], ['eventsuggestion.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('org_or_adminunit',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('organization_id', sa.Integer(), nullable=True),
@ -170,25 +215,6 @@ def upgrade():
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('place',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.Unicode(length=255), nullable=False),
sa.Column('location_id', sa.Integer(), nullable=True),
sa.Column('created_by_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ),
sa.ForeignKeyConstraint(['location_id'], ['location.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name')
)
op.create_table('adminunitmemberroles_members',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('member_id', sa.Integer(), nullable=True),
sa.Column('role_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['member_id'], ['adminunitmember.id'], ),
sa.ForeignKeyConstraint(['role_id'], ['adminunitmemberrole.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('adminunitorgroles_organizations',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('admin_unit_org_id', sa.Integer(), nullable=True),
@ -208,20 +234,15 @@ def upgrade():
sa.Column('external_link', sa.String(length=255), nullable=True),
sa.Column('ticket_link', sa.String(length=255), nullable=True),
sa.Column('verified', sa.Boolean(), nullable=True),
sa.Column('photo_id', sa.Integer(), nullable=True),
sa.Column('created_by_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['admin_unit_id'], ['adminunit.id'], ),
sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ),
sa.ForeignKeyConstraint(['host_id'], ['org_or_adminunit.id'], ),
sa.ForeignKeyConstraint(['photo_id'], ['image.id'], ),
sa.ForeignKeyConstraint(['place_id'], ['place.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('eventsuggestiondate',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('event_suggestion_id', sa.Integer(), nullable=False),
sa.Column('start', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['event_suggestion_id'], ['eventsuggestion.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('orgmemberroles_members',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('member_id', sa.Integer(), nullable=True),
@ -244,20 +265,21 @@ def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('eventdate')
op.drop_table('orgmemberroles_members')
op.drop_table('eventsuggestiondate')
op.drop_table('event')
op.drop_table('adminunitorgroles_organizations')
op.drop_table('adminunitmemberroles_members')
op.drop_table('place')
op.drop_table('orgmember')
op.drop_table('org_or_adminunit')
op.drop_table('eventsuggestion')
op.drop_table('eventsuggestiondate')
op.drop_table('adminunitorg')
op.drop_table('adminunitmember')
op.drop_table('adminunitmemberroles_members')
op.drop_table('actor')
op.drop_table('roles_users')
op.drop_table('place')
op.drop_table('organization')
op.drop_table('eventsuggestion')
op.drop_table('adminunitmember')
op.drop_table('roles_users')
op.drop_table('location')
op.drop_table('image')
op.drop_table('adminunit')
op.drop_table('user')
op.drop_table('role')

View File

@ -2,7 +2,7 @@ from app import db
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import relationship, backref
from sqlalchemy.schema import CheckConstraint
from sqlalchemy import UniqueConstraint, Boolean, DateTime, Column, Integer, String, ForeignKey, Unicode, UnicodeText, Numeric
from sqlalchemy import UniqueConstraint, Boolean, DateTime, Column, Integer, String, ForeignKey, Unicode, UnicodeText, Numeric, LargeBinary
from flask_security import UserMixin, RoleMixin
import datetime
@ -19,6 +19,14 @@ class TrackableMixin(object):
def created_by(cls):
return relationship("User")
### Multi purpose
class Image(db.Model, TrackableMixin):
__tablename__ = 'image'
id = Column(Integer(), primary_key=True)
data = db.Column(db.LargeBinary)
encoding_format = Column(String(80))
### User
class RolesUsers(db.Model):
@ -79,6 +87,12 @@ class Organization(db.Model, TrackableMixin):
__tablename__ = 'organization'
id = Column(Integer(), primary_key=True)
name = Column(Unicode(255), unique=True)
legal_name = Column(Unicode(255))
location_id = db.Column(db.Integer, db.ForeignKey('location.id'))
location = db.relationship('Location')
logo_id = db.Column(db.Integer, db.ForeignKey('image.id'))
logo = db.relationship('Image', uselist=False)
url = Column(String(255))
members = relationship('OrgMember', backref=backref('organization', lazy=True))
### Admin Unit
@ -135,6 +149,7 @@ class AdminUnit(db.Model, TrackableMixin):
organizations = relationship('AdminUnitOrg', backref=backref('adminunit', lazy=True))
# Universal Types
class Actor(db.Model):
__tablename__ = 'actor'
__table_args__ = (UniqueConstraint('user_id', 'organization_id', 'admin_unit_id'),)
@ -175,6 +190,10 @@ class Place(db.Model, TrackableMixin):
name = Column(Unicode(255), nullable=False, unique=True)
location_id = db.Column(db.Integer, db.ForeignKey('location.id'))
location = db.relationship('Location')
photo_id = db.Column(db.Integer, db.ForeignKey('image.id'))
photo = db.relationship('Image', uselist=False)
url = Column(String(255))
description = Column(UnicodeText())
# Events
class EventSuggestion(db.Model, TrackableMixin):
@ -215,6 +234,8 @@ class Event(db.Model, TrackableMixin):
external_link = Column(String(255))
ticket_link = Column(String(255))
verified = Column(Boolean())
photo_id = db.Column(db.Integer, db.ForeignKey('image.id'))
photo = db.relationship('Image', uselist=False)
dates = relationship('EventDate', backref=backref('event', lazy=False), cascade="all, delete-orphan")
# wiederkehrende Dates sind zeitlich eingeschränkt

BIN
static/img/lotte.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
static/img/minersrock.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
static/img/schlosserei.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

View File

@ -56,16 +56,14 @@
{% endif %}
{% endmacro %}
{% macro render_location(location) %}
{{ location.street }}, {{ location.postalCode }} {{ location.city }}
{% endmacro %}
{% macro render_location(location) %}{{ location.street }}, {{ location.postalCode }} {{ location.city }}{% endmacro %}
{% macro render_place(place) %}
{% if place.location %}
{%- if place.location -%}
{{ place.name }}, {{render_location(place.location)}}
{% else %}
{%- else -%}
{{ place.name }}
{% endif %}
{%- endif -%}
{% endmacro %}
{% macro render_events_sub_menu(user_can_create_event, user_can_list_event_suggestion) %}
@ -123,4 +121,44 @@
</a>
</div>
{% endmacro %}
{% endmacro %}
{% macro render_location_card(location, place=None) %}
{% if location %}
<div class="card card-body">
<p>
{{ location.street }}<br />
{{ location.postalCode }} {{ location.city }}
</p>
<p>
<a href="http://www.google.com/maps?q={% if place %}{{ render_place(place) | quote_plus }}{% else %}{{ render_location(location) | quote_plus }}{% endif %}">{{ _('Show on Google Maps') }}</a>
</p>
</div>
{% endif %}
{% endmacro %}
{% macro render_link_prop(link) %}
{% if link %}
<div>
<i class="fa fa-fw fa-external-link" data-toggle="tooltip" title="{{ _('Link') }}"></i>
<a href="{{ link }}">{{ link }}</a>
</div>
{% endif %}
{% endmacro %}
{% macro render_location_prop(location) %}
{% if location %}
<div>
<i class="fa fa-fw fa-map-marker" data-toggle="tooltip" title="{{ _('Location') }}"></i>
{{ render_location(location) }}
</div>
{% endif %}
{% endmacro %}
{% macro render_image(image_id) %}
{% if image_id %}
<img src="{{ url_for('image', id=image_id) }}"/>
{% endif %}
{% endmacro %}

View File

@ -1,5 +1,5 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_ooa_with_link, render_place %}
{% from "_macros.html" import render_image, render_ooa_with_link, render_place, render_link_prop %}
{% block title %}
{{ event.name }}
{% endblock %}
@ -29,14 +29,18 @@
{% endif %}
</div>
{% if event.photo_id %}
<div class="my-4">{{ render_image(event.photo_id) }}</div>
{% endif %}
<div class="my-4">{{ event.description }}</div>
{% if event.external_link %}
<div class="my-4">
<i class="fa fa-fw fa-external-link" data-toggle="tooltip" title="{{ _('Link') }}"></i>
<a href="{{ event.external_link }}">{{ event.external_link }}</a>
</div>
{% endif %}
{% if event.external_link or event.ticket_link %}
<div class="my-4">
{{ render_link_prop(event.external_link) }}
{{ render_link_prop(event.ticket_link) }}
</div>
{% endif %}
<div class="my-4">
<div><i class="fa fa-fw fa-users" data-toggle="tooltip" title="{{ _('Host') }}"></i> {{ render_ooa_with_link(event.host) }}</div>

View File

@ -56,6 +56,7 @@
<a class="nav-item nav-link" href="{{ url_for('admin_units') }}">{{ _('Admin Units') }}</a>
<a class="nav-item nav-link" href="{{ url_for('organizations') }}">{{ _('Organizations') }}</a>
<a class="nav-item nav-link" href="{{ url_for('events') }}">{{ _('Events') }}</a>
<a class="nav-item nav-link" href="{{ url_for('places') }}">{{ _('Places') }}</a>
</div>
<div class="navbar-nav navbar-right">
{% if current_user.is_authenticated %}

View File

@ -1,5 +1,5 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_events %}
{% from "_macros.html" import render_events, render_location_prop, render_link_prop, render_image %}
{% block title %}
{{ organization.name }}
{% endblock %}
@ -9,24 +9,40 @@
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#info" role="tab" area-selected="true">{{ _('Info') }}</a>
</li>
{% if current_user_member or can_list_members %}
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#members" role="tab" area-selected="true">{{ _('Members') }}</a>
<a class="nav-link" data-toggle="tab" href="#members" role="tab">{{ _('Members') }}</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#events" role="tab">{{ _('Events') }}</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#events" role="tab" area-selected="true">{{ _('Events') }}</a>
</li>
{% endif %}
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div class="tab-pane pt-4 active" id="info" role="tabpanel">
{% if organization.legal_name %}
<div>{{ organization.legal_name }}</div>
{% endif %}
<div class="my-4">
{{ render_location_prop(organization.location) }}
{{ render_link_prop(organization.url) }}
</div>
{% if organization.logo_id %}
<div class="my-4">{{ render_image(organization.logo_id) }}</div>
{% endif %}
</div>
{% if current_user_member or can_list_members %}
<div class="tab-pane pt-4 active" id="members" role="tabpanel">
<div class="tab-pane pt-4" id="members" role="tabpanel">
{% if current_user_member %}
<div class="my-4">
{{ _('You are a member of this organization.') }}
@ -56,7 +72,8 @@
{% endif %}
</div>
{% endif %}
<div class="tab-pane pt-4{% if not current_user_member and not can_list_members %} active{% endif %}" id="events" role="tabpanel">
<div class="tab-pane pt-4" id="events" role="tabpanel">
{{ render_events(events) }}
</div>
</div>

28
templates/place/list.html Normal file
View File

@ -0,0 +1,28 @@
{% extends "layout.html" %}
{% block title %}
{{ _('Places') }}
{% endblock %}
{% block content %}
<h1>{{ _('Places') }}</h1>
<div class="table-responsive">
<table class="table table-sm table-bordered table-hover table-striped">
<thead>
<tr>
<th>{{ _('Name') }}</th>
</tr>
</thead>
<tbody>
{% for place in places %}
<tr>
<td>
<a href="{{ url_for('place', place_id=place.id) }}">{{ place.name }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@ -1,5 +1,5 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_place, render_events %}
{% from "_macros.html" import render_place, render_events, render_location_card, render_link_prop, render_image %}
{% block title %}
{{ place.name }}
{% endblock %}
@ -7,21 +7,39 @@
<h1>{{ place.name }}</h1>
{% if place.location %}
<div class="card card-body">
<p>
{{ place.location.street }}<br />
{{ place.location.postalCode }} {{ place.location.city }}
</p>
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#info" role="tab" area-selected="true">{{ _('Info') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#events" role="tab">{{ _('Events') }}</a>
</li>
</ul>
<p>
<a href="http://www.google.com/maps?q={{ render_place(place) | quote_plus }}">{{ _('Show on Google Maps') }}</a>
</p>
<!-- Tab panes -->
<div class="tab-content">
<div class="tab-pane pt-4 active" id="info" role="tabpanel">
{{ render_location_card(place.location, place) }}
<div class="my-4">
{{ render_link_prop(place.url) }}
</div>
{% if place.photo_id %}
<div class="my-4">{{ render_image(place.photo_id) }}</div>
{% endif %}
{% if place.description %}
<div class="my-4">{{ place.description }}</div>
{% endif %}
</div>
<div class="tab-pane pt-4" id="events" role="tabpanel">
{{ render_events(place.events) }}
</div>
</div>
{% endif %}
<h2>{{ _('Events') }}</h2>
{{ render_events(place.events) }}
{% endblock %}