Internal/modules (#1)

* Restructured app
* Added Travis CI
* Added 'Deploy to heroku' button
This commit is contained in:
Daniel Grams 2020-11-13 12:24:26 +01:00 committed by GitHub
parent 2434ea24f6
commit d63f340384
231 changed files with 864 additions and 705 deletions

1
.flaskenv Normal file
View File

@ -0,0 +1 @@
FLASK_APP=bootstrap.py

18
.travis.yml Normal file
View File

@ -0,0 +1,18 @@
language: python
python:
- "3.7"
addons:
apt:
packages:
- postgresql-10-postgis-2.5
- postgresql-10-postgis-2.5-scripts
postgresql: '10'
services:
- postgresql
install:
- pip install -r requirements.txt
before_script:
- psql -c 'create database gsevpt_tests;' -U postgres
- psql -c 'create extension postgis;' -U postgres -d gsevpt_tests
script:
- pytest

2
.vscode/launch.json vendored
View File

@ -10,7 +10,7 @@
"request": "launch", "request": "launch",
"module": "flask", "module": "flask",
"env": { "env": {
"FLASK_APP": "app.py", "FLASK_APP": "project",
"FLASK_ENV": "development", "FLASK_ENV": "development",
"FLASK_DEBUG": "1" "FLASK_DEBUG": "1"
}, },

View File

@ -1,2 +1,2 @@
release: python manage.py db upgrade release: python manage.py db upgrade
web: gunicorn app:app --log-file=- web: gunicorn project:app --log-file=-

110
README.md
View File

@ -1,74 +1,70 @@
[![Build Status](https://travis-ci.com/DanielGrams/gsevpt.svg?branch=master)](https://travis-ci.com/DanielGrams/gsevpt)
# Goslar Event Prototype # Goslar Event Prototype
Website prototype using Python, Flask and Postgres running on Heroku. Website prototype using Python, Flask and Postgres running on Heroku.
## Setup ## Automatic Deployment
### Environment variables [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)
Create `.env` file in the root directory and define the following variables: ## Manual Installation
``` ### Requirements
DATABASE_URL=
GOOGLE_OAUTH_CLIENT_ID= - Python 3.7
GOOGLE_OAUTH_CLIENT_SECRET= - pip
OAUTHLIB_INSECURE_TRANSPORT=true - Postgres with postgis
OAUTHLIB_RELAX_TOKEN_SCOPE=true
GOOGLE_MAPS_API_KEY= ### Create database
```sh
psql -c 'create database gsevpt;' -U postgres
``` ```
### Install and run ### Install and run
``` ```sh
export DATABASE_URL="postgresql://postgres@localhost/gsevpt"
pip install -r requirements.txt pip install -r requirements.txt
python manage.py db upgrade
flask run --host 0.0.0.0 flask run --host 0.0.0.0
``` ```
## Configuration
Create `.env` file in the root directory or pass as environment variables.
### Security
| Variable | Function |
| --- | --- |
| SECRET_KEY | A secret key for verifying the integrity of signed cookies. Generate a nice key using secrets.token_urlsafe(). |
| SECURITY_PASSWORD_HASH | Bcrypt is set as default SECURITY_PASSWORD_HASH, which requires a salt. Generate a good salt using: secrets.SystemRandom().getrandbits(128). |
### Send notifications via Mail
| Variable | Function |
| --- | --- |
| MAIL_DEFAULT_SENDER | see <https://pythonhosted.org/Flask-Mail/> |
| MAIL_PASSWORD | " |
| MAIL_PORT | " |
| MAIL_SERVER | " |
| MAIL_USERNAME | " |
### Login with Google via OAuth
| Variable | Function |
| --- | --- |
| GOOGLE_OAUTH_CLIENT_ID | Client Id |
| GOOGLE_OAUTH_CLIENT_SECRET | Secret |
### Resolve addresses with Google Maps
| Variable | Function |
| --- | --- |
| GOOGLE_MAPS_API_KEY | API Key with Places API enabled |
## Development ## Development
### Database [Development](doc/development.md)
```
python manage.py db init
python manage.py db migrate
python manage.py db upgrade
```
#### Local development only
```
python manage.py db history
python manage.py db downgrade
// reset git: migrations/versions
python manage.py db migrate
python manage.py db upgrade
```
### Kill local detached server
```
lsof -i :5000
kill -9 PIDNUMBER
```
### i18n
<https://pythonhosted.org/Flask-BabelEx/>
#### Init
```
pybabel extract -F babel.cfg -o messages.pot . && pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot . && pybabel init -i messages.pot -d translations -l de
```
#### Neue msgid's scannen und in *.po mergen
```
pybabel extract -F babel.cfg -o messages.pot . && pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot . && pybabel update -i messages.pot -d translations
```
#### Nach dem Übersetzen
```
pybabel compile -d translations
```

18
app.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "Goslar Event Prototype",
"description": "Website prototype using Python, Flask and Postgres running on Heroku.",
"image": "heroku/python",
"keywords": ["python" ],
"repository": "https://github.com/DanielGrams/gsevpt",
"addons": [ "heroku-postgresql" ],
"env": {
"SECRET_TOKEN": {
"description": "A secret key for verifying the integrity of signed cookies.",
"generator": "secret"
},
"SECURITY_PASSWORD_SALT": {
"description": "Bcrypt salt for encrypting passwords.",
"generator": "secret"
}
}
}

View File

@ -1,4 +1,4 @@
[ignore: env/**] [ignore: env/**]
[python: **.py] [python: app/**.py]
[jinja2: templates/**.html] [jinja2: app/templates/**.html]
extensions=jinja2.ext.autoescape,jinja2.ext.with_ extensions=jinja2.ext.autoescape,jinja2.ext.with_

1
bootstrap.py Normal file
View File

@ -0,0 +1 @@
from project import app

52
doc/development.md Normal file
View File

@ -0,0 +1,52 @@
# Development
## Tests
### Create test database
```sh
psql -c 'create database gsevpt_tests;' -U postgres
psql -c 'create extension postgis;' -d gsevpt_tests -U postgres
```
### Run tests
```sh
pytest
```
## Database
### Create new revision
```sh
python manage.py db migrate
```
### Upgrade database
```sh
python manage.py db upgrade
```
## i18n
<https://pythonhosted.org/Flask-BabelEx/>
### Init
```sh
pybabel extract -F babel.cfg -o messages.pot . && pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot . && pybabel init -i messages.pot -d app/translations -l de
```
### Extract new msgid's and merge into *.po files
```sh
pybabel extract -F babel.cfg -o messages.pot . && pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot . && pybabel update -i messages.pot -d app/translations
```
#### Compile after translation is done
```sh
pybabel compile -d app/translations
```

View File

@ -1,6 +1,6 @@
from flask_script import Manager, Command from flask_script import Manager, Command
from flask_migrate import Migrate, MigrateCommand from flask_migrate import Migrate, MigrateCommand
from app import app, db from project import app, db
migrate = Migrate(app, db) migrate = Migrate(app, db)
manager = Manager(app) manager = Manager(app)

View File

@ -8,7 +8,7 @@ Create Date: ${create_date}
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
${imports if imports else ""} ${imports if imports else ""}
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-10-27 20:58:57.392619
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,8 +8,8 @@ Create Date: 2020-10-22 17:59:27.823624
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
from models import EventRejectionReason, EventReviewStatus from project.models import EventRejectionReason, EventReviewStatus
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = '021f602d9965' revision = '021f602d9965'
@ -27,8 +27,8 @@ def upgrade():
sa.Column('start', sa.DateTime(timezone=True), nullable=False), sa.Column('start', sa.DateTime(timezone=True), nullable=False),
sa.Column('description', sa.UnicodeText(), nullable=True), sa.Column('description', sa.UnicodeText(), nullable=True),
sa.Column('external_link', sa.String(length=255), nullable=True), sa.Column('external_link', sa.String(length=255), nullable=True),
sa.Column('review_status', db.IntegerEnum(EventReviewStatus), nullable=True), sa.Column('review_status', dbtypes.IntegerEnum(EventReviewStatus), nullable=True),
sa.Column('rejection_resaon', db.IntegerEnum(EventRejectionReason), nullable=True), sa.Column('rejection_resaon', dbtypes.IntegerEnum(EventRejectionReason), nullable=True),
sa.Column('contact_name', sa.Unicode(length=255), nullable=False), sa.Column('contact_name', sa.Unicode(length=255), nullable=False),
sa.Column('contact_email', sa.Unicode(length=255), nullable=True), sa.Column('contact_email', sa.Unicode(length=255), nullable=True),
sa.Column('contact_phone', sa.Unicode(length=255), nullable=True), sa.Column('contact_phone', sa.Unicode(length=255), nullable=True),

View File

@ -7,8 +7,9 @@ Create Date: 2020-10-02 09:29:12.932229
""" """
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.sql import text
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
from geoalchemy2.types import Geometry from geoalchemy2.types import Geometry
@ -20,6 +21,10 @@ depends_on = None
def upgrade(): def upgrade():
bind = op.get_bind()
bind.execute(text("create extension if not exists postgis;"))
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
#op.drop_table('spatial_ref_sys') #op.drop_table('spatial_ref_sys')
op.add_column('location', sa.Column('coordinate', Geometry(geometry_type='POINT', from_text='ST_GeomFromEWKT', name='geometry'), nullable=True)) op.add_column('location', sa.Column('coordinate', Geometry(geometry_type='POINT', from_text='ST_GeomFromEWKT', name='geometry'), nullable=True))

View File

@ -8,7 +8,7 @@ Create Date: 2020-10-18 11:55:12.315808
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-11-08 16:14:01.866196
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -9,7 +9,7 @@ from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy import orm from sqlalchemy import orm
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-07-17 19:54:25.703175
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-09-18 15:04:03.359403
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-10-27 20:31:42.566357
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-09-29 15:38:44.033998
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-07-13 19:01:04.770613
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-07-30 13:13:44.694716
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,8 +8,8 @@ Create Date: 2020-08-01 15:43:11.377833
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
from models import EventRejectionReason, EventReviewStatus from project.models import EventRejectionReason, EventReviewStatus
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
@ -21,8 +21,8 @@ depends_on = None
def upgrade(): def upgrade():
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
op.add_column('event', sa.Column('rejection_resaon', db.IntegerEnum(EventRejectionReason), nullable=True)) op.add_column('event', sa.Column('rejection_resaon', dbtypes.IntegerEnum(EventRejectionReason), nullable=True))
op.add_column('event', sa.Column('review_status', db.IntegerEnum(EventReviewStatus), nullable=True)) op.add_column('event', sa.Column('review_status', dbtypes.IntegerEnum(EventReviewStatus), nullable=True))
op.drop_column('event', 'verified') op.drop_column('event', 'verified')
# ### end Alembic commands ### # ### end Alembic commands ###

View File

@ -8,7 +8,7 @@ Create Date: 2020-07-28 16:29:41.403957
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-10-01 11:09:16.765736
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-10-23 15:51:36.330825
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,8 +8,8 @@ Create Date: 2020-09-28 10:38:46.424791
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
from models import FeaturedEventRejectionReason, FeaturedEventReviewStatus from project.models import FeaturedEventRejectionReason, FeaturedEventReviewStatus
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
@ -25,8 +25,8 @@ def upgrade():
sa.Column('created_at', sa.DateTime(), nullable=True), sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False), sa.Column('id', sa.Integer(), nullable=False),
sa.Column('event_id', sa.Integer(), nullable=False), sa.Column('event_id', sa.Integer(), nullable=False),
sa.Column('review_status', db.IntegerEnum(FeaturedEventReviewStatus), nullable=True), sa.Column('review_status', dbtypes.IntegerEnum(FeaturedEventReviewStatus), nullable=True),
sa.Column('rejection_resaon', db.IntegerEnum(FeaturedEventRejectionReason), nullable=True), sa.Column('rejection_resaon', dbtypes.IntegerEnum(FeaturedEventRejectionReason), nullable=True),
sa.Column('rating', sa.Integer(), nullable=True), sa.Column('rating', sa.Integer(), nullable=True),
sa.Column('created_by_id', sa.Integer(), nullable=True), sa.Column('created_by_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ), sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ),

View File

@ -8,7 +8,7 @@ Create Date: 2020-09-24 18:53:02.861732
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,8 +8,8 @@ Create Date: 2020-10-18 13:06:47.639083
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
from models import EventRejectionReason, EventReviewStatus from project.models import EventRejectionReason, EventReviewStatus
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-07-17 11:27:53.084732
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-07-31 16:30:19.185088
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,9 +8,9 @@ Create Date: 2020-09-29 16:53:02.520125
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
from models import EventReferenceRequestRejectionReason, EventReferenceRequestReviewStatus from project.models import EventReferenceRequestRejectionReason, EventReferenceRequestReviewStatus
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = 'a75bd9c8ad3a' revision = 'a75bd9c8ad3a'
@ -38,8 +38,8 @@ def upgrade():
sa.Column('id', sa.Integer(), nullable=False), sa.Column('id', sa.Integer(), nullable=False),
sa.Column('event_id', sa.Integer(), nullable=False), sa.Column('event_id', sa.Integer(), nullable=False),
sa.Column('admin_unit_id', sa.Integer(), nullable=False), sa.Column('admin_unit_id', sa.Integer(), nullable=False),
sa.Column('review_status', db.IntegerEnum(EventReferenceRequestReviewStatus), nullable=True), sa.Column('review_status', dbtypes.IntegerEnum(EventReferenceRequestReviewStatus), nullable=True),
sa.Column('rejection_reason', db.IntegerEnum(EventReferenceRequestRejectionReason), nullable=True), sa.Column('rejection_reason', dbtypes.IntegerEnum(EventReferenceRequestRejectionReason), nullable=True),
sa.Column('created_by_id', sa.Integer(), nullable=True), sa.Column('created_by_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['admin_unit_id'], ['adminunit.id'], ), sa.ForeignKeyConstraint(['admin_unit_id'], ['adminunit.id'], ),
sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ), sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ),

View File

@ -8,7 +8,7 @@ Create Date: 2020-09-25 11:26:03.139800
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-07-26 15:20:17.685921
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-07-26 16:08:39.066127
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-07-26 15:48:47.723256
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-10-04 17:06:54.502012
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-07-28 17:10:49.606513
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-07-08 08:53:44.373606
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,8 +8,8 @@ Create Date: 2020-07-07 15:49:58.653888
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
from models import EventTargetGroupOrigin, EventAttendanceMode, EventStatus from project.models import EventTargetGroupOrigin, EventAttendanceMode, EventStatus
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = 'f1bc3fa623c7' revision = 'f1bc3fa623c7'
@ -23,11 +23,11 @@ def upgrade():
op.add_column('event', sa.Column('accessible_for_free', sa.Boolean(), nullable=True)) op.add_column('event', sa.Column('accessible_for_free', sa.Boolean(), nullable=True))
op.add_column('event', sa.Column('age_from', sa.Integer(), nullable=True)) op.add_column('event', sa.Column('age_from', sa.Integer(), nullable=True))
op.add_column('event', sa.Column('age_to', sa.Integer(), nullable=True)) op.add_column('event', sa.Column('age_to', sa.Integer(), nullable=True))
op.add_column('event', sa.Column('attendance_mode', db.IntegerEnum(EventAttendanceMode), nullable=True)) op.add_column('event', sa.Column('attendance_mode', dbtypes.IntegerEnum(EventAttendanceMode), nullable=True))
op.add_column('event', sa.Column('kid_friendly', sa.Boolean(), nullable=True)) op.add_column('event', sa.Column('kid_friendly', sa.Boolean(), nullable=True))
op.add_column('event', sa.Column('status', db.IntegerEnum(EventStatus), nullable=True)) op.add_column('event', sa.Column('status', dbtypes.IntegerEnum(EventStatus), nullable=True))
op.add_column('event', sa.Column('tags', sa.UnicodeText(), nullable=True)) op.add_column('event', sa.Column('tags', sa.UnicodeText(), nullable=True))
op.add_column('event', sa.Column('target_group_origin', db.IntegerEnum(EventTargetGroupOrigin), nullable=True)) op.add_column('event', sa.Column('target_group_origin', dbtypes.IntegerEnum(EventTargetGroupOrigin), nullable=True))
# ### end Alembic commands ### # ### end Alembic commands ###

View File

@ -8,7 +8,7 @@ Create Date: 2020-09-18 15:27:37.608869
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -8,7 +8,7 @@ Create Date: 2020-07-17 19:51:08.457429
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy_utils import sqlalchemy_utils
import db from project import dbtypes
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -4,8 +4,8 @@ from flask import jsonify, Flask, render_template, request, url_for, redirect, a
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
from sqlalchemy.sql import asc, func from sqlalchemy.sql import asc, func
from sqlalchemy import and_, or_, not_ from sqlalchemy import and_, or_, not_, event
from flask_security import Security, current_user, auth_required, roles_required, hash_password, SQLAlchemySessionUserDatastore from flask_security import Security, current_user, auth_required, roles_required, SQLAlchemySessionUserDatastore
from flask_security.utils import FsPermNeed from flask_security.utils import FsPermNeed
from flask_babelex import Babel, gettext, lazy_gettext, format_datetime, to_user_timezone from flask_babelex import Babel, gettext, lazy_gettext, format_datetime, to_user_timezone
from flask_principal import Permission from flask_principal import Permission
@ -29,6 +29,8 @@ app.config['SECURITY_EMAIL_SENDER'] = os.getenv("MAIL_DEFAULT_SENDER")
app.config['LANGUAGES'] = ['en', 'de'] app.config['LANGUAGES'] = ['en', 'de']
app.config['GOOGLE_OAUTH_CLIENT_ID'] = os.getenv('GOOGLE_OAUTH_CLIENT_ID') app.config['GOOGLE_OAUTH_CLIENT_ID'] = os.getenv('GOOGLE_OAUTH_CLIENT_ID')
app.config['GOOGLE_OAUTH_CLIENT_SECRET'] = os.getenv('GOOGLE_OAUTH_CLIENT_SECRET') app.config['GOOGLE_OAUTH_CLIENT_SECRET'] = os.getenv('GOOGLE_OAUTH_CLIENT_SECRET')
app.config['OAUTHLIB_INSECURE_TRANSPORT'] = True
app.config['OAUTHLIB_RELAX_TOKEN_SCOPE'] = True
# Generate a nice key using secrets.token_urlsafe() # Generate a nice key using secrets.token_urlsafe()
app.config['SECRET_KEY'] = os.environ.get("SECRET_KEY", 'pf9Wkove4IKEAXvy-cQkeDPhv9Cb3Ag-wyJILbq_dFw') app.config['SECRET_KEY'] = os.environ.get("SECRET_KEY", 'pf9Wkove4IKEAXvy-cQkeDPhv9Cb3Ag-wyJILbq_dFw')
@ -68,30 +70,31 @@ if app.config['MAIL_SUPPRESS_SEND']:
print(message.body) print(message.body)
email_dispatched.connect(log_message) email_dispatched.connect(log_message)
# create db # Create db
db = SQLAlchemy(app) db = SQLAlchemy(app)
# qr code # qr code
QRcode(app) QRcode(app)
from jsonld import DateTimeEncoder # JSON
from project.jsonld import DateTimeEncoder
app.json_encoder = DateTimeEncoder app.json_encoder = DateTimeEncoder
# Setup Flask-Security # Setup Flask-Security
from models import User, Role from project.models import User, Role
user_datastore = SQLAlchemySessionUserDatastore(db.session, User, Role) user_datastore = SQLAlchemySessionUserDatastore(db.session, User, Role)
security = Security(app, user_datastore) security = Security(app, user_datastore)
# OAuth # OAuth
from oauth import blueprint from project.oauth import blueprint
app.register_blueprint(blueprint, url_prefix="/login") app.register_blueprint(blueprint, url_prefix="/login")
import i10n from project.i10n import *
import jinja_filters from project.jinja_filters import *
import init_data from project.init_data import *
# Routes # Routes
from views import ( from project.views import (
admin, admin,
admin_unit, admin_unit,
admin_unit_member, admin_unit_member,

View File

@ -2,7 +2,7 @@ from flask import abort
from flask_security import current_user from flask_security import current_user
from flask_security.utils import FsPermNeed from flask_security.utils import FsPermNeed
from flask_principal import Permission from flask_principal import Permission
from models import AdminUnitMember, AdminUnit from project.models import AdminUnitMember, AdminUnit
def has_current_user_permission(permission): def has_current_user_permission(permission):
user_perm = Permission(FsPermNeed(permission)) user_perm = Permission(FsPermNeed(permission))

View File

@ -6,8 +6,8 @@ from wtforms.fields.html5 import EmailField, TelField, URLField
from wtforms.validators import DataRequired, Optional, Regexp from wtforms.validators import DataRequired, Optional, Regexp
from wtforms.widgets.html5 import ColorInput from wtforms.widgets.html5 import ColorInput
import decimal import decimal
from models import Location, Image from project.models import Location, Image
from .common import FileImageForm from project.forms.common import FileImageForm
class AdminUnitLocationForm(FlaskForm): class AdminUnitLocationForm(FlaskForm):
street = StringField(lazy_gettext('Street'), validators=[Optional()]) street = StringField(lazy_gettext('Street'), validators=[Optional()])

View File

@ -5,8 +5,8 @@ from wtforms import StringField, SubmitField, DecimalField, TextAreaField, FormF
from wtforms.fields.html5 import EmailField, TelField from wtforms.fields.html5 import EmailField, TelField
from wtforms.validators import DataRequired, Optional, Regexp from wtforms.validators import DataRequired, Optional, Regexp
import decimal import decimal
from models import Location from project.models import Location
from .widgets import MultiCheckboxField from project.forms.widgets import MultiCheckboxField
class InviteAdminUnitMemberForm(FlaskForm): class InviteAdminUnitMemberForm(FlaskForm):
email = EmailField(lazy_gettext('Email'), validators=[DataRequired()]) email = EmailField(lazy_gettext('Email'), validators=[DataRequired()])

View File

@ -6,9 +6,9 @@ from wtforms import SelectMultipleField, FieldList, RadioField, DateTimeField, S
from wtforms.fields.html5 import DateTimeLocalField, EmailField, URLField from wtforms.fields.html5 import DateTimeLocalField, EmailField, URLField
from wtforms.validators import DataRequired, Optional from wtforms.validators import DataRequired, Optional
from wtforms.widgets import html_params, HTMLString from wtforms.widgets import html_params, HTMLString
from models import EventPlace, EventTargetGroupOrigin, EventAttendanceMode, EventStatus, Location, EventOrganizer, EventRejectionReason, EventReviewStatus, Image from project.models import EventPlace, EventTargetGroupOrigin, EventAttendanceMode, EventStatus, Location, EventOrganizer, EventRejectionReason, EventReviewStatus, Image
from .common import event_rating_choices, Base64ImageForm from project.forms.common import event_rating_choices, Base64ImageForm
from .widgets import CustomDateTimeField, CustomDateField from project.forms.widgets import CustomDateTimeField, CustomDateField
class EventPlaceLocationForm(FlaskForm): class EventPlaceLocationForm(FlaskForm):
street = StringField(lazy_gettext('Street'), validators=[Optional()]) street = StringField(lazy_gettext('Street'), validators=[Optional()])

View File

@ -6,9 +6,9 @@ from wtforms import HiddenField, SelectMultipleField, FieldList, RadioField, Dat
from wtforms.fields.html5 import DateTimeLocalField, EmailField from wtforms.fields.html5 import DateTimeLocalField, EmailField
from wtforms.validators import DataRequired, Optional from wtforms.validators import DataRequired, Optional
from wtforms.widgets import html_params, HTMLString from wtforms.widgets import html_params, HTMLString
from models import EventPlace, EventTargetGroupOrigin, EventAttendanceMode, EventStatus, Location, EventOrganizer, EventRejectionReason, EventReviewStatus from project.models import EventPlace, EventTargetGroupOrigin, EventAttendanceMode, EventStatus, Location, EventOrganizer, EventRejectionReason, EventReviewStatus
from .common import event_rating_choices, weekday_choices, distance_choices from project.forms.common import event_rating_choices, weekday_choices, distance_choices
from .widgets import CustomDateField, MultiCheckboxField from project.forms.widgets import CustomDateField, MultiCheckboxField
class FindEventDateForm(FlaskForm): class FindEventDateForm(FlaskForm):
class Meta: class Meta:

View File

@ -6,8 +6,8 @@ from wtforms.fields.html5 import DateTimeLocalField, EmailField, URLField
from wtforms.validators import DataRequired, Optional from wtforms.validators import DataRequired, Optional
from wtforms.widgets import html_params, HTMLString from wtforms.widgets import html_params, HTMLString
import decimal import decimal
from models import Location, Image from project.models import Location, Image
from .common import FileImageForm from project.forms.common import FileImageForm
class EventPlaceLocationForm(FlaskForm): class EventPlaceLocationForm(FlaskForm):
street = StringField(lazy_gettext('Street'), validators=[Optional()]) street = StringField(lazy_gettext('Street'), validators=[Optional()])

View File

@ -6,10 +6,10 @@ from wtforms import FieldList, RadioField, DateTimeField, StringField, SubmitFie
from wtforms.fields.html5 import DateTimeLocalField, EmailField, TelField, URLField from wtforms.fields.html5 import DateTimeLocalField, EmailField, TelField, URLField
from wtforms.validators import DataRequired, Optional from wtforms.validators import DataRequired, Optional
from wtforms.widgets import html_params, HTMLString from wtforms.widgets import html_params, HTMLString
from models import EventSuggestion, EventPlace, EventTargetGroupOrigin, EventAttendanceMode, EventStatus, Location, EventOrganizer, EventRejectionReason, EventReviewStatus, Image from project.models import EventSuggestion, EventPlace, EventTargetGroupOrigin, EventAttendanceMode, EventStatus, Location, EventOrganizer, EventRejectionReason, EventReviewStatus, Image
from .common import event_rating_choices, Base64ImageForm from project.forms.common import event_rating_choices, Base64ImageForm
from .widgets import CustomDateTimeField, CustomDateField, TagSelectField from project.forms.widgets import CustomDateTimeField, CustomDateField, TagSelectField
from .common import event_rating_choices from project.forms.common import event_rating_choices
class CreateEventSuggestionForm(FlaskForm): class CreateEventSuggestionForm(FlaskForm):
name = StringField(lazy_gettext('Name'), validators=[DataRequired()], description=lazy_gettext('Enter a short, meaningful name for the event.')) name = StringField(lazy_gettext('Name'), validators=[DataRequired()], description=lazy_gettext('Enter a short, meaningful name for the event.'))

View File

@ -5,8 +5,8 @@ from wtforms import StringField, SubmitField, DecimalField, TextAreaField, FormF
from wtforms.fields.html5 import EmailField, TelField, URLField from wtforms.fields.html5 import EmailField, TelField, URLField
from wtforms.validators import DataRequired, Optional, Regexp from wtforms.validators import DataRequired, Optional, Regexp
import decimal import decimal
from models import Location, Image from project.models import Location, Image
from .common import FileImageForm from project.forms.common import FileImageForm
class OrganizerLocationForm(FlaskForm): class OrganizerLocationForm(FlaskForm):
street = StringField(lazy_gettext('Street'), validators=[Optional()]) street = StringField(lazy_gettext('Street'), validators=[Optional()])

View File

@ -6,9 +6,9 @@ from wtforms import HiddenField, SelectMultipleField, FieldList, RadioField, Dat
from wtforms.fields.html5 import DateTimeLocalField, EmailField from wtforms.fields.html5 import DateTimeLocalField, EmailField
from wtforms.validators import DataRequired, Optional from wtforms.validators import DataRequired, Optional
from wtforms.widgets import html_params, HTMLString from wtforms.widgets import html_params, HTMLString
from models import EventPlace, EventTargetGroupOrigin, EventAttendanceMode, EventStatus, Location, EventOrganizer, EventRejectionReason, EventReviewStatus from project.models import EventPlace, EventTargetGroupOrigin, EventAttendanceMode, EventStatus, Location, EventOrganizer, EventRejectionReason, EventReviewStatus
from .common import event_rating_choices, weekday_choices, distance_choices from project.forms.common import event_rating_choices, weekday_choices, distance_choices
from .widgets import CustomDateField, MultiCheckboxField from project.forms.widgets import CustomDateField, MultiCheckboxField
class PlaningForm(FlaskForm): class PlaningForm(FlaskForm):
class Meta: class Meta:

View File

@ -2,7 +2,7 @@ from flask_babelex import lazy_gettext, gettext
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import SelectField, StringField, SubmitField from wtforms import SelectField, StringField, SubmitField
from wtforms.validators import DataRequired from wtforms.validators import DataRequired
from .common import event_rating_choices from project.forms.common import event_rating_choices
class CreateEventReferenceForm(FlaskForm): class CreateEventReferenceForm(FlaskForm):
admin_unit_id = SelectField(lazy_gettext('Admin unit'), validators=[DataRequired()], coerce=int) admin_unit_id = SelectField(lazy_gettext('Admin unit'), validators=[DataRequired()], coerce=int)

View File

@ -2,8 +2,8 @@ from flask_babelex import lazy_gettext, gettext
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import SelectField, StringField, SubmitField from wtforms import SelectField, StringField, SubmitField
from wtforms.validators import DataRequired from wtforms.validators import DataRequired
from .common import event_rating_choices from project.forms.common import event_rating_choices
from models import EventReferenceRequestRejectionReason, EventReferenceRequestReviewStatus from project.models import EventReferenceRequestRejectionReason, EventReferenceRequestReviewStatus
class CreateEventReferenceRequestForm(FlaskForm): class CreateEventReferenceRequestForm(FlaskForm):
admin_unit_id = SelectField(lazy_gettext('Admin unit'), validators=[DataRequired()], coerce=int) admin_unit_id = SelectField(lazy_gettext('Admin unit'), validators=[DataRequired()], coerce=int)

View File

@ -4,7 +4,7 @@ from wtforms.validators import StopValidation
import pytz import pytz
from datetime import datetime from datetime import datetime
from flask_babelex import to_user_timezone, gettext from flask_babelex import to_user_timezone, gettext
from dateutils import berlin_tz from project.dateutils import berlin_tz
class MultiCheckboxField(SelectMultipleField): class MultiCheckboxField(SelectMultipleField):
widget = ListWidget(prefix_label=False) widget = ListWidget(prefix_label=False)

BIN
project/gsevpt.sqlite Normal file

Binary file not shown.

BIN
project/gsevpt.sqlite3 Normal file

Binary file not shown.

View File

@ -1,4 +1,4 @@
from app import app, babel from project import app, babel
from flask import request from flask import request
@babel.localeselector @babel.localeselector

View File

@ -1,7 +1,7 @@
from app import app, db from project import app, db
from services.user import upsert_user_role, add_roles_to_user from project.services.user import upsert_user_role, add_roles_to_user
from services.admin_unit import upsert_admin_unit_member_role from project.services.admin_unit import upsert_admin_unit_member_role
from models import Location from project.models import Location
@app.before_first_request @app.before_first_request
def create_initial_data(): def create_initial_data():

View File

@ -1,5 +1,5 @@
from app import app from project import app
from utils import get_event_category_name, get_localized_enum_name from project.utils import get_event_category_name, get_localized_enum_name
from urllib.parse import quote_plus from urllib.parse import quote_plus
import os import os
@ -15,9 +15,9 @@ app.jinja_env.filters['quote_plus'] = lambda u: quote_plus(u)
def get_manage_menu_options_context_processor(): def get_manage_menu_options_context_processor():
def get_manage_menu_options(admin_unit): def get_manage_menu_options(admin_unit):
from access import has_access from project.access import has_access
from services.event_suggestion import get_event_reviews_badge_query from project.services.event_suggestion import get_event_reviews_badge_query
from services.reference import get_reference_requests_incoming_badge_query from project.services.reference import get_reference_requests_incoming_badge_query
reviews_badge = 0 reviews_badge = 0
reference_requests_incoming_badge = get_reference_requests_incoming_badge_query(admin_unit).count() reference_requests_incoming_badge = get_reference_requests_incoming_badge_query(admin_unit).count()

View File

@ -2,7 +2,7 @@ import datetime
import decimal import decimal
from json import JSONEncoder from json import JSONEncoder
from flask import url_for from flask import url_for
from models import EventAttendanceMode, EventStatus from project.models import EventAttendanceMode, EventStatus
import pytz import pytz
berlin_tz = pytz.timezone('Europe/Berlin') berlin_tz = pytz.timezone('Europe/Berlin')

View File

@ -1,4 +1,4 @@
from app import db from project import db
from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship, backref, deferred from sqlalchemy.orm import relationship, backref, deferred
@ -11,7 +11,7 @@ from flask_security import UserMixin, RoleMixin
from flask_dance.consumer.storage.sqla import OAuthConsumerMixin from flask_dance.consumer.storage.sqla import OAuthConsumerMixin
from enum import IntEnum from enum import IntEnum
import datetime import datetime
from db import IntegerEnum from project.dbtypes import IntegerEnum
from geoalchemy2 import Geometry from geoalchemy2 import Geometry
from sqlalchemy import and_ from sqlalchemy import and_

View File

@ -4,8 +4,8 @@ from flask_dance.contrib.google import make_google_blueprint
from flask_dance.consumer import oauth_authorized, oauth_error from flask_dance.consumer import oauth_authorized, oauth_error
from flask_dance.consumer.storage.sqla import SQLAlchemyStorage from flask_dance.consumer.storage.sqla import SQLAlchemyStorage
from sqlalchemy.orm.exc import NoResultFound from sqlalchemy.orm.exc import NoResultFound
from models import User, OAuth from project.models import User, OAuth
from app import db, user_datastore from project import db, user_datastore
from flask_babelex import gettext from flask_babelex import gettext
blueprint = make_google_blueprint( blueprint = make_google_blueprint(

View File

@ -1,4 +1,4 @@
from app import app, db, get_admin_unit, update_event_dates_with_recurrence_rule, upsert_event_category from project import app, db, get_admin_unit, update_event_dates_with_recurrence_rule, upsert_event_category
from pprint import pprint from pprint import pprint
import datetime import datetime
from dateutil import parser, tz from dateutil import parser, tz
@ -11,7 +11,7 @@ import json
import re import re
import unicodedata import unicodedata
import decimal import decimal
from models import EventReviewStatus, EventTargetGroupOrigin, Location, Event, EventStatus, EventCategory, EventPlace, EventOrganizer, AdminUnit from project.models import EventReviewStatus, EventTargetGroupOrigin, Location, Event, EventStatus, EventCategory, EventPlace, EventOrganizer, AdminUnit
from sqlalchemy import and_, or_, not_ from sqlalchemy import and_, or_, not_
berlin_tz = pytz.timezone('Europe/Berlin') berlin_tz = pytz.timezone('Europe/Berlin')

View File

@ -1,4 +1,4 @@
from app import app, db from project import app, db
from pprint import pprint from pprint import pprint
import datetime import datetime
from dateutil import parser, tz from dateutil import parser, tz
@ -13,11 +13,11 @@ from flask import jsonify
import re import re
import unicodedata import unicodedata
import decimal import decimal
from models import EventReviewStatus, EventTargetGroupOrigin, Location, Event, EventStatus, EventCategory, EventPlace, EventOrganizer, AdminUnit from project.models import EventReviewStatus, EventTargetGroupOrigin, Location, Event, EventStatus, EventCategory, EventPlace, EventOrganizer, AdminUnit
from sqlalchemy import and_, or_, not_ from sqlalchemy import and_, or_, not_
from dateutils import berlin_tz from project.dateutils import berlin_tz
from services.admin_unit import get_admin_unit from project.services.admin_unit import get_admin_unit
from services.event import upsert_event_category, update_event_dates_with_recurrence_rule from project.services.event import upsert_event_category, update_event_dates_with_recurrence_rule
admin_unit = get_admin_unit('Harzinfo') admin_unit = get_admin_unit('Harzinfo')
category = upsert_event_category('Other') category = upsert_event_category('Other')

View File

@ -1,5 +1,5 @@
from app import db from project import db
from models import AdminUnit, AdminUnitMember, AdminUnitMemberRole from project.models import AdminUnit, AdminUnitMember, AdminUnitMemberRole
def upsert_admin_unit(unit_name, short_name = None): def upsert_admin_unit(unit_name, short_name = None):
admin_unit = AdminUnit.query.filter_by(name = unit_name).first() admin_unit = AdminUnit.query.filter_by(name = unit_name).first()

View File

@ -1,5 +1,5 @@
from models import EventReviewStatus, EventCategory, Event, EventDate, EventReference, EventPlace, Location, EventSuggestion from project.models import EventReviewStatus, EventCategory, Event, EventDate, EventReference, EventPlace, Location, EventSuggestion
from dateutils import dates_from_recurrence_rule, today, date_add_time, date_set_end_of_day from project.dateutils import dates_from_recurrence_rule, today, date_add_time, date_set_end_of_day
from sqlalchemy import and_, or_, not_, func from sqlalchemy import and_, or_, not_, func
from sqlalchemy.sql import extract from sqlalchemy.sql import extract
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta

View File

@ -1,4 +1,4 @@
from dateutils import today, date_add_time, date_set_end_of_day, form_input_from_date, form_input_to_date from project.dateutils import today, date_add_time, date_set_end_of_day, form_input_from_date, form_input_to_date
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from flask import request from flask import request

View File

@ -1,4 +1,4 @@
from models import EventReviewStatus, EventSuggestion from project.models import EventReviewStatus, EventSuggestion
from sqlalchemy import and_ from sqlalchemy import and_
def get_event_reviews_badge_query(admin_unit): def get_event_reviews_badge_query(admin_unit):

View File

@ -1,4 +1,4 @@
from models import Location from project.models import Location
def upsert_location(street, postalCode, city, latitude = 0, longitude = 0, state = None): def upsert_location(street, postalCode, city, latitude = 0, longitude = 0, state = None):
result = Location.query.filter_by(street = street, postalCode=postalCode, city=city, state=state).first() result = Location.query.filter_by(street = street, postalCode=postalCode, city=city, state=state).first()

View File

@ -1,4 +1,4 @@
from models import EventOrganizer, EventPlace from project.models import EventOrganizer, EventPlace
from sqlalchemy import and_, or_, not_ from sqlalchemy import and_, or_, not_
from sqlalchemy.sql import asc, func from sqlalchemy.sql import asc, func

View File

@ -1,4 +1,4 @@
from models import EventPlace from project.models import EventPlace
from sqlalchemy.sql import asc, func from sqlalchemy.sql import asc, func
def upsert_event_place(admin_unit_id, organizer_id, name): def upsert_event_place(admin_unit_id, organizer_id, name):

View File

@ -1,5 +1,5 @@
from app import db from project import db
from models import EventReference, EventReferenceRequest, EventReferenceRequestReviewStatus from project.models import EventReference, EventReferenceRequest, EventReferenceRequestReviewStatus
from sqlalchemy import and_, or_, not_ from sqlalchemy import and_, or_, not_
def create_event_reference_for_request(request): def create_event_reference_for_request(request):

View File

@ -1,4 +1,5 @@
from app import user_datastore from project import user_datastore
from flask_security import Security, current_user, auth_required, roles_required, hash_password
def upsert_user(email, password="password"): def upsert_user(email, password="password"):
result = user_datastore.find_user(email=email) result = user_datastore.find_user(email=email)

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 939 KiB

After

Width:  |  Height:  |  Size: 939 KiB

View File

Before

Width:  |  Height:  |  Size: 298 KiB

After

Width:  |  Height:  |  Size: 298 KiB

View File

Before

Width:  |  Height:  |  Size: 193 KiB

After

Width:  |  Height:  |  Size: 193 KiB

View File

Before

Width:  |  Height:  |  Size: 308 KiB

After

Width:  |  Height:  |  Size: 308 KiB

View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

Before

Width:  |  Height:  |  Size: 237 KiB

After

Width:  |  Height:  |  Size: 237 KiB

View File

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Some files were not shown because too many files have changed in this diff Show More