diff --git a/migrations/versions/00daa8c472ba_.py b/migrations/versions/00daa8c472ba_.py index c403cec..2e46508 100644 --- a/migrations/versions/00daa8c472ba_.py +++ b/migrations/versions/00daa8c472ba_.py @@ -32,7 +32,9 @@ def upgrade(): def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint(None, "eventsuggestion", type_="foreignkey") + op.drop_constraint( + "eventsuggestion_event_id_fkey", "eventsuggestion", type_="foreignkey" + ) op.create_foreign_key( "eventsuggestion_event_id_fkey", "eventsuggestion", @@ -40,22 +42,22 @@ def downgrade(): ["event_id"], ["id"], ) - op.create_table( - "spatial_ref_sys", - sa.Column("srid", sa.INTEGER(), autoincrement=False, nullable=False), - sa.Column( - "auth_name", sa.VARCHAR(length=256), autoincrement=False, nullable=True - ), - sa.Column("auth_srid", sa.INTEGER(), autoincrement=False, nullable=True), - sa.Column( - "srtext", sa.VARCHAR(length=2048), autoincrement=False, nullable=True - ), - sa.Column( - "proj4text", sa.VARCHAR(length=2048), autoincrement=False, nullable=True - ), - sa.CheckConstraint( - "(srid > 0) AND (srid <= 998999)", name="spatial_ref_sys_srid_check" - ), - sa.PrimaryKeyConstraint("srid", name="spatial_ref_sys_pkey"), - ) + # op.create_table( + # "spatial_ref_sys", + # sa.Column("srid", sa.INTEGER(), autoincrement=False, nullable=False), + # sa.Column( + # "auth_name", sa.VARCHAR(length=256), autoincrement=False, nullable=True + # ), + # sa.Column("auth_srid", sa.INTEGER(), autoincrement=False, nullable=True), + # sa.Column( + # "srtext", sa.VARCHAR(length=2048), autoincrement=False, nullable=True + # ), + # sa.Column( + # "proj4text", sa.VARCHAR(length=2048), autoincrement=False, nullable=True + # ), + # sa.CheckConstraint( + # "(srid > 0) AND (srid <= 998999)", name="spatial_ref_sys_srid_check" + # ), + # sa.PrimaryKeyConstraint("srid", name="spatial_ref_sys_pkey"), + # ) # ### end Alembic commands ### diff --git a/migrations/versions/27da3ceea723_.py b/migrations/versions/27da3ceea723_.py index 9e8ec06..92d178e 100644 --- a/migrations/versions/27da3ceea723_.py +++ b/migrations/versions/27da3ceea723_.py @@ -55,22 +55,22 @@ def downgrade(): op.drop_column("adminunit", "widget_link_color") op.drop_column("adminunit", "widget_font") op.drop_column("adminunit", "widget_background_color") - op.create_table( - "spatial_ref_sys", - sa.Column("srid", sa.INTEGER(), autoincrement=False, nullable=False), - sa.Column( - "auth_name", sa.VARCHAR(length=256), autoincrement=False, nullable=True - ), - sa.Column("auth_srid", sa.INTEGER(), autoincrement=False, nullable=True), - sa.Column( - "srtext", sa.VARCHAR(length=2048), autoincrement=False, nullable=True - ), - sa.Column( - "proj4text", sa.VARCHAR(length=2048), autoincrement=False, nullable=True - ), - sa.CheckConstraint( - "(srid > 0) AND (srid <= 998999)", name="spatial_ref_sys_srid_check" - ), - sa.PrimaryKeyConstraint("srid", name="spatial_ref_sys_pkey"), - ) + # op.create_table( + # "spatial_ref_sys", + # sa.Column("srid", sa.INTEGER(), autoincrement=False, nullable=False), + # sa.Column( + # "auth_name", sa.VARCHAR(length=256), autoincrement=False, nullable=True + # ), + # sa.Column("auth_srid", sa.INTEGER(), autoincrement=False, nullable=True), + # sa.Column( + # "srtext", sa.VARCHAR(length=2048), autoincrement=False, nullable=True + # ), + # sa.Column( + # "proj4text", sa.VARCHAR(length=2048), autoincrement=False, nullable=True + # ), + # sa.CheckConstraint( + # "(srid > 0) AND (srid <= 998999)", name="spatial_ref_sys_srid_check" + # ), + # sa.PrimaryKeyConstraint("srid", name="spatial_ref_sys_pkey"), + # ) # ### end Alembic commands ### diff --git a/migrations/versions/3c5b34fd1156_.py b/migrations/versions/3c5b34fd1156_.py index 75176e3..934baa5 100644 --- a/migrations/versions/3c5b34fd1156_.py +++ b/migrations/versions/3c5b34fd1156_.py @@ -65,7 +65,7 @@ def upgrade(): sa.PrimaryKeyConstraint("id"), ) - migrate_category_to_categories() + upgrade_category_to_categories() # op.drop_table('spatial_ref_sys') op.drop_constraint("event_category_id_fkey", "event", type_="foreignkey") @@ -73,7 +73,7 @@ def upgrade(): # ### end Alembic commands ### -def migrate_category_to_categories(): +def upgrade_category_to_categories(): bind = op.get_bind() session = orm.Session(bind=bind) @@ -83,32 +83,53 @@ def migrate_category_to_categories(): session.commit() +def downgrade_categories_to_category(): + bind = op.get_bind() + session = orm.Session(bind=bind) + + for event in session.query(Event): + event.category = event.categories[0] + + session.commit() + + def downgrade(): # ### commands auto generated by Alembic - please adjust! ### op.add_column( "event", - sa.Column("category_id", sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column("category_id", sa.INTEGER(), autoincrement=False, nullable=True), ) + downgrade_categories_to_category() + op.alter_column( + "event", + sa.Column( + "category_id", + existing_type=sa.INTEGER(), + autoincrement=False, + nullable=False, + ), + ) + op.create_foreign_key( "event_category_id_fkey", "event", "eventcategory", ["category_id"], ["id"] ) - op.create_table( - "spatial_ref_sys", - sa.Column("srid", sa.INTEGER(), autoincrement=False, nullable=False), - sa.Column( - "auth_name", sa.VARCHAR(length=256), autoincrement=False, nullable=True - ), - sa.Column("auth_srid", sa.INTEGER(), autoincrement=False, nullable=True), - sa.Column( - "srtext", sa.VARCHAR(length=2048), autoincrement=False, nullable=True - ), - sa.Column( - "proj4text", sa.VARCHAR(length=2048), autoincrement=False, nullable=True - ), - sa.CheckConstraint( - "(srid > 0) AND (srid <= 998999)", name="spatial_ref_sys_srid_check" - ), - sa.PrimaryKeyConstraint("srid", name="spatial_ref_sys_pkey"), - ) + # op.create_table( + # "spatial_ref_sys", + # sa.Column("srid", sa.INTEGER(), autoincrement=False, nullable=False), + # sa.Column( + # "auth_name", sa.VARCHAR(length=256), autoincrement=False, nullable=True + # ), + # sa.Column("auth_srid", sa.INTEGER(), autoincrement=False, nullable=True), + # sa.Column( + # "srtext", sa.VARCHAR(length=2048), autoincrement=False, nullable=True + # ), + # sa.Column( + # "proj4text", sa.VARCHAR(length=2048), autoincrement=False, nullable=True + # ), + # sa.CheckConstraint( + # "(srid > 0) AND (srid <= 998999)", name="spatial_ref_sys_srid_check" + # ), + # sa.PrimaryKeyConstraint("srid", name="spatial_ref_sys_pkey"), + # ) op.drop_table("event_eventcategories") # ### end Alembic commands ### diff --git a/migrations/versions/e33f225323f3_.py b/migrations/versions/e33f225323f3_.py index 3691fe9..96dd343 100644 --- a/migrations/versions/e33f225323f3_.py +++ b/migrations/versions/e33f225323f3_.py @@ -73,34 +73,46 @@ def upgrade(): def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint(None, "settings", type_="foreignkey") + op.drop_constraint("settings_updated_by_id_fkey", "settings", type_="foreignkey") op.drop_column("settings", "updated_by_id") op.drop_column("settings", "updated_at") - op.drop_constraint(None, "location", type_="foreignkey") + op.drop_constraint("location_updated_by_id_fkey", "location", type_="foreignkey") op.drop_column("location", "updated_by_id") op.drop_column("location", "updated_at") - op.drop_constraint(None, "image", type_="foreignkey") + op.drop_constraint("image_updated_by_id_fkey", "image", type_="foreignkey") op.drop_column("image", "updated_by_id") op.drop_column("image", "updated_at") - op.drop_constraint(None, "eventsuggestion", type_="foreignkey") + op.drop_constraint( + "eventsuggestion_updated_by_id_fkey", "eventsuggestion", type_="foreignkey" + ) op.drop_column("eventsuggestion", "updated_by_id") op.drop_column("eventsuggestion", "updated_at") - op.drop_constraint(None, "eventreferencerequest", type_="foreignkey") + op.drop_constraint( + "eventreferencerequest_updated_by_id_fkey", + "eventreferencerequest", + type_="foreignkey", + ) op.drop_column("eventreferencerequest", "updated_by_id") op.drop_column("eventreferencerequest", "updated_at") - op.drop_constraint(None, "eventreference", type_="foreignkey") + op.drop_constraint( + "eventreference_updated_by_id_fkey", "eventreference", type_="foreignkey" + ) op.drop_column("eventreference", "updated_by_id") op.drop_column("eventreference", "updated_at") - op.drop_constraint(None, "eventplace", type_="foreignkey") + op.drop_constraint( + "eventplace_updated_by_id_fkey", "eventplace", type_="foreignkey" + ) op.drop_column("eventplace", "updated_by_id") op.drop_column("eventplace", "updated_at") - op.drop_constraint(None, "eventorganizer", type_="foreignkey") + op.drop_constraint( + "eventorganizer_updated_by_id_fkey", "eventorganizer", type_="foreignkey" + ) op.drop_column("eventorganizer", "updated_by_id") op.drop_column("eventorganizer", "updated_at") - op.drop_constraint(None, "event", type_="foreignkey") + op.drop_constraint("event_updated_by_id_fkey", "event", type_="foreignkey") op.drop_column("event", "updated_by_id") op.drop_column("event", "updated_at") - op.drop_constraint(None, "adminunit", type_="foreignkey") + op.drop_constraint("adminunit_updated_by_id_fkey", "adminunit", type_="foreignkey") op.drop_column("adminunit", "updated_by_id") op.drop_column("adminunit", "updated_at") # ### end Alembic commands ### diff --git a/project/forms/admin.py b/project/forms/admin.py index 79112cb..0557367 100644 --- a/project/forms/admin.py +++ b/project/forms/admin.py @@ -1,7 +1,7 @@ from flask_babelex import lazy_gettext from flask_wtf import FlaskForm from wtforms import BooleanField, RadioField, StringField, SubmitField, TextAreaField -from wtforms.fields.html5 import EmailField +from wtforms.fields import EmailField from wtforms.validators import DataRequired, Optional from project.forms.widgets import MultiCheckboxField diff --git a/project/forms/admin_unit.py b/project/forms/admin_unit.py index 3fd687d..f0e2429 100644 --- a/project/forms/admin_unit.py +++ b/project/forms/admin_unit.py @@ -1,10 +1,9 @@ from flask_babelex import lazy_gettext from flask_wtf import FlaskForm from wtforms import DecimalField, FormField, StringField, SubmitField, TextAreaField -from wtforms.fields.core import BooleanField -from wtforms.fields.html5 import EmailField, TelField, URLField +from wtforms.fields import BooleanField, EmailField, TelField, URLField from wtforms.validators import DataRequired, Length, Optional, Regexp -from wtforms.widgets.html5 import ColorInput +from wtforms.widgets import ColorInput from project.forms.common import Base64ImageForm from project.forms.widgets import HTML5StringField diff --git a/project/forms/admin_unit_member.py b/project/forms/admin_unit_member.py index 7797ba9..8213016 100644 --- a/project/forms/admin_unit_member.py +++ b/project/forms/admin_unit_member.py @@ -1,7 +1,7 @@ from flask_babelex import lazy_gettext from flask_wtf import FlaskForm from wtforms import SubmitField -from wtforms.fields.html5 import EmailField +from wtforms.fields import EmailField from wtforms.validators import DataRequired from project.forms.widgets import MultiCheckboxField diff --git a/project/forms/common.py b/project/forms/common.py index 02cd531..4a6ab37 100644 --- a/project/forms/common.py +++ b/project/forms/common.py @@ -32,8 +32,8 @@ class Base64ImageForm(BaseImageForm): obj.data, obj.encoding_format ) - def validate(self): - result = super().validate() + def validate(self, extra_validators=None): + result = super().validate(extra_validators) if self.image_base64.data: image = get_image_from_base64_str(self.image_base64.data) diff --git a/project/forms/event.py b/project/forms/event.py index a25ee16..fab6e2a 100644 --- a/project/forms/event.py +++ b/project/forms/event.py @@ -14,8 +14,7 @@ from wtforms import ( SubmitField, TextAreaField, ) -from wtforms.fields.core import FieldList -from wtforms.fields.html5 import EmailField, URLField +from wtforms.fields import EmailField, FieldList, URLField from wtforms.validators import DataRequired, Length, Optional from project.forms.common import Base64ImageForm, distance_choices, event_rating_choices @@ -72,8 +71,8 @@ class EventDateDefinitionFormMixin: class EventDateDefinitionForm(FlaskForm, EventDateDefinitionFormMixin): - def validate(self): - result = super().validate() + def validate(self, extra_validators=None): + result = super().validate(extra_validators) if not self.validate_date_definition(): result = False @@ -270,8 +269,8 @@ class BaseEventForm(SharedEventForm): ), ) - def validate(self): - result = super().validate() + def validate(self, extra_validators=None): + result = super().validate(extra_validators) if self.co_organizer_ids.data and self.organizer_id.data: if self.organizer_id.data in self.co_organizer_ids.data: @@ -342,8 +341,8 @@ class CreateEventForm(BaseEventForm): PublicStatus.published if self.submit.data else PublicStatus.draft ) - def validate(self): - result = super().validate() + def validate(self, extra_validators=None): + result = super().validate(extra_validators) if ( not self.event_place_id.data or self.event_place_id.data == 0 diff --git a/project/forms/event_place.py b/project/forms/event_place.py index fea06f3..2790592 100644 --- a/project/forms/event_place.py +++ b/project/forms/event_place.py @@ -1,7 +1,7 @@ from flask_babelex import lazy_gettext from flask_wtf import FlaskForm from wtforms import DecimalField, FormField, StringField, SubmitField, TextAreaField -from wtforms.fields.html5 import URLField +from wtforms.fields import URLField from wtforms.validators import DataRequired, Optional from project.forms.common import Base64ImageForm diff --git a/project/forms/event_suggestion.py b/project/forms/event_suggestion.py index 9a6492a..0b7c090 100644 --- a/project/forms/event_suggestion.py +++ b/project/forms/event_suggestion.py @@ -7,7 +7,7 @@ from wtforms import ( StringField, SubmitField, ) -from wtforms.fields.html5 import EmailField, TelField +from wtforms.fields import EmailField, TelField from wtforms.validators import DataRequired, Optional from project.forms.common import get_accept_tos_markup @@ -110,8 +110,8 @@ class CreateEventSuggestionForm(SharedEventForm, EventDateDefinitionFormMixin): else: field.populate_obj(obj, name) - def validate(self): - result = super().validate() + def validate(self, extra_validators=None): + result = super().validate(extra_validators) if not self.validate_date_definition(): result = False diff --git a/project/forms/organizer.py b/project/forms/organizer.py index 23ed23a..a5aef62 100644 --- a/project/forms/organizer.py +++ b/project/forms/organizer.py @@ -1,7 +1,7 @@ from flask_babelex import lazy_gettext from flask_wtf import FlaskForm from wtforms import DecimalField, FormField, StringField, SubmitField -from wtforms.fields.html5 import EmailField, TelField, URLField +from wtforms.fields import EmailField, TelField, URLField from wtforms.validators import DataRequired, Optional from project.forms.common import Base64ImageForm diff --git a/project/forms/reference_request.py b/project/forms/reference_request.py index c1e5cbc..49b010e 100644 --- a/project/forms/reference_request.py +++ b/project/forms/reference_request.py @@ -1,7 +1,7 @@ from flask_babelex import lazy_gettext from flask_wtf import FlaskForm from wtforms import SelectField, StringField, SubmitField -from wtforms.fields.core import BooleanField +from wtforms.fields import BooleanField from wtforms.validators import DataRequired, Optional from project.forms.common import event_rating_choices diff --git a/project/forms/security.py b/project/forms/security.py index b1e2aaa..7e99da0 100644 --- a/project/forms/security.py +++ b/project/forms/security.py @@ -35,8 +35,8 @@ class ExtendedConfirmRegisterForm(ConfirmRegisterForm): class ExtendedLoginForm(LoginForm): - def validate(self): - result = super().validate() + def validate(self, **kwargs): + result = super().validate(**kwargs) if not result and self.requires_confirmation: flash_message( diff --git a/project/forms/widgets.py b/project/forms/widgets.py index b2bb123..a82a439 100644 --- a/project/forms/widgets.py +++ b/project/forms/widgets.py @@ -3,7 +3,7 @@ from datetime import datetime from flask_babelex import gettext, to_user_timezone from markupsafe import Markup from wtforms import DateTimeField, SelectField, SelectMultipleField -from wtforms.fields.core import StringField +from wtforms.fields import StringField from wtforms.validators import Length, StopValidation from wtforms.widgets import CheckboxInput, ListWidget, html_params @@ -164,14 +164,14 @@ class HTML5StringField(StringField): self, label=None, validators=None, - filters=tuple(), + filters=(), description="", id=None, default=None, widget=None, render_kw=None, + name=None, _form=None, - _name=None, _prefix="", _translations=None, _meta=None, @@ -194,8 +194,8 @@ class HTML5StringField(StringField): default, widget, render_kw, + name, _form, - _name, _prefix, _translations, _meta, diff --git a/project/models/admin_unit.py b/project/models/admin_unit.py index 125c6ef..c29b8b3 100644 --- a/project/models/admin_unit.py +++ b/project/models/admin_unit.py @@ -1,4 +1,4 @@ -from flask_security import RoleMixin +from flask_security import AsaList, RoleMixin from sqlalchemy import ( Boolean, Column, @@ -14,6 +14,7 @@ from sqlalchemy import ( ) from sqlalchemy.event import listens_for from sqlalchemy.ext.hybrid import hybrid_property +from sqlalchemy.ext.mutable import MutableList from sqlalchemy.orm import aliased, backref, deferred, relationship from sqlalchemy.orm.relationships import remote from sqlalchemy.schema import CheckConstraint @@ -37,7 +38,7 @@ class AdminUnitMemberRole(db.Model, RoleMixin): name = Column(String(80), unique=True) title = Column(Unicode(255)) description = Column(String(255)) - permissions = Column(UnicodeText()) + permissions = Column(MutableList.as_mutable(AsaList()), nullable=True) class AdminUnitMember(db.Model): @@ -299,7 +300,7 @@ class AdminUnit(db.Model, TrackableMixin): AdminUnitRelation.source_admin_unit_id == SourceAdminUnit.id, ) return ( - select([func.count()]) + select(func.count()) .select_from(j) .where( and_( @@ -308,7 +309,7 @@ class AdminUnit(db.Model, TrackableMixin): SourceAdminUnit.can_verify_other, ) ) - .as_scalar() + .scalar_subquery() > 0 ) diff --git a/project/models/event.py b/project/models/event.py index abec5ec..0b055ce 100644 --- a/project/models/event.py +++ b/project/models/event.py @@ -91,11 +91,11 @@ class Event(db.Model, TrackableMixin, EventMixin): @min_start.expression def min_start(cls): return ( - select([EventDateDefinition.start]) + select(EventDateDefinition.start) .where(EventDateDefinition.event_id == cls.id) .order_by(EventDateDefinition.start) .limit(1) - .as_scalar() + .scalar_subquery() ) @hybrid_property @@ -108,7 +108,7 @@ class Event(db.Model, TrackableMixin, EventMixin): @is_recurring.expression def is_recurring(cls): return ( - select([func.count()]) + select(func.count()) .select_from(EventDateDefinition.__table__) .where( and_( @@ -116,7 +116,7 @@ class Event(db.Model, TrackableMixin, EventMixin): func.coalesce(EventDateDefinition.recurrence_rule, "") != "", ) ) - .as_scalar() + .scalar_subquery() ) > 0 date_definitions = relationship( diff --git a/project/models/event_mixin.py b/project/models/event_mixin.py index 7c2e684..81d580f 100644 --- a/project/models/event_mixin.py +++ b/project/models/event_mixin.py @@ -9,8 +9,7 @@ from sqlalchemy import ( Unicode, UnicodeText, ) -from sqlalchemy.ext.declarative import declared_attr -from sqlalchemy.orm import relationship +from sqlalchemy.orm import declared_attr, relationship from project.dbtypes import IntegerEnum from project.models.functions import create_tsvector diff --git a/project/models/trackable_mixin.py b/project/models/trackable_mixin.py index 9a3a06d..d2323d8 100644 --- a/project/models/trackable_mixin.py +++ b/project/models/trackable_mixin.py @@ -1,8 +1,7 @@ import datetime from sqlalchemy import Column, DateTime, ForeignKey -from sqlalchemy.ext.declarative import declared_attr -from sqlalchemy.orm import deferred, relationship +from sqlalchemy.orm import declared_attr, deferred, relationship from project.models.functions import _current_user_id_or_none diff --git a/project/models/user.py b/project/models/user.py index ef7c206..c7db44f 100644 --- a/project/models/user.py +++ b/project/models/user.py @@ -1,7 +1,7 @@ import datetime from flask_dance.consumer.storage.sqla import OAuthConsumerMixin -from flask_security import RoleMixin, UserMixin +from flask_security import AsaList, RoleMixin, UserMixin from sqlalchemy import ( Boolean, Column, @@ -10,9 +10,9 @@ from sqlalchemy import ( Integer, String, Unicode, - UnicodeText, UniqueConstraint, ) +from sqlalchemy.ext.mutable import MutableList from sqlalchemy.orm import backref, deferred, relationship from project import db @@ -31,7 +31,7 @@ class Role(db.Model, RoleMixin): name = Column(String(80), unique=True) title = Column(Unicode(255)) description = Column(String(255)) - permissions = Column(UnicodeText()) + permissions = Column(MutableList.as_mutable(AsaList()), nullable=True) class User(db.Model, UserMixin): diff --git a/project/services/admin_unit.py b/project/services/admin_unit.py index 6ede52b..bf3d1ea 100644 --- a/project/services/admin_unit.py +++ b/project/services/admin_unit.py @@ -118,8 +118,7 @@ def upsert_admin_unit_member_role(role_name, role_title, permissions): db.session.add(result) result.title = role_title - result.remove_permissions(result.get_permissions()) - result.add_permissions(permissions) + result.permissions = permissions return result diff --git a/project/services/event.py b/project/services/event.py index e0f4222..25e8a09 100644 --- a/project/services/event.py +++ b/project/services/event.py @@ -7,7 +7,14 @@ from flask import url_for from flask_babelex import format_date, format_time, gettext from icalendar.prop import vDDDLists from sqlalchemy import and_, case, func, or_ -from sqlalchemy.orm import aliased, contains_eager, defaultload, joinedload, lazyload +from sqlalchemy.orm import ( + aliased, + contains_eager, + defaultload, + joinedload, + lazyload, + undefer_group, +) from sqlalchemy.sql import extract from project import app, db @@ -194,7 +201,7 @@ def get_event_dates_query(params): result = ( result.options( - contains_eager(EventDate.event) + joinedload(EventDate.event) .contains_eager(Event.event_place) .contains_eager(EventPlace.location), joinedload(EventDate.event) @@ -216,12 +223,10 @@ def get_event_dates_query(params): if admin_unit_reference: result = result.order_by( case( - [ - ( - admin_unit_reference.rating.isnot(None), - admin_unit_reference.rating, - ), - ], + ( + admin_unit_reference.rating.isnot(None), + admin_unit_reference.rating, + ), else_=Event.rating, ).desc() ) @@ -238,12 +243,12 @@ def get_event_date_with_details_or_404(event_id): .join(Event.event_place, isouter=True) .join(EventPlace.location, isouter=True) .options( - contains_eager(EventDate.event) + joinedload(EventDate.event) .contains_eager(Event.event_place) .contains_eager(EventPlace.location), joinedload(EventDate.event).undefer_group("trackable"), # Place - defaultload(EventDate.event) + joinedload(EventDate.event) .defaultload(Event.event_place) .joinedload(EventPlace.photo), # Category @@ -254,19 +259,19 @@ def get_event_date_with_details_or_404(event_id): joinedload(EventDate.event) .joinedload(Event.organizer) .undefer_group("detail") - .undefer("logo_id") + .undefer(EventOrganizer.logo_id) .joinedload(EventOrganizer.logo), # Photo joinedload(EventDate.event).joinedload(Event.photo), # Admin unit joinedload(EventDate.event) .joinedload(Event.admin_unit) - .undefer("logo_id") + .undefer(AdminUnit.logo_id) .undefer_group("detail") .undefer_group("widget") .joinedload(AdminUnit.location), # Admin unit logo - defaultload(EventDate.event) + joinedload(EventDate.event) .defaultload(Event.admin_unit) .joinedload(AdminUnit.logo), ) @@ -277,12 +282,12 @@ def get_event_date_with_details_or_404(event_id): def get_event_with_details_or_404(event_id): return ( - Event.query.join(EventPlace, isouter=True) + Event.query.join(Event.event_place, isouter=True) .join(Location, isouter=True) .options( - contains_eager(Event.event_place).contains_eager(EventPlace.location), - defaultload(Event).undefer_group("trackable"), + undefer_group("trackable"), # Place + joinedload(Event.event_place).contains_eager(EventPlace.location), joinedload(Event.event_place).joinedload(EventPlace.photo), # Category joinedload(Event.categories).load_only( @@ -291,13 +296,13 @@ def get_event_with_details_or_404(event_id): # Organizer joinedload(Event.organizer) .undefer_group("detail") - .undefer("logo_id") + .undefer(EventOrganizer.logo_id) .joinedload(EventOrganizer.logo), # Photo joinedload(Event.photo), # Admin unit with location joinedload(Event.admin_unit) - .undefer("logo_id") + .undefer(AdminUnit.logo_id) .undefer_group("detail") .undefer_group("widget") .joinedload(AdminUnit.location), @@ -635,8 +640,10 @@ def create_ical_events_for_search( def update_recurring_dates(): + from sqlalchemy import text + # Setting the timezone is neccessary for cli command - db.session.execute("SET timezone TO :val;", {"val": berlin_tz.zone}) + db.session.execute(text("SET timezone TO :val;"), {"val": berlin_tz.zone}) events = get_recurring_events() diff --git a/project/services/user.py b/project/services/user.py index bdfa294..841c25a 100644 --- a/project/services/user.py +++ b/project/services/user.py @@ -42,8 +42,7 @@ def set_roles_for_user(email, roles): def upsert_user_role(role_name, role_title, permissions): role = user_datastore.find_or_create_role(role_name) role.title = role_title - role.remove_permissions(role.get_permissions()) - role.add_permissions(permissions) + role.permissions = permissions return role diff --git a/project/views/event.py b/project/views/event.py index 704f947..76dc0b5 100644 --- a/project/views/event.py +++ b/project/views/event.py @@ -299,6 +299,9 @@ def prepare_event_place(form): if place: form.event_place_id.choices = [(place.id, get_place_str(place))] + if not form.event_place_id.choices: + form.event_place_id.choices = [] + def prepare_organizer(form): if form.organizer_id.data and form.organizer_id.data > 0: @@ -313,6 +316,12 @@ def prepare_organizer(form): ).all() form.co_organizer_ids.choices = [(o.id, o.name) for o in co_organizers] + if not form.organizer_id.choices: + form.organizer_id.choices = [] + + if not form.co_organizer_ids.choices: + form.co_organizer_ids.choices = [] + def prepare_event_form(form): form.category_ids.choices = get_event_category_choices() diff --git a/project/views/event_date.py b/project/views/event_date.py index e8970e8..100a179 100644 --- a/project/views/event_date.py +++ b/project/views/event_date.py @@ -26,6 +26,7 @@ from project.views.utils import ( def prepare_event_date_form(form): form.category_id.choices = get_event_category_choices() form.category_id.choices.insert(0, (0, "")) + form.location.choices = [] @app.route("/eventdates") diff --git a/project/views/manage.py b/project/views/manage.py index 6d1a6c3..7402c0b 100644 --- a/project/views/manage.py +++ b/project/views/manage.py @@ -176,6 +176,8 @@ def manage_admin_unit_events(id): if form.location.data: # pragma: no cover form.location.choices = [(form.location.data, form.location_name.data)] + else: + form.location.choices = [] if form.validate(): form.populate_obj(params) diff --git a/project/views/root.py b/project/views/root.py index 6e7ee65..4034031 100644 --- a/project/views/root.py +++ b/project/views/root.py @@ -4,6 +4,7 @@ import os.path from flask import render_template, request, send_from_directory, url_for from flask_babelex import gettext from markupsafe import Markup +from sqlalchemy import text from project import ( app, @@ -40,7 +41,8 @@ def home(): @app.route("/up") def up(): - db.engine.execute("SELECT 1") + with db.engine.connect() as conn: + conn.execute(text("SELECT 1")) if "REDIS_URL" in app.config and app.config["REDIS_URL"]: # pragma: no cover celery.control.ping() diff --git a/tests/seeder.py b/tests/seeder.py index e07e083..222610c 100644 --- a/tests/seeder.py +++ b/tests/seeder.py @@ -305,14 +305,19 @@ class Seeder(object): def get_event_category_id(self, category_name): from project.services.event import get_event_category - category = get_event_category(category_name) + with self._app.app_context(): + category = get_event_category(category_name) + return category.id def get_event_date_id(self, event_id): from project.models import Event - event = Event.query.get(event_id) - return event.dates[0].id + with self._app.app_context(): + event = Event.query.get(event_id) + event_date_id = event.dates[0].id + + return event_date_id def create_event( self, diff --git a/tests/test___init__.py b/tests/test___init__.py index 057f05b..941aa2f 100644 --- a/tests/test___init__.py +++ b/tests/test___init__.py @@ -13,9 +13,11 @@ def test_mail_server(): def drop_db(db): - db.drop_all() - db.engine.execute("DROP TABLE IF EXISTS alembic_version;") - db.engine.execute("DROP TABLE IF EXISTS analytics;") + with db.engine.connect() as conn: + with conn.begin(): + db.drop_all() + conn.execute(sqlalchemy.text("DROP TABLE IF EXISTS alembic_version;")) + conn.execute(sqlalchemy.text("DROP TABLE IF EXISTS analytics;")) def populate_db(db): @@ -33,7 +35,9 @@ BEGIN INSERT INTO event (name, admin_unit_id, event_place_id, organizer_id, start) VALUES ('Event', admin_unit_id, event_place_id, organizer_id, current_timestamp) RETURNING id INTO event_id; END $$; """ - db.engine.execute(sqlalchemy.text(sql).execution_options(autocommit=True)) + with db.engine.connect() as conn: + with conn.begin(): + conn.execute(sqlalchemy.text(sql).execution_options(autocommit=True)) def test_migrations(app, seeder): @@ -54,6 +58,7 @@ def test_migrations(app, seeder): seeder.create_event_suggestion(admin_unit_id) seeder.create_any_reference(admin_unit_id) seeder.create_reference_request(event_id, admin_unit_id) + db.session.commit() downgrade() diff --git a/tests/utils.py b/tests/utils.py index dbd6d28..c2f00ba 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -64,28 +64,28 @@ class UtilActions(object): response = self._client.get("/login") assert response.status_code == 200 - with self._client: - response = self._client.post( - "/login", - data={ - "email": email, - "password": password, - "csrf_token": self.get_csrf(response), - "submit": "Anmelden", - }, - follow_redirects=follow_redirects, - ) - - if follow_redirects: - assert response.status_code == 200 - else: - assert response.status_code == 302 - - assert g.identity.user.email == email - with self._app.app_context(): - user = find_user_by_email(email) - user_id = user.id + with self._client: + response = self._client.post( + "/login", + data={ + "email": email, + "password": password, + "csrf_token": self.get_csrf(response), + "submit": "Anmelden", + }, + follow_redirects=follow_redirects, + ) + + if follow_redirects: + assert response.status_code == 200 + else: + assert response.status_code == 302 + + assert g.identity.user.email == email + + user = find_user_by_email(email) + user_id = user.id return user_id @@ -300,8 +300,10 @@ class UtilActions(object): def assert_response_redirect(self, response, endpoint, **values): assert response.status_code == 302 - redirect_url = "http://localhost" + self.get_url(endpoint, **values) - assert response.headers["Location"] == redirect_url + response_location = response.headers["Location"] + redirect_url = self.get_url(endpoint, **values) + absolute_url = "http://localhost" + redirect_url + assert response_location == redirect_url or response_location == absolute_url def assert_response_contains_alert(self, response, category, message=None): assert response.status_code == 200 diff --git a/tests/views/test_admin_unit_member_invitation.py b/tests/views/test_admin_unit_member_invitation.py index b50cc2d..df04474 100644 --- a/tests/views/test_admin_unit_member_invitation.py +++ b/tests/views/test_admin_unit_member_invitation.py @@ -155,8 +155,7 @@ def test_read_decline(client, app, db, utils, seeder): "decline": "Ablehnen", }, ) - assert response.status_code == 302 - assert response.headers["Location"] == "http://localhost/manage" + utils.assert_response_redirect(response, "manage") with app.app_context(): from project.services.admin_unit import ( @@ -211,8 +210,7 @@ def test_read_new_member_not_registered(client, app, utils, seeder): url = "/invitations/%d" % invitation_id response = client.get(url) - assert response.status_code == 302 - assert response.headers["Location"] == "http://localhost/register" + utils.assert_response_redirect(response, "security.register") def test_read_new_member_not_authenticated(client, app, utils, seeder): @@ -226,8 +224,7 @@ def test_read_new_member_not_authenticated(client, app, utils, seeder): url = "/invitations/%d" % invitation_id response = client.get(url) - assert response.status_code == 302 - assert response.headers["Location"].startswith("http://localhost/login") + utils.assert_response_redirect(response, "security.login", next=url) @pytest.mark.parametrize("user_exists", [True, False]) @@ -245,9 +242,8 @@ def test_read_currentUserDoesNotMatchInvitationEmail( seeder.create_user(email) url = "/invitations/%d" % invitation_id - environ, response = client.get(url, follow_redirects=True, as_tuple=True) + response = client.get(url, follow_redirects=True) - assert environ["REQUEST_URI"] == "/profile" utils.assert_response_ok(response) utils.assert_response_contains( response, "Die Einladung wurde für einen anderen Nutzer ausgestellt." diff --git a/tests/views/test_user.py b/tests/views/test_user.py index 565b706..5f6abe2 100644 --- a/tests/views/test_user.py +++ b/tests/views/test_user.py @@ -25,8 +25,7 @@ def test_organization_invitation_not_authenticated(client, app, utils, seeder): seeder.create_user("invited@test.de") url = utils.get_url("user_organization_invitation", id=invitation_id) response = client.get(url) - assert response.status_code == 302 - assert response.headers["Location"].startswith("http://localhost/login") + utils.assert_response_redirect(response, "security.login", next=url) @pytest.mark.parametrize("user_exists", [True, False]) @@ -40,9 +39,8 @@ def test_organization_invitation_currentUserDoesNotMatchInvitationEmail( seeder.create_user("invited@test.de") url = utils.get_url("user_organization_invitation", id=invitation_id) - environ, response = client.get(url, follow_redirects=True, as_tuple=True) + response = client.get(url, follow_redirects=True) - assert environ["REQUEST_URI"] == "/profile" utils.assert_response_ok(response) utils.assert_response_contains( response, "Die Einladung wurde für einen anderen Nutzer ausgestellt."