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",
|
||||
"module": "flask",
|
||||
"env": {
|
||||
"FLASK_APP": "app.py",
|
||||
"FLASK_APP": "project",
|
||||
"FLASK_ENV": "development",
|
||||
"FLASK_DEBUG": "1"
|
||||
},
|
||||
|
||||
2
Procfile
@ -1,2 +1,2 @@
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
```
|
||||
DATABASE_URL=
|
||||
GOOGLE_OAUTH_CLIENT_ID=
|
||||
GOOGLE_OAUTH_CLIENT_SECRET=
|
||||
OAUTHLIB_INSECURE_TRANSPORT=true
|
||||
OAUTHLIB_RELAX_TOKEN_SCOPE=true
|
||||
GOOGLE_MAPS_API_KEY=
|
||||
### Requirements
|
||||
|
||||
- Python 3.7
|
||||
- pip
|
||||
- Postgres with postgis
|
||||
|
||||
### Create database
|
||||
|
||||
```sh
|
||||
psql -c 'create database gsevpt;' -U postgres
|
||||
```
|
||||
|
||||
### Install and run
|
||||
|
||||
```
|
||||
```sh
|
||||
export DATABASE_URL="postgresql://postgres@localhost/gsevpt"
|
||||
pip install -r requirements.txt
|
||||
python manage.py db upgrade
|
||||
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
|
||||
|
||||
### Database
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
[Development](doc/development.md)
|
||||
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/**]
|
||||
[python: **.py]
|
||||
[jinja2: templates/**.html]
|
||||
[python: app/**.py]
|
||||
[jinja2: app/templates/**.html]
|
||||
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_migrate import Migrate, MigrateCommand
|
||||
from app import app, db
|
||||
from project import app, db
|
||||
|
||||
migrate = Migrate(app, db)
|
||||
manager = Manager(app)
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: ${create_date}
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
${imports if imports else ""}
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-10-27 20:58:57.392619
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,8 +8,8 @@ Create Date: 2020-10-22 17:59:27.823624
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from models import EventRejectionReason, EventReviewStatus
|
||||
from project import dbtypes
|
||||
from project.models import EventRejectionReason, EventReviewStatus
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '021f602d9965'
|
||||
@ -27,8 +27,8 @@ def upgrade():
|
||||
sa.Column('start', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('description', sa.UnicodeText(), nullable=True),
|
||||
sa.Column('external_link', sa.String(length=255), nullable=True),
|
||||
sa.Column('review_status', db.IntegerEnum(EventReviewStatus), nullable=True),
|
||||
sa.Column('rejection_resaon', db.IntegerEnum(EventRejectionReason), nullable=True),
|
||||
sa.Column('review_status', dbtypes.IntegerEnum(EventReviewStatus), nullable=True),
|
||||
sa.Column('rejection_resaon', dbtypes.IntegerEnum(EventRejectionReason), nullable=True),
|
||||
sa.Column('contact_name', sa.Unicode(length=255), nullable=False),
|
||||
sa.Column('contact_email', 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
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.sql import text
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
from geoalchemy2.types import Geometry
|
||||
|
||||
|
||||
@ -20,6 +21,10 @@ depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
|
||||
bind = op.get_bind()
|
||||
bind.execute(text("create extension if not exists postgis;"))
|
||||
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
#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))
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-10-18 11:55:12.315808
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-11-08 16:14:01.866196
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -9,7 +9,7 @@ from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-07-17 19:54:25.703175
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-09-18 15:04:03.359403
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-10-27 20:31:42.566357
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-09-29 15:38:44.033998
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-07-13 19:01:04.770613
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-07-30 13:13:44.694716
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,8 +8,8 @@ Create Date: 2020-08-01 15:43:11.377833
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from models import EventRejectionReason, EventReviewStatus
|
||||
from project import dbtypes
|
||||
from project.models import EventRejectionReason, EventReviewStatus
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
@ -21,8 +21,8 @@ depends_on = None
|
||||
|
||||
def upgrade():
|
||||
# ### 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('review_status', db.IntegerEnum(EventReviewStatus), nullable=True))
|
||||
op.add_column('event', sa.Column('rejection_resaon', dbtypes.IntegerEnum(EventRejectionReason), nullable=True))
|
||||
op.add_column('event', sa.Column('review_status', dbtypes.IntegerEnum(EventReviewStatus), nullable=True))
|
||||
op.drop_column('event', 'verified')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-07-28 16:29:41.403957
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-10-01 11:09:16.765736
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-10-23 15:51:36.330825
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,8 +8,8 @@ Create Date: 2020-09-28 10:38:46.424791
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from models import FeaturedEventRejectionReason, FeaturedEventReviewStatus
|
||||
from project import dbtypes
|
||||
from project.models import FeaturedEventRejectionReason, FeaturedEventReviewStatus
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
@ -25,8 +25,8 @@ def upgrade():
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('event_id', sa.Integer(), nullable=False),
|
||||
sa.Column('review_status', db.IntegerEnum(FeaturedEventReviewStatus), nullable=True),
|
||||
sa.Column('rejection_resaon', db.IntegerEnum(FeaturedEventRejectionReason), nullable=True),
|
||||
sa.Column('review_status', dbtypes.IntegerEnum(FeaturedEventReviewStatus), nullable=True),
|
||||
sa.Column('rejection_resaon', dbtypes.IntegerEnum(FeaturedEventRejectionReason), nullable=True),
|
||||
sa.Column('rating', sa.Integer(), nullable=True),
|
||||
sa.Column('created_by_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ),
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-09-24 18:53:02.861732
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,8 +8,8 @@ Create Date: 2020-10-18 13:06:47.639083
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from models import EventRejectionReason, EventReviewStatus
|
||||
from project import dbtypes
|
||||
from project.models import EventRejectionReason, EventReviewStatus
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-07-17 11:27:53.084732
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-07-31 16:30:19.185088
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,9 +8,9 @@ Create Date: 2020-09-29 16:53:02.520125
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
from sqlalchemy.dialects import postgresql
|
||||
from models import EventReferenceRequestRejectionReason, EventReferenceRequestReviewStatus
|
||||
from project.models import EventReferenceRequestRejectionReason, EventReferenceRequestReviewStatus
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'a75bd9c8ad3a'
|
||||
@ -38,8 +38,8 @@ def upgrade():
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('event_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('rejection_reason', db.IntegerEnum(EventReferenceRequestRejectionReason), nullable=True),
|
||||
sa.Column('review_status', dbtypes.IntegerEnum(EventReferenceRequestReviewStatus), nullable=True),
|
||||
sa.Column('rejection_reason', dbtypes.IntegerEnum(EventReferenceRequestRejectionReason), nullable=True),
|
||||
sa.Column('created_by_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['admin_unit_id'], ['adminunit.id'], ),
|
||||
sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ),
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-09-25 11:26:03.139800
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-07-26 15:20:17.685921
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-07-26 16:08:39.066127
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-07-26 15:48:47.723256
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-10-04 17:06:54.502012
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-07-28 17:10:49.606513
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-07-08 08:53:44.373606
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,8 +8,8 @@ Create Date: 2020-07-07 15:49:58.653888
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from models import EventTargetGroupOrigin, EventAttendanceMode, EventStatus
|
||||
from project import dbtypes
|
||||
from project.models import EventTargetGroupOrigin, EventAttendanceMode, EventStatus
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
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('age_from', 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('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('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 ###
|
||||
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-09-18 15:27:37.608869
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@ -8,7 +8,7 @@ Create Date: 2020-07-17 19:51:08.457429
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import db
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# 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 sqlalchemy.orm import joinedload
|
||||
from sqlalchemy.sql import asc, func
|
||||
from sqlalchemy import and_, or_, not_
|
||||
from flask_security import Security, current_user, auth_required, roles_required, hash_password, SQLAlchemySessionUserDatastore
|
||||
from sqlalchemy import and_, or_, not_, event
|
||||
from flask_security import Security, current_user, auth_required, roles_required, SQLAlchemySessionUserDatastore
|
||||
from flask_security.utils import FsPermNeed
|
||||
from flask_babelex import Babel, gettext, lazy_gettext, format_datetime, to_user_timezone
|
||||
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['GOOGLE_OAUTH_CLIENT_ID'] = os.getenv('GOOGLE_OAUTH_CLIENT_ID')
|
||||
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()
|
||||
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)
|
||||
email_dispatched.connect(log_message)
|
||||
|
||||
# create db
|
||||
# Create db
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
# qr code
|
||||
QRcode(app)
|
||||
|
||||
from jsonld import DateTimeEncoder
|
||||
# JSON
|
||||
from project.jsonld import DateTimeEncoder
|
||||
app.json_encoder = DateTimeEncoder
|
||||
|
||||
# Setup Flask-Security
|
||||
from models import User, Role
|
||||
from project.models import User, Role
|
||||
user_datastore = SQLAlchemySessionUserDatastore(db.session, User, Role)
|
||||
security = Security(app, user_datastore)
|
||||
|
||||
# OAuth
|
||||
from oauth import blueprint
|
||||
from project.oauth import blueprint
|
||||
app.register_blueprint(blueprint, url_prefix="/login")
|
||||
|
||||
import i10n
|
||||
import jinja_filters
|
||||
import init_data
|
||||
from project.i10n import *
|
||||
from project.jinja_filters import *
|
||||
from project.init_data import *
|
||||
|
||||
# Routes
|
||||
from views import (
|
||||
from project.views import (
|
||||
admin,
|
||||
admin_unit,
|
||||
admin_unit_member,
|
||||
@ -2,7 +2,7 @@ from flask import abort
|
||||
from flask_security import current_user
|
||||
from flask_security.utils import FsPermNeed
|
||||
from flask_principal import Permission
|
||||
from models import AdminUnitMember, AdminUnit
|
||||
from project.models import AdminUnitMember, AdminUnit
|
||||
|
||||
def has_current_user_permission(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.widgets.html5 import ColorInput
|
||||
import decimal
|
||||
from models import Location, Image
|
||||
from .common import FileImageForm
|
||||
from project.models import Location, Image
|
||||
from project.forms.common import FileImageForm
|
||||
|
||||
class AdminUnitLocationForm(FlaskForm):
|
||||
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.validators import DataRequired, Optional, Regexp
|
||||
import decimal
|
||||
from models import Location
|
||||
from .widgets import MultiCheckboxField
|
||||
from project.models import Location
|
||||
from project.forms.widgets import MultiCheckboxField
|
||||
|
||||
class InviteAdminUnitMemberForm(FlaskForm):
|
||||
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.validators import DataRequired, Optional
|
||||
from wtforms.widgets import html_params, HTMLString
|
||||
from models import EventPlace, EventTargetGroupOrigin, EventAttendanceMode, EventStatus, Location, EventOrganizer, EventRejectionReason, EventReviewStatus, Image
|
||||
from .common import event_rating_choices, Base64ImageForm
|
||||
from .widgets import CustomDateTimeField, CustomDateField
|
||||
from project.models import EventPlace, EventTargetGroupOrigin, EventAttendanceMode, EventStatus, Location, EventOrganizer, EventRejectionReason, EventReviewStatus, Image
|
||||
from project.forms.common import event_rating_choices, Base64ImageForm
|
||||
from project.forms.widgets import CustomDateTimeField, CustomDateField
|
||||
|
||||
class EventPlaceLocationForm(FlaskForm):
|
||||
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.validators import DataRequired, Optional
|
||||
from wtforms.widgets import html_params, HTMLString
|
||||
from models import EventPlace, EventTargetGroupOrigin, EventAttendanceMode, EventStatus, Location, EventOrganizer, EventRejectionReason, EventReviewStatus
|
||||
from .common import event_rating_choices, weekday_choices, distance_choices
|
||||
from .widgets import CustomDateField, MultiCheckboxField
|
||||
from project.models import EventPlace, EventTargetGroupOrigin, EventAttendanceMode, EventStatus, Location, EventOrganizer, EventRejectionReason, EventReviewStatus
|
||||
from project.forms.common import event_rating_choices, weekday_choices, distance_choices
|
||||
from project.forms.widgets import CustomDateField, MultiCheckboxField
|
||||
|
||||
class FindEventDateForm(FlaskForm):
|
||||
class Meta:
|
||||
@ -6,8 +6,8 @@ from wtforms.fields.html5 import DateTimeLocalField, EmailField, URLField
|
||||
from wtforms.validators import DataRequired, Optional
|
||||
from wtforms.widgets import html_params, HTMLString
|
||||
import decimal
|
||||
from models import Location, Image
|
||||
from .common import FileImageForm
|
||||
from project.models import Location, Image
|
||||
from project.forms.common import FileImageForm
|
||||
|
||||
class EventPlaceLocationForm(FlaskForm):
|
||||
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.validators import DataRequired, Optional
|
||||
from wtforms.widgets import html_params, HTMLString
|
||||
from models import EventSuggestion, EventPlace, EventTargetGroupOrigin, EventAttendanceMode, EventStatus, Location, EventOrganizer, EventRejectionReason, EventReviewStatus, Image
|
||||
from .common import event_rating_choices, Base64ImageForm
|
||||
from .widgets import CustomDateTimeField, CustomDateField, TagSelectField
|
||||
from .common import event_rating_choices
|
||||
from project.models import EventSuggestion, EventPlace, EventTargetGroupOrigin, EventAttendanceMode, EventStatus, Location, EventOrganizer, EventRejectionReason, EventReviewStatus, Image
|
||||
from project.forms.common import event_rating_choices, Base64ImageForm
|
||||
from project.forms.widgets import CustomDateTimeField, CustomDateField, TagSelectField
|
||||
from project.forms.common import event_rating_choices
|
||||
|
||||
class CreateEventSuggestionForm(FlaskForm):
|
||||
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.validators import DataRequired, Optional, Regexp
|
||||
import decimal
|
||||
from models import Location, Image
|
||||
from .common import FileImageForm
|
||||
from project.models import Location, Image
|
||||
from project.forms.common import FileImageForm
|
||||
|
||||
class OrganizerLocationForm(FlaskForm):
|
||||
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.validators import DataRequired, Optional
|
||||
from wtforms.widgets import html_params, HTMLString
|
||||
from models import EventPlace, EventTargetGroupOrigin, EventAttendanceMode, EventStatus, Location, EventOrganizer, EventRejectionReason, EventReviewStatus
|
||||
from .common import event_rating_choices, weekday_choices, distance_choices
|
||||
from .widgets import CustomDateField, MultiCheckboxField
|
||||
from project.models import EventPlace, EventTargetGroupOrigin, EventAttendanceMode, EventStatus, Location, EventOrganizer, EventRejectionReason, EventReviewStatus
|
||||
from project.forms.common import event_rating_choices, weekday_choices, distance_choices
|
||||
from project.forms.widgets import CustomDateField, MultiCheckboxField
|
||||
|
||||
class PlaningForm(FlaskForm):
|
||||
class Meta:
|
||||
@ -2,7 +2,7 @@ from flask_babelex import lazy_gettext, gettext
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import SelectField, StringField, SubmitField
|
||||
from wtforms.validators import DataRequired
|
||||
from .common import event_rating_choices
|
||||
from project.forms.common import event_rating_choices
|
||||
|
||||
class CreateEventReferenceForm(FlaskForm):
|
||||
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 wtforms import SelectField, StringField, SubmitField
|
||||
from wtforms.validators import DataRequired
|
||||
from .common import event_rating_choices
|
||||
from models import EventReferenceRequestRejectionReason, EventReferenceRequestReviewStatus
|
||||
from project.forms.common import event_rating_choices
|
||||
from project.models import EventReferenceRequestRejectionReason, EventReferenceRequestReviewStatus
|
||||
|
||||
class CreateEventReferenceRequestForm(FlaskForm):
|
||||
admin_unit_id = SelectField(lazy_gettext('Admin unit'), validators=[DataRequired()], coerce=int)
|
||||
@ -4,7 +4,7 @@ from wtforms.validators import StopValidation
|
||||
import pytz
|
||||
from datetime import datetime
|
||||
from flask_babelex import to_user_timezone, gettext
|
||||
from dateutils import berlin_tz
|
||||
from project.dateutils import berlin_tz
|
||||
|
||||
class MultiCheckboxField(SelectMultipleField):
|
||||
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
|
||||
|
||||
@babel.localeselector
|
||||
@ -1,7 +1,7 @@
|
||||
from app import app, db
|
||||
from services.user import upsert_user_role, add_roles_to_user
|
||||
from services.admin_unit import upsert_admin_unit_member_role
|
||||
from models import Location
|
||||
from project import app, db
|
||||
from project.services.user import upsert_user_role, add_roles_to_user
|
||||
from project.services.admin_unit import upsert_admin_unit_member_role
|
||||
from project.models import Location
|
||||
|
||||
@app.before_first_request
|
||||
def create_initial_data():
|
||||
@ -1,5 +1,5 @@
|
||||
from app import app
|
||||
from utils import get_event_category_name, get_localized_enum_name
|
||||
from project import app
|
||||
from project.utils import get_event_category_name, get_localized_enum_name
|
||||
from urllib.parse import quote_plus
|
||||
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(admin_unit):
|
||||
from access import has_access
|
||||
from services.event_suggestion import get_event_reviews_badge_query
|
||||
from services.reference import get_reference_requests_incoming_badge_query
|
||||
from project.access import has_access
|
||||
from project.services.event_suggestion import get_event_reviews_badge_query
|
||||
from project.services.reference import get_reference_requests_incoming_badge_query
|
||||
|
||||
reviews_badge = 0
|
||||
reference_requests_incoming_badge = get_reference_requests_incoming_badge_query(admin_unit).count()
|
||||
@ -2,7 +2,7 @@ import datetime
|
||||
import decimal
|
||||
from json import JSONEncoder
|
||||
from flask import url_for
|
||||
from models import EventAttendanceMode, EventStatus
|
||||
from project.models import EventAttendanceMode, EventStatus
|
||||
import pytz
|
||||
|
||||
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.hybrid import hybrid_property
|
||||
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 enum import IntEnum
|
||||
import datetime
|
||||
from db import IntegerEnum
|
||||
from project.dbtypes import IntegerEnum
|
||||
from geoalchemy2 import Geometry
|
||||
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.storage.sqla import SQLAlchemyStorage
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
from models import User, OAuth
|
||||
from app import db, user_datastore
|
||||
from project.models import User, OAuth
|
||||
from project import db, user_datastore
|
||||
from flask_babelex import gettext
|
||||
|
||||
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
|
||||
import datetime
|
||||
from dateutil import parser, tz
|
||||
@ -11,7 +11,7 @@ import json
|
||||
import re
|
||||
import unicodedata
|
||||
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_
|
||||
|
||||
berlin_tz = pytz.timezone('Europe/Berlin')
|
||||
@ -1,4 +1,4 @@
|
||||
from app import app, db
|
||||
from project import app, db
|
||||
from pprint import pprint
|
||||
import datetime
|
||||
from dateutil import parser, tz
|
||||
@ -13,11 +13,11 @@ from flask import jsonify
|
||||
import re
|
||||
import unicodedata
|
||||
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 dateutils import berlin_tz
|
||||
from services.admin_unit import get_admin_unit
|
||||
from services.event import upsert_event_category, update_event_dates_with_recurrence_rule
|
||||
from project.dateutils import berlin_tz
|
||||
from project.services.admin_unit import get_admin_unit
|
||||
from project.services.event import upsert_event_category, update_event_dates_with_recurrence_rule
|
||||
|
||||
admin_unit = get_admin_unit('Harzinfo')
|
||||
category = upsert_event_category('Other')
|
||||
@ -1,5 +1,5 @@
|
||||
from app import db
|
||||
from models import AdminUnit, AdminUnitMember, AdminUnitMemberRole
|
||||
from project import db
|
||||
from project.models import AdminUnit, AdminUnitMember, AdminUnitMemberRole
|
||||
|
||||
def upsert_admin_unit(unit_name, short_name = None):
|
||||
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 dateutils import dates_from_recurrence_rule, today, date_add_time, date_set_end_of_day
|
||||
from project.models import EventReviewStatus, EventCategory, Event, EventDate, EventReference, EventPlace, Location, EventSuggestion
|
||||
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.sql import extract
|
||||
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 flask import request
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from models import EventReviewStatus, EventSuggestion
|
||||
from project.models import EventReviewStatus, EventSuggestion
|
||||
from sqlalchemy import and_
|
||||
|
||||
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):
|
||||
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.sql import asc, func
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from models import EventPlace
|
||||
from project.models import EventPlace
|
||||
from sqlalchemy.sql import asc, func
|
||||
|
||||
def upsert_event_place(admin_unit_id, organizer_id, name):
|
||||
@ -1,5 +1,5 @@
|
||||
from app import db
|
||||
from models import EventReference, EventReferenceRequest, EventReferenceRequestReviewStatus
|
||||
from project import db
|
||||
from project.models import EventReference, EventReferenceRequest, EventReferenceRequestReviewStatus
|
||||
from sqlalchemy import and_, or_, not_
|
||||
|
||||
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"):
|
||||
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 |