event organizer

This commit is contained in:
Daniel Grams 2020-07-17 13:35:09 +02:00
parent 60ab7b8f69
commit dfbe01bdd6
8 changed files with 242 additions and 11 deletions

27
app.py
View File

@ -53,7 +53,7 @@ app.json_encoder = DateTimeEncoder
# Setup Flask-Security
# Define models
from models import EventCategory, Image, EventSuggestion, EventSuggestionDate, OrgOrAdminUnit, Actor, Place, Location, User, Role, AdminUnit, AdminUnitMember, AdminUnitMemberRole, OrgMember, OrgMemberRole, Organization, AdminUnitOrg, AdminUnitOrgRole, Event, EventDate
from models import EventOrganizer, EventCategory, 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)
from oauth import blueprint
@ -715,6 +715,20 @@ def get_event_suggestions_for_current_user():
@app.before_first_request
def create_initial_data():
events = Event.query.filter_by(organizer = None).all()
for event in events:
if event.host:
ooa = event.host.admin_unit if event.host.admin_unit else event.host.organization
organizer = EventOrganizer()
organizer.org_name = ooa.name
organizer.url = ooa.url
organizer.email = ooa.email
organizer.phone = ooa.phone
event.organizer = organizer
db.session.commit()
return
# Event categories
@ -1293,6 +1307,9 @@ from forms.admin_unit import CreateAdminUnitForm, UpdateAdminUnitForm
def update_event_with_form(event, form):
form.populate_obj(event)
if event.host_id == 0:
event.host_id = None
update_event_dates_with_recurrence_rule(event, form.start.data, form.end.data)
if form.photo_file.data:
@ -1305,6 +1322,9 @@ def prepare_event_form(form):
form.category_id.choices = sorted([(c.id, get_event_category_name(c)) for c in EventCategory.query.all()], key=lambda ooa: ooa[1])
form.admin_unit_id.choices = sorted([(admin_unit.id, admin_unit.name) for admin_unit in get_admin_units_for_organizations()], key=lambda admin_unit: admin_unit[1])
form.host_id.choices.insert(0, (0, ''))
form.place_id.choices.insert(0, (0, ''))
@app.route("/events/create", methods=('GET', 'POST'))
@auth_required()
def event_create():
@ -1313,11 +1333,10 @@ def event_create():
form = CreateEventForm(category_id=upsert_event_category('Other').id)
prepare_event_form(form)
form.host_id.choices.insert(0, (0, ''))
form.place_id.choices.insert(0, (0, ''))
if form.validate_on_submit():
event = Event()
event.organizer = EventOrganizer()
update_event_with_form(event, form)
try:
@ -1327,6 +1346,8 @@ def event_create():
return redirect(url_for('event', event_id=event.id))
except SQLAlchemyError as e:
flash(handleSqlError(e), 'danger')
else:
flash_errors(form)
return render_template('event/create.html', form=form)
@app.route('/event/<int:event_id>/update', methods=('GET', 'POST'))

View File

@ -1,13 +1,30 @@
from flask_babelex import lazy_gettext
from flask_babelex import lazy_gettext, gettext
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileAllowed
from wtforms import DateTimeField, StringField, SubmitField, TextAreaField, SelectField, BooleanField, IntegerField
from wtforms.fields.html5 import DateTimeLocalField
from wtforms import DateTimeField, StringField, SubmitField, TextAreaField, SelectField, BooleanField, IntegerField, FormField
from wtforms.fields.html5 import DateTimeLocalField, EmailField
from wtforms.validators import DataRequired, Optional
from wtforms.widgets import html_params, HTMLString
from models import EventTargetGroupOrigin, EventAttendanceMode, EventStatus
from .widgets import CustomDateTimeField
class EventOrganizerForm(FlaskForm):
name = StringField(lazy_gettext('Name'), validators=[Optional()])
org_name = StringField(lazy_gettext('Organization'), validators=[Optional()])
url = StringField(lazy_gettext('Link URL'), validators=[Optional()])
email = EmailField(lazy_gettext('Email'), validators=[Optional()])
phone = StringField(lazy_gettext('Phone'), validators=[Optional()])
def validate(self):
if not super(EventOrganizerForm, self).validate():
return False
if not self.name.data and not self.org_name.data:
msg = gettext('At least one of name and organization must be set')
self.name.errors.append(msg)
self.org_name.errors.append(msg)
return False
return True
class CreateEventForm(FlaskForm):
submit = SubmitField(lazy_gettext("Create event"))
name = StringField(lazy_gettext('Name'), validators=[DataRequired()])
@ -20,8 +37,10 @@ class CreateEventForm(FlaskForm):
previous_start_date = CustomDateTimeField(lazy_gettext('Previous start date'), validators=[Optional()])
tags = StringField(lazy_gettext('Tags'), validators=[Optional()])
organizer = FormField(EventOrganizerForm)
place_id = SelectField(lazy_gettext('Place'), validators=[DataRequired()], coerce=int)
host_id = SelectField(lazy_gettext('Host'), validators=[DataRequired()], coerce=int)
host_id = SelectField(lazy_gettext('Host'), validators=[Optional()], coerce=int)
category_id = SelectField(lazy_gettext('Category'), validators=[DataRequired()], coerce=int)
admin_unit_id = SelectField(lazy_gettext('Admin unit'), validators=[DataRequired()], coerce=int)

View File

@ -39,6 +39,60 @@ def get_sd_for_admin_unit(admin_unit):
result["name"] = admin_unit.name
return result
def get_sd_for_organizer_organization_contact(organizer):
result = {}
result["@type"] = "ContactPoint"
result["name"] = organizer.name
if organizer.email:
result["email"] = organizer.email
if organizer.phone:
result["telephone"] = organizer.phone
return result
def get_sd_for_organizer_organization(organizer):
result = {}
result["@type"] = "Organization"
result["name"] = organizer.org_name
if organizer.name:
result["contactPoint"] = get_sd_for_organizer_organization_contact(organizer)
else:
if organizer.email:
result["email"] = organizer.email
if organizer.phone:
result["phone"] = organizer.phone
if organizer.url:
result["url"] = organizer.url
return result
def get_sd_for_organizer_person(organizer):
result = {}
result["@type"] = "Person"
result["name"] = organizer.name
if organizer.email:
result["email"] = organizer.email
if organizer.phone:
result["phone"] = organizer.phone
if organizer.url:
result["url"] = organizer.url
return result
def get_sd_for_organizer(organizer):
if organizer.org_name:
return get_sd_for_organizer_organization(organizer)
return get_sd_for_organizer_person(organizer)
def get_sd_for_ooa(ooa):
if ooa.organization:
return get_sd_for_org(ooa.organization)
@ -96,7 +150,13 @@ def get_sd_for_event_date(event_date):
result["description"] = event.description
result["startDate"] = event_date.start
result["location"] = get_sd_for_place(event.place)
result["organizer"] = get_sd_for_ooa(event.host)
organizer_list = list()
if event.organizer:
organizer_list.append(get_sd_for_organizer(event.organizer))
if event.host:
organizer_list.append(get_sd_for_ooa(event.host))
result["organizer"] = organizer_list
if event_date.end:
result["endDate"] = event_date.end

View File

@ -0,0 +1,59 @@
"""empty message
Revision ID: 975c22ae802b
Revises: 5c8457f2eac1
Create Date: 2020-07-17 11:27:53.084732
"""
from alembic import op
import sqlalchemy as sa
import sqlalchemy_utils
import db
# revision identifiers, used by Alembic.
revision = '975c22ae802b'
down_revision = '5c8457f2eac1'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('eventorganizer',
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('org_name', sa.Unicode(length=255), nullable=True),
sa.Column('url', sa.String(length=255), nullable=True),
sa.Column('email', sa.Unicode(length=255), nullable=True),
sa.Column('phone', 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')
)
op.add_column('event', sa.Column('organizer_id', sa.Integer(), nullable=True))
op.alter_column('event', 'host_id',
existing_type=sa.INTEGER(),
nullable=True)
op.alter_column('event', 'place_id',
existing_type=sa.INTEGER(),
nullable=True)
op.create_foreign_key(None, 'event', 'eventorganizer', ['organizer_id'], ['id'])
op.drop_constraint('place_name_key', 'place', type_='unique')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_unique_constraint('place_name_key', 'place', ['name'])
op.drop_constraint(None, 'event', type_='foreignkey')
op.alter_column('event', 'place_id',
existing_type=sa.INTEGER(),
nullable=False)
op.alter_column('event', 'host_id',
existing_type=sa.INTEGER(),
nullable=False)
op.drop_column('event', 'organizer_id')
op.drop_table('eventorganizer')
# ### end Alembic commands ###

View File

@ -205,7 +205,7 @@ class Location(db.Model, TrackableMixin):
class Place(db.Model, TrackableMixin):
__tablename__ = 'place'
id = Column(Integer(), primary_key=True)
name = Column(Unicode(255), nullable=False, unique=True)
name = Column(Unicode(255), nullable=False)
location_id = db.Column(db.Integer, db.ForeignKey('location.id'))
location = db.relationship('Location')
photo_id = db.Column(db.Integer, db.ForeignKey('image.id'))
@ -260,14 +260,28 @@ class EventStatus(IntEnum):
postponed = 4
rescheduled = 5
class EventOrganizer(db.Model, TrackableMixin):
__tablename__ = 'eventorganizer'
__table_args__ = (
CheckConstraint('NOT(name IS NULL AND org_name IS NULL)'),
)
id = Column(Integer(), primary_key=True)
name = Column(Unicode(255))
org_name = Column(Unicode(255))
url = Column(String(255))
email = Column(Unicode(255))
phone = Column(Unicode(255))
class Event(db.Model, TrackableMixin):
__tablename__ = 'event'
id = Column(Integer(), primary_key=True)
admin_unit_id = db.Column(db.Integer, db.ForeignKey('adminunit.id'), nullable=False)
admin_unit = db.relationship('AdminUnit', backref=db.backref('events', lazy=True))
host_id = db.Column(db.Integer, db.ForeignKey('org_or_adminunit.id'), nullable=False)
organizer_id = db.Column(db.Integer, db.ForeignKey('eventorganizer.id'), nullable=True)
organizer = db.relationship('EventOrganizer', uselist=False)
host_id = db.Column(db.Integer, db.ForeignKey('org_or_adminunit.id'), nullable=True)
host = db.relationship('OrgOrAdminUnit', backref=db.backref('events', lazy=True))
place_id = db.Column(db.Integer, db.ForeignKey('place.id'), nullable=False)
place_id = db.Column(db.Integer, db.ForeignKey('place.id'), nullable=True)
place = db.relationship('Place', backref=db.backref('events', lazy=True))
name = Column(Unicode(255), nullable=False)
description = Column(UnicodeText(), nullable=False)

View File

@ -155,6 +155,15 @@
{% endif %}
{% endmacro %}
{% macro render_string_prop(prop, icon = None, label_key = None) %}
{% if prop %}
<div>
{% if icon %}<i class="fa fa-fw {{ icon }}" data-toggle="tooltip" title="{{ _(label_key) }}"></i>{% endif %}
{{ prop }}
</div>
{% endif %}
{% endmacro %}
{% macro render_bool_prop(prop, icon, label_key) %}
{% if prop %}
<div>
@ -319,6 +328,25 @@
</div>
</div>
{% if event.organizer %}
<div class="card mb-3">
<div class="card-header">
{{ _('Organizer') }}
</div>
<div class="card-body">
{{ render_string_prop(event.organizer.name) }}
{{ render_string_prop(event.organizer.org_name) }}
{{ render_link_prop(event.organizer.url) }}
{{ render_email_prop(event.organizer.email) }}
{{ render_phone_prop(event.organizer.phone) }}
</div>
</div>
{% endif %}
{% if event.host %}
<div class="card mb-3">
<div class="card-header">
{{ _('Host') }}
@ -359,6 +387,8 @@
</div>
</div>
{% endif %}
{% endmacro %}
{% macro render_google_sign_in_button() %}

View File

@ -48,6 +48,20 @@
</div>
</div>
<div class="card mb-4">
<div class="card-header">
{{ _('Organizer') }}
</div>
<div class="card-body">
{{ form.organizer.hidden_tag() }}
{{ render_field_with_errors(form.organizer.form.name) }}
{{ render_field_with_errors(form.organizer.form.org_name) }}
{{ render_field_with_errors(form.organizer.form.url) }}
{{ render_field_with_errors(form.organizer.form.email) }}
{{ render_field_with_errors(form.organizer.form.phone) }}
</div>
</div>
<div class="card mb-4">
<div class="card-header">
{{ _('Place') }}

View File

@ -48,6 +48,20 @@
</div>
</div>
<div class="card mb-4">
<div class="card-header">
{{ _('Organizer') }}
</div>
<div class="card-body">
{{ form.organizer.hidden_tag() }}
{{ render_field_with_errors(form.organizer.form.name) }}
{{ render_field_with_errors(form.organizer.form.org_name) }}
{{ render_field_with_errors(form.organizer.form.url) }}
{{ render_field_with_errors(form.organizer.form.email) }}
{{ render_field_with_errors(form.organizer.form.phone) }}
</div>
</div>
<div class="card mb-4">
<div class="card-header">
{{ _('Place') }}