Internal/modules (#1)
* Restructured app * Added Travis CI * Added 'Deploy to heroku' button
18
.travis.yml
Normal 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
@ -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"
|
||||||
},
|
},
|
||||||
|
|||||||
2
Procfile
@ -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
@ -1,74 +1,70 @@
|
|||||||
|
[](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
|
[](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
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
@ -0,0 +1 @@
|
|||||||
|
from project import app
|
||||||
52
doc/development.md
Normal 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
|
||||||
|
```
|
||||||
@ -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)
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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),
|
||||||
|
|||||||
@ -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))
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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 ###
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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'], ),
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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'], ),
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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 ###
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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,
|
||||||
@ -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))
|
||||||
@ -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()])
|
||||||
@ -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()])
|
||||||
@ -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()])
|
||||||
@ -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:
|
||||||
@ -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()])
|
||||||
@ -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.'))
|
||||||
@ -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()])
|
||||||
@ -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:
|
||||||
@ -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)
|
||||||
@ -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)
|
||||||
@ -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
BIN
project/gsevpt.sqlite3
Normal 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
|
||||||
@ -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():
|
||||||
@ -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()
|
||||||
@ -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')
|
||||||
@ -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_
|
||||||
|
|
||||||
@ -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(
|
||||||
@ -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')
|
||||||
@ -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')
|
||||||
@ -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()
|
||||||
@ -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
|
||||||
@ -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
|
||||||
|
|
||||||
@ -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):
|
||||||
@ -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()
|
||||||
@ -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
|
||||||
|
|
||||||
@ -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):
|
||||||
@ -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):
|
||||||
@ -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)
|
||||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 939 KiB After Width: | Height: | Size: 939 KiB |
|
Before Width: | Height: | Size: 298 KiB After Width: | Height: | Size: 298 KiB |
|
Before Width: | Height: | Size: 193 KiB After Width: | Height: | Size: 193 KiB |
|
Before Width: | Height: | Size: 308 KiB After Width: | Height: | Size: 308 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 237 KiB After Width: | Height: | Size: 237 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 144 KiB |