mirror of
https://github.com/lucaspalomodevelop/eventcally.git
synced 2026-03-13 00:07:22 +00:00
Merge pull request #433 from eventcally/issues/432
Library updates #432
This commit is contained in:
commit
b5c78617d0
12
.github/workflows/cypress.yml
vendored
12
.github/workflows/cypress.yml
vendored
@ -42,10 +42,10 @@ jobs:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.8"
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@ -116,10 +116,10 @@ jobs:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.8"
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@ -7,7 +7,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: jamescurtin/isort-action@master
|
||||
- uses: psf/black@stable
|
||||
- uses: TrueBrain/actions-flake8@v1.4.1
|
||||
12
.github/workflows/test.yml
vendored
12
.github/workflows/test.yml
vendored
@ -52,10 +52,10 @@ jobs:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.8'
|
||||
python-version: '3.9'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@ -83,10 +83,10 @@ jobs:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.8'
|
||||
python-version: '3.9'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
repos:
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.6.3
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: isort
|
||||
name: isort (python)
|
||||
- repo: https://github.com/psf/black
|
||||
rev: stable
|
||||
rev: 23.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3.7
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 3.8.4
|
||||
language_version: python3.9
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 6.0.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
@ -1,4 +1,4 @@
|
||||
FROM python:3.7
|
||||
FROM python:3.9
|
||||
|
||||
# Add rsync
|
||||
RUN apt update -qq && apt upgrade -y && apt autoremove -y
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
describe("Event", () => {
|
||||
[{ recurrence: false }, { recurrence: true }].forEach(function (test) {
|
||||
it("creates event with recurrence=" + test.recurrence, () => {
|
||||
it.only("creates event with recurrence=" + test.recurrence, () => {
|
||||
cy.login();
|
||||
cy.createAdminUnit().then(function (adminUnitId) {
|
||||
cy.visit("/admin_unit/" + adminUnitId + "/events/create");
|
||||
@ -48,7 +48,7 @@ describe("Event", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("saves draft", () => {
|
||||
it.only("saves draft", () => {
|
||||
cy.login();
|
||||
cy.createAdminUnit().then(function (adminUnitId) {
|
||||
cy.visit("/admin_unit/" + adminUnitId + "/events/create");
|
||||
|
||||
@ -14,7 +14,7 @@ docker run -p 5000:5000 -e "DATABASE_URL=postgresql://postgres@localhost/eventca
|
||||
|
||||
### Requirements
|
||||
|
||||
- Python 3.7
|
||||
- Python 3.9
|
||||
- pip
|
||||
- Postgres with postgis
|
||||
|
||||
@ -27,12 +27,12 @@ psql -c 'create database eventcally;' -U postgres
|
||||
### Install and run
|
||||
|
||||
```sh
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
(venv) pip install -r requirements.txt
|
||||
(venv) export DATABASE_URL='postgresql://postgres@localhost/eventcally'
|
||||
(venv) flask db upgrade
|
||||
(venv) gunicorn -c gunicorn.conf.py project:app
|
||||
python3 -m venv env
|
||||
source env/bin/activate
|
||||
(env) pip install -r requirements.txt
|
||||
(env) export DATABASE_URL='postgresql://postgres@localhost/eventcally'
|
||||
(env) flask db upgrade
|
||||
(env) gunicorn -c gunicorn.conf.py project:app
|
||||
```
|
||||
|
||||
## Scheduled/Cron jobs
|
||||
|
||||
@ -1 +1 @@
|
||||
Generic single-database configuration.
|
||||
Single-database configuration for Flask.
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
keys = root,sqlalchemy,alembic,flask_migrate
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
@ -34,6 +34,11 @@ level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[logger_flask_migrate]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = flask_migrate
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
from __future__ import with_statement
|
||||
|
||||
import logging
|
||||
from logging.config import fileConfig
|
||||
|
||||
from alembic import context
|
||||
from sqlalchemy import engine_from_config, pool
|
||||
from flask import current_app
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
@ -15,17 +13,24 @@ config = context.config
|
||||
fileConfig(config.config_file_name)
|
||||
logger = logging.getLogger("alembic.env")
|
||||
|
||||
|
||||
def get_engine():
|
||||
return current_app.extensions["migrate"].db.engine
|
||||
|
||||
|
||||
def get_engine_url():
|
||||
try:
|
||||
return get_engine().url.render_as_string(hide_password=False).replace("%", "%%")
|
||||
except AttributeError:
|
||||
return str(get_engine().url).replace("%", "%%")
|
||||
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
# from myapp import mymodel
|
||||
# target_metadata = mymodel.Base.metadata
|
||||
from flask import current_app
|
||||
|
||||
config.set_main_option(
|
||||
"sqlalchemy.url",
|
||||
str(current_app.extensions["migrate"].db.engine.url).replace("%", "%%"),
|
||||
)
|
||||
target_metadata = current_app.extensions["migrate"].db.metadata
|
||||
config.set_main_option("sqlalchemy.url", get_engine_url())
|
||||
target_db = current_app.extensions["migrate"].db
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
@ -33,6 +38,12 @@ target_metadata = current_app.extensions["migrate"].db.metadata
|
||||
# ... etc.
|
||||
|
||||
|
||||
def get_metadata():
|
||||
if hasattr(target_db, "metadatas"):
|
||||
return target_db.metadatas[None]
|
||||
return target_db.metadata
|
||||
|
||||
|
||||
def exclude_tables_from_config(config_):
|
||||
tables_ = config_.get("tables", None)
|
||||
if tables_ is not None:
|
||||
@ -65,7 +76,7 @@ def run_migrations_offline():
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(
|
||||
url=url,
|
||||
target_metadata=target_metadata,
|
||||
target_metadata=get_metadata(),
|
||||
literal_binds=True,
|
||||
include_object=include_object,
|
||||
)
|
||||
@ -92,16 +103,12 @@ def run_migrations_online():
|
||||
directives[:] = []
|
||||
logger.info("No changes in schema detected.")
|
||||
|
||||
connectable = engine_from_config(
|
||||
config.get_section(config.config_ini_section),
|
||||
prefix="sqlalchemy.",
|
||||
poolclass=pool.NullPool,
|
||||
)
|
||||
connectable = get_engine()
|
||||
|
||||
with connectable.connect() as connection:
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
target_metadata=get_metadata(),
|
||||
process_revision_directives=process_revision_directives,
|
||||
include_object=include_object,
|
||||
**current_app.extensions["migrate"].configure_args
|
||||
|
||||
@ -32,7 +32,9 @@ def upgrade():
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, "eventsuggestion", type_="foreignkey")
|
||||
op.drop_constraint(
|
||||
"eventsuggestion_event_id_fkey", "eventsuggestion", type_="foreignkey"
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"eventsuggestion_event_id_fkey",
|
||||
"eventsuggestion",
|
||||
@ -40,22 +42,22 @@ def downgrade():
|
||||
["event_id"],
|
||||
["id"],
|
||||
)
|
||||
op.create_table(
|
||||
"spatial_ref_sys",
|
||||
sa.Column("srid", sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column(
|
||||
"auth_name", sa.VARCHAR(length=256), autoincrement=False, nullable=True
|
||||
),
|
||||
sa.Column("auth_srid", sa.INTEGER(), autoincrement=False, nullable=True),
|
||||
sa.Column(
|
||||
"srtext", sa.VARCHAR(length=2048), autoincrement=False, nullable=True
|
||||
),
|
||||
sa.Column(
|
||||
"proj4text", sa.VARCHAR(length=2048), autoincrement=False, nullable=True
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"(srid > 0) AND (srid <= 998999)", name="spatial_ref_sys_srid_check"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("srid", name="spatial_ref_sys_pkey"),
|
||||
)
|
||||
# op.create_table(
|
||||
# "spatial_ref_sys",
|
||||
# sa.Column("srid", sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
# sa.Column(
|
||||
# "auth_name", sa.VARCHAR(length=256), autoincrement=False, nullable=True
|
||||
# ),
|
||||
# sa.Column("auth_srid", sa.INTEGER(), autoincrement=False, nullable=True),
|
||||
# sa.Column(
|
||||
# "srtext", sa.VARCHAR(length=2048), autoincrement=False, nullable=True
|
||||
# ),
|
||||
# sa.Column(
|
||||
# "proj4text", sa.VARCHAR(length=2048), autoincrement=False, nullable=True
|
||||
# ),
|
||||
# sa.CheckConstraint(
|
||||
# "(srid > 0) AND (srid <= 998999)", name="spatial_ref_sys_srid_check"
|
||||
# ),
|
||||
# sa.PrimaryKeyConstraint("srid", name="spatial_ref_sys_pkey"),
|
||||
# )
|
||||
# ### end Alembic commands ###
|
||||
|
||||
@ -55,22 +55,22 @@ def downgrade():
|
||||
op.drop_column("adminunit", "widget_link_color")
|
||||
op.drop_column("adminunit", "widget_font")
|
||||
op.drop_column("adminunit", "widget_background_color")
|
||||
op.create_table(
|
||||
"spatial_ref_sys",
|
||||
sa.Column("srid", sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column(
|
||||
"auth_name", sa.VARCHAR(length=256), autoincrement=False, nullable=True
|
||||
),
|
||||
sa.Column("auth_srid", sa.INTEGER(), autoincrement=False, nullable=True),
|
||||
sa.Column(
|
||||
"srtext", sa.VARCHAR(length=2048), autoincrement=False, nullable=True
|
||||
),
|
||||
sa.Column(
|
||||
"proj4text", sa.VARCHAR(length=2048), autoincrement=False, nullable=True
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"(srid > 0) AND (srid <= 998999)", name="spatial_ref_sys_srid_check"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("srid", name="spatial_ref_sys_pkey"),
|
||||
)
|
||||
# op.create_table(
|
||||
# "spatial_ref_sys",
|
||||
# sa.Column("srid", sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
# sa.Column(
|
||||
# "auth_name", sa.VARCHAR(length=256), autoincrement=False, nullable=True
|
||||
# ),
|
||||
# sa.Column("auth_srid", sa.INTEGER(), autoincrement=False, nullable=True),
|
||||
# sa.Column(
|
||||
# "srtext", sa.VARCHAR(length=2048), autoincrement=False, nullable=True
|
||||
# ),
|
||||
# sa.Column(
|
||||
# "proj4text", sa.VARCHAR(length=2048), autoincrement=False, nullable=True
|
||||
# ),
|
||||
# sa.CheckConstraint(
|
||||
# "(srid > 0) AND (srid <= 998999)", name="spatial_ref_sys_srid_check"
|
||||
# ),
|
||||
# sa.PrimaryKeyConstraint("srid", name="spatial_ref_sys_pkey"),
|
||||
# )
|
||||
# ### end Alembic commands ###
|
||||
|
||||
@ -9,7 +9,7 @@ import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
from alembic import op
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
from project import dbtypes
|
||||
|
||||
@ -65,7 +65,7 @@ def upgrade():
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
|
||||
migrate_category_to_categories()
|
||||
upgrade_category_to_categories()
|
||||
|
||||
# op.drop_table('spatial_ref_sys')
|
||||
op.drop_constraint("event_category_id_fkey", "event", type_="foreignkey")
|
||||
@ -73,7 +73,7 @@ def upgrade():
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def migrate_category_to_categories():
|
||||
def upgrade_category_to_categories():
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
|
||||
@ -83,32 +83,53 @@ def migrate_category_to_categories():
|
||||
session.commit()
|
||||
|
||||
|
||||
def downgrade_categories_to_category():
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
|
||||
for event in session.query(Event):
|
||||
event.category = event.categories[0]
|
||||
|
||||
session.commit()
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column(
|
||||
"event",
|
||||
sa.Column("category_id", sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column("category_id", sa.INTEGER(), autoincrement=False, nullable=True),
|
||||
)
|
||||
downgrade_categories_to_category()
|
||||
op.alter_column(
|
||||
"event",
|
||||
sa.Column(
|
||||
"category_id",
|
||||
existing_type=sa.INTEGER(),
|
||||
autoincrement=False,
|
||||
nullable=False,
|
||||
),
|
||||
)
|
||||
|
||||
op.create_foreign_key(
|
||||
"event_category_id_fkey", "event", "eventcategory", ["category_id"], ["id"]
|
||||
)
|
||||
op.create_table(
|
||||
"spatial_ref_sys",
|
||||
sa.Column("srid", sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column(
|
||||
"auth_name", sa.VARCHAR(length=256), autoincrement=False, nullable=True
|
||||
),
|
||||
sa.Column("auth_srid", sa.INTEGER(), autoincrement=False, nullable=True),
|
||||
sa.Column(
|
||||
"srtext", sa.VARCHAR(length=2048), autoincrement=False, nullable=True
|
||||
),
|
||||
sa.Column(
|
||||
"proj4text", sa.VARCHAR(length=2048), autoincrement=False, nullable=True
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"(srid > 0) AND (srid <= 998999)", name="spatial_ref_sys_srid_check"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("srid", name="spatial_ref_sys_pkey"),
|
||||
)
|
||||
# op.create_table(
|
||||
# "spatial_ref_sys",
|
||||
# sa.Column("srid", sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
# sa.Column(
|
||||
# "auth_name", sa.VARCHAR(length=256), autoincrement=False, nullable=True
|
||||
# ),
|
||||
# sa.Column("auth_srid", sa.INTEGER(), autoincrement=False, nullable=True),
|
||||
# sa.Column(
|
||||
# "srtext", sa.VARCHAR(length=2048), autoincrement=False, nullable=True
|
||||
# ),
|
||||
# sa.Column(
|
||||
# "proj4text", sa.VARCHAR(length=2048), autoincrement=False, nullable=True
|
||||
# ),
|
||||
# sa.CheckConstraint(
|
||||
# "(srid > 0) AND (srid <= 998999)", name="spatial_ref_sys_srid_check"
|
||||
# ),
|
||||
# sa.PrimaryKeyConstraint("srid", name="spatial_ref_sys_pkey"),
|
||||
# )
|
||||
op.drop_table("event_eventcategories")
|
||||
# ### end Alembic commands ###
|
||||
|
||||
@ -73,34 +73,46 @@ def upgrade():
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, "settings", type_="foreignkey")
|
||||
op.drop_constraint("settings_updated_by_id_fkey", "settings", type_="foreignkey")
|
||||
op.drop_column("settings", "updated_by_id")
|
||||
op.drop_column("settings", "updated_at")
|
||||
op.drop_constraint(None, "location", type_="foreignkey")
|
||||
op.drop_constraint("location_updated_by_id_fkey", "location", type_="foreignkey")
|
||||
op.drop_column("location", "updated_by_id")
|
||||
op.drop_column("location", "updated_at")
|
||||
op.drop_constraint(None, "image", type_="foreignkey")
|
||||
op.drop_constraint("image_updated_by_id_fkey", "image", type_="foreignkey")
|
||||
op.drop_column("image", "updated_by_id")
|
||||
op.drop_column("image", "updated_at")
|
||||
op.drop_constraint(None, "eventsuggestion", type_="foreignkey")
|
||||
op.drop_constraint(
|
||||
"eventsuggestion_updated_by_id_fkey", "eventsuggestion", type_="foreignkey"
|
||||
)
|
||||
op.drop_column("eventsuggestion", "updated_by_id")
|
||||
op.drop_column("eventsuggestion", "updated_at")
|
||||
op.drop_constraint(None, "eventreferencerequest", type_="foreignkey")
|
||||
op.drop_constraint(
|
||||
"eventreferencerequest_updated_by_id_fkey",
|
||||
"eventreferencerequest",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.drop_column("eventreferencerequest", "updated_by_id")
|
||||
op.drop_column("eventreferencerequest", "updated_at")
|
||||
op.drop_constraint(None, "eventreference", type_="foreignkey")
|
||||
op.drop_constraint(
|
||||
"eventreference_updated_by_id_fkey", "eventreference", type_="foreignkey"
|
||||
)
|
||||
op.drop_column("eventreference", "updated_by_id")
|
||||
op.drop_column("eventreference", "updated_at")
|
||||
op.drop_constraint(None, "eventplace", type_="foreignkey")
|
||||
op.drop_constraint(
|
||||
"eventplace_updated_by_id_fkey", "eventplace", type_="foreignkey"
|
||||
)
|
||||
op.drop_column("eventplace", "updated_by_id")
|
||||
op.drop_column("eventplace", "updated_at")
|
||||
op.drop_constraint(None, "eventorganizer", type_="foreignkey")
|
||||
op.drop_constraint(
|
||||
"eventorganizer_updated_by_id_fkey", "eventorganizer", type_="foreignkey"
|
||||
)
|
||||
op.drop_column("eventorganizer", "updated_by_id")
|
||||
op.drop_column("eventorganizer", "updated_at")
|
||||
op.drop_constraint(None, "event", type_="foreignkey")
|
||||
op.drop_constraint("event_updated_by_id_fkey", "event", type_="foreignkey")
|
||||
op.drop_column("event", "updated_by_id")
|
||||
op.drop_column("event", "updated_at")
|
||||
op.drop_constraint(None, "adminunit", type_="foreignkey")
|
||||
op.drop_constraint("adminunit_updated_by_id_fkey", "adminunit", type_="foreignkey")
|
||||
op.drop_column("adminunit", "updated_by_id")
|
||||
op.drop_column("adminunit", "updated_at")
|
||||
# ### end Alembic commands ###
|
||||
|
||||
@ -10,7 +10,7 @@ import sqlalchemy_utils
|
||||
from alembic import op
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy.dialects import postgresql
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
36
package-lock.json
generated
36
package-lock.json
generated
@ -613,9 +613,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cypress": {
|
||||
"version": "12.8.1",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-12.8.1.tgz",
|
||||
"integrity": "sha512-lIFbKdaSYAOarNLHNFa2aPZu6YSF+8UY4VRXMxJrFUnk6RvfG0AWsZ7/qle/aIz30TNUD4aOihz2ZgS4vuQVSA==",
|
||||
"version": "12.9.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-12.9.0.tgz",
|
||||
"integrity": "sha512-Ofe09LbHKgSqX89Iy1xen2WvpgbvNxDzsWx3mgU1mfILouELeXYGwIib3ItCwoRrRifoQwcBFmY54Vs0zw7QCg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
@ -1411,9 +1411,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
@ -1617,9 +1617,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
|
||||
"integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
@ -2512,9 +2512,9 @@
|
||||
}
|
||||
},
|
||||
"cypress": {
|
||||
"version": "12.8.1",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-12.8.1.tgz",
|
||||
"integrity": "sha512-lIFbKdaSYAOarNLHNFa2aPZu6YSF+8UY4VRXMxJrFUnk6RvfG0AWsZ7/qle/aIz30TNUD4aOihz2ZgS4vuQVSA==",
|
||||
"version": "12.9.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-12.9.0.tgz",
|
||||
"integrity": "sha512-Ofe09LbHKgSqX89Iy1xen2WvpgbvNxDzsWx3mgU1mfILouELeXYGwIib3ItCwoRrRifoQwcBFmY54Vs0zw7QCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@cypress/request": "^2.88.10",
|
||||
@ -3120,9 +3120,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
@ -3286,9 +3286,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
|
||||
"integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
|
||||
"dev": true
|
||||
},
|
||||
"request-progress": {
|
||||
|
||||
@ -88,11 +88,11 @@ class EventBaseSchemaMixin(TrackableSchemaMixin):
|
||||
}
|
||||
)
|
||||
kid_friendly = marshmallow.auto_field(
|
||||
missing=False,
|
||||
load_default=False,
|
||||
metadata={"description": "If the event is particularly suitable for children."},
|
||||
)
|
||||
accessible_for_free = marshmallow.auto_field(
|
||||
missing=False,
|
||||
load_default=False,
|
||||
metadata={"description": "If the event is accessible for free."},
|
||||
)
|
||||
age_from = marshmallow.auto_field(
|
||||
@ -103,19 +103,19 @@ class EventBaseSchemaMixin(TrackableSchemaMixin):
|
||||
)
|
||||
target_group_origin = EnumField(
|
||||
EventTargetGroupOrigin,
|
||||
missing=EventTargetGroupOrigin.both,
|
||||
load_default=EventTargetGroupOrigin.both,
|
||||
metadata={
|
||||
"description": "Whether the event is particularly suitable for tourists or residents."
|
||||
},
|
||||
)
|
||||
attendance_mode = EnumField(
|
||||
EventAttendanceMode,
|
||||
missing=EventAttendanceMode.offline,
|
||||
load_default=EventAttendanceMode.offline,
|
||||
metadata={"description": "Choose how people can attend the event."},
|
||||
)
|
||||
status = EnumField(
|
||||
EventStatus,
|
||||
missing=EventStatus.scheduled,
|
||||
load_default=EventStatus.scheduled,
|
||||
metadata={"description": "Select the status of the event."},
|
||||
)
|
||||
previous_start_date = CustomDateTimeField(
|
||||
@ -124,13 +124,13 @@ class EventBaseSchemaMixin(TrackableSchemaMixin):
|
||||
},
|
||||
)
|
||||
registration_required = marshmallow.auto_field(
|
||||
missing=False,
|
||||
load_default=False,
|
||||
metadata={
|
||||
"description": "If the participants needs to register for the event."
|
||||
},
|
||||
)
|
||||
booked_up = marshmallow.auto_field(
|
||||
missing=False,
|
||||
load_default=False,
|
||||
metadata={"description": "If the event is booked up or sold out."},
|
||||
)
|
||||
expected_participants = marshmallow.auto_field(
|
||||
@ -143,7 +143,7 @@ class EventBaseSchemaMixin(TrackableSchemaMixin):
|
||||
)
|
||||
public_status = EnumField(
|
||||
PublicStatus,
|
||||
missing=PublicStatus.published,
|
||||
load_default=PublicStatus.published,
|
||||
metadata={"description": "Public status of the event."},
|
||||
)
|
||||
|
||||
@ -296,8 +296,8 @@ class EventWriteSchemaMixin(object):
|
||||
metadata={"description": "Categories that fit the event."},
|
||||
)
|
||||
rating = marshmallow.auto_field(
|
||||
missing=50,
|
||||
default=50,
|
||||
load_default=50,
|
||||
dump_default=50,
|
||||
validate=validate.OneOf([0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]),
|
||||
metadata={
|
||||
"description": "How relevant the event is to your organization. 0 (Little relevant), 50 (Default), 100 (Highlight)."
|
||||
@ -322,7 +322,7 @@ class EventPostRequestSchema(
|
||||
|
||||
date_definitions = fields.List(
|
||||
fields.Nested(EventDateDefinitionPostRequestSchema),
|
||||
default=None,
|
||||
dump_default=None,
|
||||
required=True,
|
||||
validate=[validate.Length(min=1)],
|
||||
metadata={"description": "At least one date definition."},
|
||||
@ -339,7 +339,7 @@ class EventPatchRequestSchema(
|
||||
|
||||
date_definitions = fields.List(
|
||||
fields.Nested(EventDateDefinitionPatchRequestSchema),
|
||||
default=None,
|
||||
dump_default=None,
|
||||
required=True,
|
||||
validate=[validate.Length(min=1)],
|
||||
metadata={"description": "At least one date definition."},
|
||||
@ -370,6 +370,6 @@ class EventImportRequestSchema(marshmallow.Schema):
|
||||
)
|
||||
public_status = EnumField(
|
||||
PublicStatus,
|
||||
missing=PublicStatus.published,
|
||||
load_default=PublicStatus.published,
|
||||
metadata={"description": "Public status of the event."},
|
||||
)
|
||||
|
||||
@ -36,7 +36,7 @@ class EventDateDefinitionBaseSchemaMixin(object):
|
||||
},
|
||||
)
|
||||
allday = fields.Bool(
|
||||
missing=False,
|
||||
load_default=False,
|
||||
metadata={"description": "If the event is an all-day event."},
|
||||
)
|
||||
recurrence_rule = fields.Str(
|
||||
|
||||
@ -22,12 +22,12 @@ def etag_cache(func):
|
||||
return wrapper
|
||||
|
||||
|
||||
def require_api_access(scope=None, operator="AND", optional=False):
|
||||
def require_api_access(scopes=None, optional=False):
|
||||
def inner_decorator(func):
|
||||
def wrapped(*args, **kwargs): # see authlib ResourceProtector#__call__
|
||||
try: # pragma: no cover
|
||||
try:
|
||||
require_oauth.acquire_token(scope, operator)
|
||||
require_oauth.acquire_token(scopes)
|
||||
except MissingAuthorizationError as error:
|
||||
if optional:
|
||||
return func(*args, **kwargs)
|
||||
|
||||
@ -11,11 +11,11 @@ class SQLAlchemyBaseSchema(marshmallow.SQLAlchemySchema):
|
||||
def make_post_schema(self):
|
||||
for name, field in self.fields.items():
|
||||
if not field.required:
|
||||
if field.missing is missing:
|
||||
if field.load_default is missing:
|
||||
if isinstance(field, fields.List):
|
||||
field.missing = list()
|
||||
field.load_default = list()
|
||||
else:
|
||||
field.missing = None
|
||||
field.load_default = None
|
||||
field.allow_none = True
|
||||
|
||||
def make_patch_schema(self):
|
||||
@ -25,7 +25,7 @@ class SQLAlchemyBaseSchema(marshmallow.SQLAlchemySchema):
|
||||
|
||||
|
||||
class IdSchemaMixin(object):
|
||||
id = marshmallow.auto_field(dump_only=True, default=missing)
|
||||
id = marshmallow.auto_field(dump_only=True, dump_default=missing)
|
||||
|
||||
|
||||
class WriteIdSchemaMixin(object):
|
||||
@ -60,13 +60,13 @@ class UnprocessableEntityResponseSchema(ErrorResponseSchema):
|
||||
class PaginationRequestSchema(marshmallow.Schema):
|
||||
page = fields.Integer(
|
||||
required=False,
|
||||
default=1,
|
||||
dump_default=1,
|
||||
validate=validate.Range(min=1),
|
||||
metadata={"description": "The page number (1 indexed)."},
|
||||
)
|
||||
per_page = fields.Integer(
|
||||
required=False,
|
||||
default=20,
|
||||
dump_default=20,
|
||||
validate=validate.Range(min=1, max=50),
|
||||
metadata={"description": "Items per page"},
|
||||
)
|
||||
|
||||
@ -4,7 +4,7 @@ import click
|
||||
from flask.cli import AppGroup
|
||||
from flask_migrate import stamp
|
||||
from flask_security.confirmable import confirm_user
|
||||
from sqlalchemy import MetaData
|
||||
from sqlalchemy import MetaData, text
|
||||
|
||||
from project import app, db
|
||||
from project.api import scope_list
|
||||
@ -75,14 +75,15 @@ def _create_user(
|
||||
@test_cli.command("reset")
|
||||
@click.option("--seed/--no-seed", default=False)
|
||||
def reset(seed):
|
||||
meta = MetaData(bind=db.engine, reflect=True)
|
||||
meta = MetaData()
|
||||
meta.reflect(db.engine)
|
||||
con = db.engine.connect()
|
||||
trans = con.begin()
|
||||
|
||||
for table in meta.sorted_tables:
|
||||
con.execute(f'ALTER TABLE "{table.name}" DISABLE TRIGGER ALL;')
|
||||
con.execute(text(f'ALTER TABLE "{table.name}" DISABLE TRIGGER ALL;'))
|
||||
con.execute(table.delete())
|
||||
con.execute(f'ALTER TABLE "{table.name}" ENABLE TRIGGER ALL;')
|
||||
con.execute(text(f'ALTER TABLE "{table.name}" ENABLE TRIGGER ALL;'))
|
||||
|
||||
trans.commit()
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ from sqlalchemy.types import TypeDecorator
|
||||
|
||||
class IntegerEnum(TypeDecorator):
|
||||
impl = Integer
|
||||
cache_ok = True
|
||||
|
||||
def __init__(self, enumtype, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from flask_babelex import lazy_gettext
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import BooleanField, RadioField, StringField, SubmitField, TextAreaField
|
||||
from wtforms.fields.html5 import EmailField
|
||||
from wtforms.fields import EmailField
|
||||
from wtforms.validators import DataRequired, Optional
|
||||
|
||||
from project.forms.widgets import MultiCheckboxField
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
from flask_babelex import lazy_gettext
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import DecimalField, FormField, StringField, SubmitField, TextAreaField
|
||||
from wtforms.fields.core import BooleanField
|
||||
from wtforms.fields.html5 import EmailField, TelField, URLField
|
||||
from wtforms.fields import BooleanField, EmailField, TelField, URLField
|
||||
from wtforms.validators import DataRequired, Length, Optional, Regexp
|
||||
from wtforms.widgets.html5 import ColorInput
|
||||
from wtforms.widgets import ColorInput
|
||||
|
||||
from project.forms.common import Base64ImageForm
|
||||
from project.forms.widgets import HTML5StringField
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from flask_babelex import lazy_gettext
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import SubmitField
|
||||
from wtforms.fields.html5 import EmailField
|
||||
from wtforms.fields import EmailField
|
||||
from wtforms.validators import DataRequired
|
||||
|
||||
from project.forms.widgets import MultiCheckboxField
|
||||
|
||||
@ -32,8 +32,8 @@ class Base64ImageForm(BaseImageForm):
|
||||
obj.data, obj.encoding_format
|
||||
)
|
||||
|
||||
def validate(self):
|
||||
result = super().validate()
|
||||
def validate(self, extra_validators=None):
|
||||
result = super().validate(extra_validators)
|
||||
|
||||
if self.image_base64.data:
|
||||
image = get_image_from_base64_str(self.image_base64.data)
|
||||
|
||||
@ -14,8 +14,7 @@ from wtforms import (
|
||||
SubmitField,
|
||||
TextAreaField,
|
||||
)
|
||||
from wtforms.fields.core import FieldList
|
||||
from wtforms.fields.html5 import EmailField, URLField
|
||||
from wtforms.fields import EmailField, FieldList, URLField
|
||||
from wtforms.validators import DataRequired, Length, Optional
|
||||
|
||||
from project.forms.common import Base64ImageForm, distance_choices, event_rating_choices
|
||||
@ -72,8 +71,8 @@ class EventDateDefinitionFormMixin:
|
||||
|
||||
|
||||
class EventDateDefinitionForm(FlaskForm, EventDateDefinitionFormMixin):
|
||||
def validate(self):
|
||||
result = super().validate()
|
||||
def validate(self, extra_validators=None):
|
||||
result = super().validate(extra_validators)
|
||||
|
||||
if not self.validate_date_definition():
|
||||
result = False
|
||||
@ -270,8 +269,8 @@ class BaseEventForm(SharedEventForm):
|
||||
),
|
||||
)
|
||||
|
||||
def validate(self):
|
||||
result = super().validate()
|
||||
def validate(self, extra_validators=None):
|
||||
result = super().validate(extra_validators)
|
||||
|
||||
if self.co_organizer_ids.data and self.organizer_id.data:
|
||||
if self.organizer_id.data in self.co_organizer_ids.data:
|
||||
@ -342,8 +341,8 @@ class CreateEventForm(BaseEventForm):
|
||||
PublicStatus.published if self.submit.data else PublicStatus.draft
|
||||
)
|
||||
|
||||
def validate(self):
|
||||
result = super().validate()
|
||||
def validate(self, extra_validators=None):
|
||||
result = super().validate(extra_validators)
|
||||
|
||||
if (
|
||||
not self.event_place_id.data or self.event_place_id.data == 0
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from flask_babelex import lazy_gettext
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import DecimalField, FormField, StringField, SubmitField, TextAreaField
|
||||
from wtforms.fields.html5 import URLField
|
||||
from wtforms.fields import URLField
|
||||
from wtforms.validators import DataRequired, Optional
|
||||
|
||||
from project.forms.common import Base64ImageForm
|
||||
|
||||
@ -7,7 +7,7 @@ from wtforms import (
|
||||
StringField,
|
||||
SubmitField,
|
||||
)
|
||||
from wtforms.fields.html5 import EmailField, TelField
|
||||
from wtforms.fields import EmailField, TelField
|
||||
from wtforms.validators import DataRequired, Optional
|
||||
|
||||
from project.forms.common import get_accept_tos_markup
|
||||
@ -110,8 +110,8 @@ class CreateEventSuggestionForm(SharedEventForm, EventDateDefinitionFormMixin):
|
||||
else:
|
||||
field.populate_obj(obj, name)
|
||||
|
||||
def validate(self):
|
||||
result = super().validate()
|
||||
def validate(self, extra_validators=None):
|
||||
result = super().validate(extra_validators)
|
||||
|
||||
if not self.validate_date_definition():
|
||||
result = False
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from flask_babelex import lazy_gettext
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import DecimalField, FormField, StringField, SubmitField
|
||||
from wtforms.fields.html5 import EmailField, TelField, URLField
|
||||
from wtforms.fields import EmailField, TelField, URLField
|
||||
from wtforms.validators import DataRequired, Optional
|
||||
|
||||
from project.forms.common import Base64ImageForm
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from flask_babelex import lazy_gettext
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import SelectField, StringField, SubmitField
|
||||
from wtforms.fields.core import BooleanField
|
||||
from wtforms.fields import BooleanField
|
||||
from wtforms.validators import DataRequired, Optional
|
||||
|
||||
from project.forms.common import event_rating_choices
|
||||
|
||||
@ -35,8 +35,12 @@ class ExtendedConfirmRegisterForm(ConfirmRegisterForm):
|
||||
|
||||
|
||||
class ExtendedLoginForm(LoginForm):
|
||||
def validate(self):
|
||||
result = super().validate()
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._fields["email"].flags.required = True
|
||||
|
||||
def validate(self, **kwargs):
|
||||
result = super().validate(**kwargs)
|
||||
|
||||
if not result and self.requires_confirmation:
|
||||
flash_message(
|
||||
|
||||
@ -3,7 +3,7 @@ from datetime import datetime
|
||||
from flask_babelex import gettext, to_user_timezone
|
||||
from markupsafe import Markup
|
||||
from wtforms import DateTimeField, SelectField, SelectMultipleField
|
||||
from wtforms.fields.core import StringField
|
||||
from wtforms.fields import StringField
|
||||
from wtforms.validators import Length, StopValidation
|
||||
from wtforms.widgets import CheckboxInput, ListWidget, html_params
|
||||
|
||||
@ -26,13 +26,14 @@ class CustomDateTimeWidget:
|
||||
time = date_value.strftime("%H:%M")
|
||||
|
||||
kwargs_class = kwargs.pop("class", "")
|
||||
required = True if field.flags.required else False
|
||||
|
||||
date_class = kwargs_class + " datepicker"
|
||||
date_params = html_params(
|
||||
name=field.name,
|
||||
id=id,
|
||||
value=date,
|
||||
required=field.flags.required,
|
||||
required=required,
|
||||
class_=date_class,
|
||||
**kwargs
|
||||
)
|
||||
@ -42,7 +43,7 @@ class CustomDateTimeWidget:
|
||||
name=field.name,
|
||||
id=id + "-time",
|
||||
value=time,
|
||||
required=field.flags.required,
|
||||
required=required,
|
||||
class_=time_class,
|
||||
**kwargs
|
||||
)
|
||||
@ -164,14 +165,14 @@ class HTML5StringField(StringField):
|
||||
self,
|
||||
label=None,
|
||||
validators=None,
|
||||
filters=tuple(),
|
||||
filters=(),
|
||||
description="",
|
||||
id=None,
|
||||
default=None,
|
||||
widget=None,
|
||||
render_kw=None,
|
||||
name=None,
|
||||
_form=None,
|
||||
_name=None,
|
||||
_prefix="",
|
||||
_translations=None,
|
||||
_meta=None,
|
||||
@ -194,8 +195,8 @@ class HTML5StringField(StringField):
|
||||
default,
|
||||
widget,
|
||||
render_kw,
|
||||
name,
|
||||
_form,
|
||||
_name,
|
||||
_prefix,
|
||||
_translations,
|
||||
_meta,
|
||||
|
||||
@ -54,14 +54,14 @@ def resize_image_to_min(image: PIL.Image) -> PIL.Image:
|
||||
width = int(math.ceil(image.width * ratio))
|
||||
height = int(math.ceil(image.height * ratio))
|
||||
format = image.format
|
||||
result = image.resize((width, height), PIL.Image.LANCZOS)
|
||||
result = image.resize((width, height), PIL.Image.Resampling.LANCZOS)
|
||||
result.format = format
|
||||
return result
|
||||
|
||||
|
||||
def resize_image_to_max(image: PIL.Image):
|
||||
if image.width > max_image_size or image.height > max_image_size:
|
||||
image.thumbnail((max_image_size, max_image_size), PIL.Image.ANTIALIAS)
|
||||
image.thumbnail((max_image_size, max_image_size), PIL.Image.Resampling.LANCZOS)
|
||||
|
||||
|
||||
def validate_image(image: PIL.Image):
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from flask_security import RoleMixin
|
||||
from flask_security import AsaList, RoleMixin
|
||||
from sqlalchemy import (
|
||||
Boolean,
|
||||
Column,
|
||||
@ -14,6 +14,7 @@ from sqlalchemy import (
|
||||
)
|
||||
from sqlalchemy.event import listens_for
|
||||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
from sqlalchemy.ext.mutable import MutableList
|
||||
from sqlalchemy.orm import aliased, backref, deferred, relationship
|
||||
from sqlalchemy.orm.relationships import remote
|
||||
from sqlalchemy.schema import CheckConstraint
|
||||
@ -37,7 +38,7 @@ class AdminUnitMemberRole(db.Model, RoleMixin):
|
||||
name = Column(String(80), unique=True)
|
||||
title = Column(Unicode(255))
|
||||
description = Column(String(255))
|
||||
permissions = Column(UnicodeText())
|
||||
permissions = Column(MutableList.as_mutable(AsaList()), nullable=True)
|
||||
|
||||
|
||||
class AdminUnitMember(db.Model):
|
||||
@ -299,7 +300,7 @@ class AdminUnit(db.Model, TrackableMixin):
|
||||
AdminUnitRelation.source_admin_unit_id == SourceAdminUnit.id,
|
||||
)
|
||||
return (
|
||||
select([func.count()])
|
||||
select(func.count())
|
||||
.select_from(j)
|
||||
.where(
|
||||
and_(
|
||||
@ -308,7 +309,7 @@ class AdminUnit(db.Model, TrackableMixin):
|
||||
SourceAdminUnit.can_verify_other,
|
||||
)
|
||||
)
|
||||
.as_scalar()
|
||||
.scalar_subquery()
|
||||
> 0
|
||||
)
|
||||
|
||||
|
||||
@ -91,11 +91,11 @@ class Event(db.Model, TrackableMixin, EventMixin):
|
||||
@min_start.expression
|
||||
def min_start(cls):
|
||||
return (
|
||||
select([EventDateDefinition.start])
|
||||
select(EventDateDefinition.start)
|
||||
.where(EventDateDefinition.event_id == cls.id)
|
||||
.order_by(EventDateDefinition.start)
|
||||
.limit(1)
|
||||
.as_scalar()
|
||||
.scalar_subquery()
|
||||
)
|
||||
|
||||
@hybrid_property
|
||||
@ -108,7 +108,7 @@ class Event(db.Model, TrackableMixin, EventMixin):
|
||||
@is_recurring.expression
|
||||
def is_recurring(cls):
|
||||
return (
|
||||
select([func.count()])
|
||||
select(func.count())
|
||||
.select_from(EventDateDefinition.__table__)
|
||||
.where(
|
||||
and_(
|
||||
@ -116,7 +116,7 @@ class Event(db.Model, TrackableMixin, EventMixin):
|
||||
func.coalesce(EventDateDefinition.recurrence_rule, "") != "",
|
||||
)
|
||||
)
|
||||
.as_scalar()
|
||||
.scalar_subquery()
|
||||
) > 0
|
||||
|
||||
date_definitions = relationship(
|
||||
|
||||
@ -9,8 +9,7 @@ from sqlalchemy import (
|
||||
Unicode,
|
||||
UnicodeText,
|
||||
)
|
||||
from sqlalchemy.ext.declarative import declared_attr
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.orm import declared_attr, relationship
|
||||
|
||||
from project.dbtypes import IntegerEnum
|
||||
from project.models.functions import create_tsvector
|
||||
|
||||
@ -41,6 +41,12 @@ class OAuth2Client(db.Model, OAuth2ClientMixin):
|
||||
def check_token_endpoint_auth_method(self, method):
|
||||
return method in self.token_endpoint_auth_method
|
||||
|
||||
def check_endpoint_auth_method(self, method, endpoint):
|
||||
if endpoint == "token":
|
||||
return self.check_token_endpoint_auth_method(method)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class OAuth2AuthorizationCode(db.Model, OAuth2AuthorizationCodeMixin):
|
||||
__tablename__ = "oauth2_code"
|
||||
@ -66,8 +72,15 @@ class OAuth2Token(db.Model, OAuth2TokenMixin):
|
||||
.first()
|
||||
)
|
||||
|
||||
@property
|
||||
def expires_at(self):
|
||||
return self.issued_at + self.expires_in
|
||||
|
||||
def is_refresh_token_active(self):
|
||||
if self.revoked:
|
||||
if self.is_revoked():
|
||||
return False
|
||||
expires_at = self.issued_at + self.expires_in * 2
|
||||
return expires_at >= time.time()
|
||||
|
||||
return self.expires_at >= time.time()
|
||||
|
||||
def revoke_token(self):
|
||||
self.access_token_revoked_at = int(time.time())
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import datetime
|
||||
|
||||
from sqlalchemy import Column, DateTime, ForeignKey
|
||||
from sqlalchemy.ext.declarative import declared_attr
|
||||
from sqlalchemy.orm import deferred, relationship
|
||||
from sqlalchemy.orm import declared_attr, deferred, relationship
|
||||
|
||||
from project.models.functions import _current_user_id_or_none
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import datetime
|
||||
|
||||
from flask_dance.consumer.storage.sqla import OAuthConsumerMixin
|
||||
from flask_security import RoleMixin, UserMixin
|
||||
from flask_security import AsaList, RoleMixin, UserMixin
|
||||
from sqlalchemy import (
|
||||
Boolean,
|
||||
Column,
|
||||
@ -10,9 +10,9 @@ from sqlalchemy import (
|
||||
Integer,
|
||||
String,
|
||||
Unicode,
|
||||
UnicodeText,
|
||||
UniqueConstraint,
|
||||
)
|
||||
from sqlalchemy.ext.mutable import MutableList
|
||||
from sqlalchemy.orm import backref, deferred, relationship
|
||||
|
||||
from project import db
|
||||
@ -31,7 +31,7 @@ class Role(db.Model, RoleMixin):
|
||||
name = Column(String(80), unique=True)
|
||||
title = Column(Unicode(255))
|
||||
description = Column(String(255))
|
||||
permissions = Column(UnicodeText())
|
||||
permissions = Column(MutableList.as_mutable(AsaList()), nullable=True)
|
||||
|
||||
|
||||
class User(db.Model, UserMixin):
|
||||
|
||||
@ -108,19 +108,21 @@ class RefreshTokenGrant(grants.RefreshTokenGrant):
|
||||
class MyIntrospectionEndpoint(IntrospectionEndpoint):
|
||||
CLIENT_AUTH_METHODS = ["client_secret_basic", "client_secret_post"]
|
||||
|
||||
def query_token(self, token, token_type_hint, client):
|
||||
def query_token(self, token_string, token_type_hint):
|
||||
if token_type_hint == "access_token":
|
||||
tok = OAuth2Token.query.filter_by(access_token=token).first()
|
||||
tok = OAuth2Token.query.filter_by(access_token=token_string).first()
|
||||
elif token_type_hint == "refresh_token":
|
||||
tok = OAuth2Token.query.filter_by(refresh_token=token).first()
|
||||
tok = OAuth2Token.query.filter_by(refresh_token=token_string).first()
|
||||
else:
|
||||
# without token_type_hint
|
||||
tok = OAuth2Token.query.filter_by(access_token=token).first()
|
||||
tok = OAuth2Token.query.filter_by(access_token=token_string).first()
|
||||
if not tok:
|
||||
tok = OAuth2Token.query.filter_by(refresh_token=token).first()
|
||||
if tok:
|
||||
if tok.client_id == client.client_id:
|
||||
return tok
|
||||
tok = OAuth2Token.query.filter_by(refresh_token=token_string).first()
|
||||
|
||||
return tok
|
||||
|
||||
def check_permission(self, token, client, request):
|
||||
return token.client_id == client.client_id
|
||||
|
||||
def introspect_token(self, token):
|
||||
return {
|
||||
@ -132,7 +134,7 @@ class MyIntrospectionEndpoint(IntrospectionEndpoint):
|
||||
"sub": str(token.user.id),
|
||||
"aud": token.client_id,
|
||||
"iss": get_issuer(),
|
||||
"exp": token.get_expires_at(),
|
||||
"exp": token.expires_at,
|
||||
"iat": token.issued_at,
|
||||
}
|
||||
|
||||
@ -154,11 +156,11 @@ def create_revocation_endpoint(session, token_model):
|
||||
class _RevocationEndpoint(RevocationEndpoint):
|
||||
CLIENT_AUTH_METHODS = ["client_secret_basic", "client_secret_post"]
|
||||
|
||||
def query_token(self, token, token_type_hint, client):
|
||||
return query_token(token, token_type_hint, client)
|
||||
def query_token(self, token_string, token_type_hint):
|
||||
return query_token(token_string, token_type_hint)
|
||||
|
||||
def revoke_token(self, token):
|
||||
token.revoked = True
|
||||
def revoke_token(self, token, request):
|
||||
token.revoke_token()
|
||||
session.add(token)
|
||||
session.commit()
|
||||
|
||||
|
||||
@ -118,8 +118,7 @@ def upsert_admin_unit_member_role(role_name, role_title, permissions):
|
||||
db.session.add(result)
|
||||
|
||||
result.title = role_title
|
||||
result.remove_permissions(result.get_permissions())
|
||||
result.add_permissions(permissions)
|
||||
result.permissions = permissions
|
||||
return result
|
||||
|
||||
|
||||
|
||||
@ -7,7 +7,14 @@ from flask import url_for
|
||||
from flask_babelex import format_date, format_time, gettext
|
||||
from icalendar.prop import vDDDLists
|
||||
from sqlalchemy import and_, case, func, or_
|
||||
from sqlalchemy.orm import aliased, contains_eager, defaultload, joinedload, lazyload
|
||||
from sqlalchemy.orm import (
|
||||
aliased,
|
||||
contains_eager,
|
||||
defaultload,
|
||||
joinedload,
|
||||
lazyload,
|
||||
undefer_group,
|
||||
)
|
||||
from sqlalchemy.sql import extract
|
||||
|
||||
from project import app, db
|
||||
@ -194,7 +201,7 @@ def get_event_dates_query(params):
|
||||
|
||||
result = (
|
||||
result.options(
|
||||
contains_eager(EventDate.event)
|
||||
joinedload(EventDate.event)
|
||||
.contains_eager(Event.event_place)
|
||||
.contains_eager(EventPlace.location),
|
||||
joinedload(EventDate.event)
|
||||
@ -216,12 +223,10 @@ def get_event_dates_query(params):
|
||||
if admin_unit_reference:
|
||||
result = result.order_by(
|
||||
case(
|
||||
[
|
||||
(
|
||||
admin_unit_reference.rating.isnot(None),
|
||||
admin_unit_reference.rating,
|
||||
),
|
||||
],
|
||||
(
|
||||
admin_unit_reference.rating.isnot(None),
|
||||
admin_unit_reference.rating,
|
||||
),
|
||||
else_=Event.rating,
|
||||
).desc()
|
||||
)
|
||||
@ -238,12 +243,12 @@ def get_event_date_with_details_or_404(event_id):
|
||||
.join(Event.event_place, isouter=True)
|
||||
.join(EventPlace.location, isouter=True)
|
||||
.options(
|
||||
contains_eager(EventDate.event)
|
||||
joinedload(EventDate.event)
|
||||
.contains_eager(Event.event_place)
|
||||
.contains_eager(EventPlace.location),
|
||||
joinedload(EventDate.event).undefer_group("trackable"),
|
||||
# Place
|
||||
defaultload(EventDate.event)
|
||||
joinedload(EventDate.event)
|
||||
.defaultload(Event.event_place)
|
||||
.joinedload(EventPlace.photo),
|
||||
# Category
|
||||
@ -254,19 +259,19 @@ def get_event_date_with_details_or_404(event_id):
|
||||
joinedload(EventDate.event)
|
||||
.joinedload(Event.organizer)
|
||||
.undefer_group("detail")
|
||||
.undefer("logo_id")
|
||||
.undefer(EventOrganizer.logo_id)
|
||||
.joinedload(EventOrganizer.logo),
|
||||
# Photo
|
||||
joinedload(EventDate.event).joinedload(Event.photo),
|
||||
# Admin unit
|
||||
joinedload(EventDate.event)
|
||||
.joinedload(Event.admin_unit)
|
||||
.undefer("logo_id")
|
||||
.undefer(AdminUnit.logo_id)
|
||||
.undefer_group("detail")
|
||||
.undefer_group("widget")
|
||||
.joinedload(AdminUnit.location),
|
||||
# Admin unit logo
|
||||
defaultload(EventDate.event)
|
||||
joinedload(EventDate.event)
|
||||
.defaultload(Event.admin_unit)
|
||||
.joinedload(AdminUnit.logo),
|
||||
)
|
||||
@ -277,12 +282,12 @@ def get_event_date_with_details_or_404(event_id):
|
||||
|
||||
def get_event_with_details_or_404(event_id):
|
||||
return (
|
||||
Event.query.join(EventPlace, isouter=True)
|
||||
Event.query.join(Event.event_place, isouter=True)
|
||||
.join(Location, isouter=True)
|
||||
.options(
|
||||
contains_eager(Event.event_place).contains_eager(EventPlace.location),
|
||||
defaultload(Event).undefer_group("trackable"),
|
||||
undefer_group("trackable"),
|
||||
# Place
|
||||
joinedload(Event.event_place).contains_eager(EventPlace.location),
|
||||
joinedload(Event.event_place).joinedload(EventPlace.photo),
|
||||
# Category
|
||||
joinedload(Event.categories).load_only(
|
||||
@ -291,13 +296,13 @@ def get_event_with_details_or_404(event_id):
|
||||
# Organizer
|
||||
joinedload(Event.organizer)
|
||||
.undefer_group("detail")
|
||||
.undefer("logo_id")
|
||||
.undefer(EventOrganizer.logo_id)
|
||||
.joinedload(EventOrganizer.logo),
|
||||
# Photo
|
||||
joinedload(Event.photo),
|
||||
# Admin unit with location
|
||||
joinedload(Event.admin_unit)
|
||||
.undefer("logo_id")
|
||||
.undefer(AdminUnit.logo_id)
|
||||
.undefer_group("detail")
|
||||
.undefer_group("widget")
|
||||
.joinedload(AdminUnit.location),
|
||||
@ -635,8 +640,10 @@ def create_ical_events_for_search(
|
||||
|
||||
|
||||
def update_recurring_dates():
|
||||
from sqlalchemy import text
|
||||
|
||||
# Setting the timezone is neccessary for cli command
|
||||
db.session.execute("SET timezone TO :val;", {"val": berlin_tz.zone})
|
||||
db.session.execute(text("SET timezone TO :val;"), {"val": berlin_tz.zone})
|
||||
|
||||
events = get_recurring_events()
|
||||
|
||||
|
||||
@ -42,8 +42,7 @@ def set_roles_for_user(email, roles):
|
||||
def upsert_user_role(role_name, role_title, permissions):
|
||||
role = user_datastore.find_or_create_role(role_name)
|
||||
role.title = role_title
|
||||
role.remove_permissions(role.get_permissions())
|
||||
role.add_permissions(permissions)
|
||||
role.permissions = permissions
|
||||
return role
|
||||
|
||||
|
||||
|
||||
@ -156,7 +156,7 @@ function start_datepicker(input) {
|
||||
$(document).find(data_allday_attr).on('change', function() {
|
||||
$("#" + hidden_field_id + "-time").toggle(!this.checked);
|
||||
if (data_range_to_attr) {
|
||||
$(data_range_to_attr + "-time").toggle(!this.checked);
|
||||
$(document).find(data_range_to_attr + "-time").toggle(!this.checked);
|
||||
}
|
||||
|
||||
onAlldayChecked(this, hidden_field_id)
|
||||
|
||||
@ -299,6 +299,9 @@ def prepare_event_place(form):
|
||||
if place:
|
||||
form.event_place_id.choices = [(place.id, get_place_str(place))]
|
||||
|
||||
if not form.event_place_id.choices:
|
||||
form.event_place_id.choices = []
|
||||
|
||||
|
||||
def prepare_organizer(form):
|
||||
if form.organizer_id.data and form.organizer_id.data > 0:
|
||||
@ -313,6 +316,12 @@ def prepare_organizer(form):
|
||||
).all()
|
||||
form.co_organizer_ids.choices = [(o.id, o.name) for o in co_organizers]
|
||||
|
||||
if not form.organizer_id.choices:
|
||||
form.organizer_id.choices = []
|
||||
|
||||
if not form.co_organizer_ids.choices:
|
||||
form.co_organizer_ids.choices = []
|
||||
|
||||
|
||||
def prepare_event_form(form):
|
||||
form.category_ids.choices = get_event_category_choices()
|
||||
|
||||
@ -26,6 +26,7 @@ from project.views.utils import (
|
||||
def prepare_event_date_form(form):
|
||||
form.category_id.choices = get_event_category_choices()
|
||||
form.category_id.choices.insert(0, (0, ""))
|
||||
form.location.choices = []
|
||||
|
||||
|
||||
@app.route("/eventdates")
|
||||
|
||||
@ -176,6 +176,8 @@ def manage_admin_unit_events(id):
|
||||
|
||||
if form.location.data: # pragma: no cover
|
||||
form.location.choices = [(form.location.data, form.location_name.data)]
|
||||
else:
|
||||
form.location.choices = []
|
||||
|
||||
if form.validate():
|
||||
form.populate_obj(params)
|
||||
|
||||
@ -23,7 +23,7 @@ def authorize():
|
||||
return authorization.create_authorization_response(grant_user=grant_user)
|
||||
else:
|
||||
try:
|
||||
grant = authorization.validate_consent_request(end_user=user)
|
||||
grant = authorization.get_consent_grant(end_user=user)
|
||||
except OAuth2Error as error:
|
||||
return error.description, error.status_code
|
||||
|
||||
|
||||
@ -16,14 +16,14 @@ def oauth2_token_revoke(id):
|
||||
oauth2_token = OAuth2Token.query.get_or_404(id)
|
||||
owner_access_or_401(oauth2_token.user_id)
|
||||
|
||||
if oauth2_token.revoked:
|
||||
if oauth2_token.is_revoked() > 0:
|
||||
return redirect(url_for("oauth2_tokens"))
|
||||
|
||||
form = RevokeOAuth2TokenForm()
|
||||
|
||||
if form.validate_on_submit():
|
||||
try:
|
||||
oauth2_token.revoked = True
|
||||
oauth2_token.revoke_token()
|
||||
db.session.commit()
|
||||
flash(gettext("OAuth2 token successfully revoked"), "success")
|
||||
return redirect(url_for("oauth2_tokens"))
|
||||
|
||||
@ -4,6 +4,7 @@ import os.path
|
||||
from flask import render_template, request, send_from_directory, url_for
|
||||
from flask_babelex import gettext
|
||||
from markupsafe import Markup
|
||||
from sqlalchemy import text
|
||||
|
||||
from project import (
|
||||
app,
|
||||
@ -40,7 +41,8 @@ def home():
|
||||
|
||||
@app.route("/up")
|
||||
def up():
|
||||
db.engine.execute("SELECT 1")
|
||||
with db.engine.connect() as conn:
|
||||
conn.execute(text("SELECT 1"))
|
||||
|
||||
if "REDIS_URL" in app.config and app.config["REDIS_URL"]: # pragma: no cover
|
||||
celery.control.ping()
|
||||
|
||||
134
requirements.txt
134
requirements.txt
@ -1,21 +1,23 @@
|
||||
alembic==1.4.3
|
||||
alembic==1.10.3
|
||||
amqp==5.1.1
|
||||
aniso8601==8.1.0
|
||||
anytree==2.8.0
|
||||
apispec==4.0.0
|
||||
apispec-webframeworks==0.5.2
|
||||
appdirs==1.4.4
|
||||
argh==0.26.2
|
||||
arrow==0.14.7
|
||||
argh==0.28.1
|
||||
arrow==1.2.3
|
||||
async-timeout==4.0.2
|
||||
attrs==20.3.0
|
||||
Authlib==0.15.3
|
||||
Authlib==1.2.0
|
||||
Babel==2.9.1
|
||||
bcrypt==3.2.0
|
||||
beautifulsoup4==4.9.3
|
||||
bcrypt==4.0.1
|
||||
beautifulsoup4==4.12.2
|
||||
billiard==3.6.4.0
|
||||
black==23.1.0
|
||||
black==23.3.0
|
||||
blinker==1.4
|
||||
cached-property==1.5.2
|
||||
cachetools==5.3.0
|
||||
celery==5.2.7
|
||||
certifi==2020.12.5
|
||||
cffi==1.14.4
|
||||
@ -27,109 +29,121 @@ click-plugins==1.1.1
|
||||
click-repl==0.2.0
|
||||
colour==0.1.5
|
||||
coverage==5.5
|
||||
coveralls==2.2.0
|
||||
coveralls==3.3.1
|
||||
cryptography==3.3.2
|
||||
decorator==5.1.0
|
||||
distlib==0.3.1
|
||||
distlib==0.3.6
|
||||
dnspython==2.0.0
|
||||
docopt==0.6.2
|
||||
dominate==2.6.0
|
||||
email-validator==1.1.2
|
||||
filelock==3.0.12
|
||||
flake8==3.8.4
|
||||
Flask==1.1.2
|
||||
flask-apispec==0.11.0
|
||||
exceptiongroup==1.1.1
|
||||
filelock==3.11.0
|
||||
flake8==6.0.0
|
||||
Flask==2.2.3
|
||||
flask-apispec==0.11.4
|
||||
Flask-BabelEx==0.9.4
|
||||
Flask-Bootstrap==3.3.7.1
|
||||
Flask-Cors==3.0.9
|
||||
Flask-Dance==3.2.0
|
||||
Flask-Cors==3.0.10
|
||||
Flask-Dance==6.2.0
|
||||
Flask-gzip==0.2
|
||||
Flask-Login==0.5.0
|
||||
Flask-Login==0.6.2
|
||||
Flask-Mail==0.9.1
|
||||
flask-marshmallow==0.14.0
|
||||
Flask-Migrate==2.5.3
|
||||
flask-marshmallow==0.15.0
|
||||
Flask-Migrate==4.0.4
|
||||
Flask-Principal==0.4.0
|
||||
Flask-QRcode==3.0.0
|
||||
Flask-RESTful==0.3.8
|
||||
Flask-Security-Too==4.0.0
|
||||
Flask-SQLAlchemy==2.4.4
|
||||
Flask-WTF==0.14.3
|
||||
GeoAlchemy2==0.8.4
|
||||
googlemaps==4.4.7
|
||||
gunicorn==20.0.4
|
||||
icalendar==4.0.7
|
||||
Flask-QRcode==3.1.0
|
||||
Flask-RESTful==0.3.9
|
||||
Flask-Security-Too==5.1.2
|
||||
Flask-SQLAlchemy==3.0.3
|
||||
Flask-WTF==1.1.1
|
||||
GeoAlchemy2==0.13.1
|
||||
googlemaps==4.10.0
|
||||
greenlet==2.0.2
|
||||
gunicorn==20.1.0
|
||||
icalendar==5.0.5
|
||||
identify==1.5.10
|
||||
idna==2.10
|
||||
importlib-metadata==3.1.1
|
||||
importlib-metadata==6.3.0
|
||||
iniconfig==1.1.1
|
||||
isort==5.7.0
|
||||
itsdangerous==1.1.0
|
||||
Jinja2==2.11.3
|
||||
isort==5.12.0
|
||||
itsdangerous==2.1.2
|
||||
Jinja2==3.1.2
|
||||
johnnydep==1.18.0
|
||||
jsonschema==3.2.0
|
||||
kombu==5.2.4
|
||||
Mako==1.1.3
|
||||
MarkupSafe==1.1.1
|
||||
marshmallow==3.10.0
|
||||
MarkupSafe==2.1.2
|
||||
marshmallow==3.19.0
|
||||
marshmallow-enum==1.5.1
|
||||
marshmallow-sqlalchemy==0.24.1
|
||||
mccabe==0.6.1
|
||||
mistune==0.8.4
|
||||
marshmallow-sqlalchemy==0.29.0
|
||||
mccabe==0.7.0
|
||||
mistune==2.0.5
|
||||
mypy-extensions==0.4.3
|
||||
nodeenv==1.5.0
|
||||
oauthlib==3.1.0
|
||||
oyaml==1.0
|
||||
packaging==23.0
|
||||
passlib==1.7.4
|
||||
pathspec==0.11.0
|
||||
pilkit==2.0
|
||||
Pillow==9.0.0
|
||||
Pillow==9.5.0
|
||||
pipdeptree==2.7.0
|
||||
pkginfo==1.9.6
|
||||
platformdirs==3.1.0
|
||||
pluggy==0.13.1
|
||||
pre-commit==2.9.3
|
||||
pre-commit==3.2.2
|
||||
prompt-toolkit==3.0.38
|
||||
psycopg2-binary==2.8.6
|
||||
psycopg2-binary==2.9.6
|
||||
py==1.10.0
|
||||
pycodestyle==2.6.0
|
||||
pycodestyle==2.10.0
|
||||
pycparser==2.20
|
||||
pyflakes==2.2.0
|
||||
pyflakes==3.0.1
|
||||
pyparsing==2.4.7
|
||||
pyrsistent==0.17.3
|
||||
pytest==6.1.2
|
||||
pytest-cov==2.10.1
|
||||
pytest-datadir==1.3.1
|
||||
pytest-mock==3.3.1
|
||||
pytest-split==0.6.0
|
||||
pytest==7.3.1
|
||||
pytest-celery==0.0.0
|
||||
pytest-cov==4.0.0
|
||||
pytest-datadir==1.4.1
|
||||
pytest-mock==3.10.0
|
||||
pytest-split==0.8.1
|
||||
python-dateutil==2.8.1
|
||||
python-dotenv==0.15.0
|
||||
python-editor==1.0.4
|
||||
pytoolconfig==1.2.5
|
||||
pytz==2022.7.1
|
||||
PyYAML==5.4.1
|
||||
qrcode==6.1
|
||||
redis==4.5.1
|
||||
regex==2020.11.13
|
||||
redis==4.5.4
|
||||
regex==2023.3.23
|
||||
requests==2.25.0
|
||||
requests-mock==1.9.3
|
||||
requests-mock==1.10.0
|
||||
requests-oauthlib==1.3.0
|
||||
rope==0.18.0
|
||||
rope==1.7.0
|
||||
six==1.15.0
|
||||
soupsieve==2.1
|
||||
speaklater==1.3
|
||||
SQLAlchemy==1.3.20
|
||||
SQLAlchemy-Utils==0.36.8
|
||||
swagger-spec-validator==2.7.3
|
||||
TatSu==4.4.0
|
||||
SQLAlchemy==2.0.9
|
||||
SQLAlchemy-Utils==0.41.0
|
||||
structlog==23.1.0
|
||||
swagger-spec-validator==3.0.3
|
||||
tabulate==0.9.0
|
||||
TatSu==5.8.3
|
||||
toml==0.10.2
|
||||
tomli==2.0.1
|
||||
typed-ast==1.5.4
|
||||
typing-extensions==4.5.0
|
||||
typing_extensions==4.5.0
|
||||
urllib3==1.26.5
|
||||
URLObject==2.4.3
|
||||
validators==0.18.2
|
||||
validators==0.20.0
|
||||
vine==5.0.0
|
||||
virtualenv==20.2.2
|
||||
virtualenv==20.21.0
|
||||
visitor==0.1.3
|
||||
wcwidth==0.2.6
|
||||
webargs==7.0.1
|
||||
Werkzeug==1.0.1
|
||||
WTForms==2.3.3
|
||||
WTForms-SQLAlchemy==0.2
|
||||
Werkzeug==2.2.3
|
||||
wimpy==0.6
|
||||
WTForms==3.0.1
|
||||
WTForms-SQLAlchemy==0.3
|
||||
yarg==0.1.9
|
||||
zipp==3.4.0
|
||||
|
||||
@ -12,7 +12,7 @@ def pytest_generate_tests(metafunc):
|
||||
warnings.filterwarnings("error", category=SAWarning)
|
||||
|
||||
os.environ["DATABASE_URL"] = os.environ.get(
|
||||
"TEST_DATABASE_URL", "postgresql://postgres@localhost/eventcally_tests"
|
||||
"TEST_DATABASE_URL", "postgresql://user:pass@myserver/ec_tests"
|
||||
)
|
||||
os.environ["REDIS_URL"] = os.environ.get("TEST_REDIS_URL", "redis://")
|
||||
os.environ["AUTHLIB_INSECURE_TRANSPORT"] = "1"
|
||||
|
||||
@ -305,14 +305,19 @@ class Seeder(object):
|
||||
def get_event_category_id(self, category_name):
|
||||
from project.services.event import get_event_category
|
||||
|
||||
category = get_event_category(category_name)
|
||||
with self._app.app_context():
|
||||
category = get_event_category(category_name)
|
||||
|
||||
return category.id
|
||||
|
||||
def get_event_date_id(self, event_id):
|
||||
from project.models import Event
|
||||
|
||||
event = Event.query.get(event_id)
|
||||
return event.dates[0].id
|
||||
with self._app.app_context():
|
||||
event = Event.query.get(event_id)
|
||||
event_date_id = event.dates[0].id
|
||||
|
||||
return event_date_id
|
||||
|
||||
def create_event(
|
||||
self,
|
||||
|
||||
@ -13,9 +13,11 @@ def test_mail_server():
|
||||
|
||||
|
||||
def drop_db(db):
|
||||
db.drop_all()
|
||||
db.engine.execute("DROP TABLE IF EXISTS alembic_version;")
|
||||
db.engine.execute("DROP TABLE IF EXISTS analytics;")
|
||||
with db.engine.connect() as conn:
|
||||
with conn.begin():
|
||||
db.drop_all()
|
||||
conn.execute(sqlalchemy.text("DROP TABLE IF EXISTS alembic_version;"))
|
||||
conn.execute(sqlalchemy.text("DROP TABLE IF EXISTS analytics;"))
|
||||
|
||||
|
||||
def populate_db(db):
|
||||
@ -33,7 +35,9 @@ BEGIN
|
||||
INSERT INTO event (name, admin_unit_id, event_place_id, organizer_id, start) VALUES ('Event', admin_unit_id, event_place_id, organizer_id, current_timestamp) RETURNING id INTO event_id;
|
||||
END $$;
|
||||
"""
|
||||
db.engine.execute(sqlalchemy.text(sql).execution_options(autocommit=True))
|
||||
with db.engine.connect() as conn:
|
||||
with conn.begin():
|
||||
conn.execute(sqlalchemy.text(sql).execution_options(autocommit=True))
|
||||
|
||||
|
||||
def test_migrations(app, seeder):
|
||||
@ -54,6 +58,7 @@ def test_migrations(app, seeder):
|
||||
seeder.create_event_suggestion(admin_unit_id)
|
||||
seeder.create_any_reference(admin_unit_id)
|
||||
seeder.create_reference_request(event_id, admin_unit_id)
|
||||
db.session.commit()
|
||||
downgrade()
|
||||
|
||||
|
||||
|
||||
@ -110,14 +110,15 @@ def test_event_has_multiple_dates(client, app, db, seeder):
|
||||
assert event_without_recc.has_multiple_dates() is False
|
||||
|
||||
|
||||
def test_oauth2_token(client, app):
|
||||
def test_oauth2_token(client, app, seeder):
|
||||
import time
|
||||
|
||||
from project.models import OAuth2Token
|
||||
|
||||
token = OAuth2Token()
|
||||
token.revoked = True
|
||||
token.access_token_revoked_at = int(time.time())
|
||||
assert not token.is_refresh_token_active()
|
||||
|
||||
token.revoked = False
|
||||
token.issued_at = 0
|
||||
token.expires_in = 0
|
||||
assert not token.is_refresh_token_active()
|
||||
|
||||
@ -64,28 +64,28 @@ class UtilActions(object):
|
||||
response = self._client.get("/login")
|
||||
assert response.status_code == 200
|
||||
|
||||
with self._client:
|
||||
response = self._client.post(
|
||||
"/login",
|
||||
data={
|
||||
"email": email,
|
||||
"password": password,
|
||||
"csrf_token": self.get_csrf(response),
|
||||
"submit": "Anmelden",
|
||||
},
|
||||
follow_redirects=follow_redirects,
|
||||
)
|
||||
|
||||
if follow_redirects:
|
||||
assert response.status_code == 200
|
||||
else:
|
||||
assert response.status_code == 302
|
||||
|
||||
assert g.identity.user.email == email
|
||||
|
||||
with self._app.app_context():
|
||||
user = find_user_by_email(email)
|
||||
user_id = user.id
|
||||
with self._client:
|
||||
response = self._client.post(
|
||||
"/login",
|
||||
data={
|
||||
"email": email,
|
||||
"password": password,
|
||||
"csrf_token": self.get_csrf(response),
|
||||
"submit": "Anmelden",
|
||||
},
|
||||
follow_redirects=follow_redirects,
|
||||
)
|
||||
|
||||
if follow_redirects:
|
||||
assert response.status_code == 200
|
||||
else:
|
||||
assert response.status_code == 302
|
||||
|
||||
assert g.identity.user.email == email
|
||||
|
||||
user = find_user_by_email(email)
|
||||
user_id = user.id
|
||||
|
||||
return user_id
|
||||
|
||||
@ -300,8 +300,10 @@ class UtilActions(object):
|
||||
def assert_response_redirect(self, response, endpoint, **values):
|
||||
assert response.status_code == 302
|
||||
|
||||
redirect_url = "http://localhost" + self.get_url(endpoint, **values)
|
||||
assert response.headers["Location"] == redirect_url
|
||||
response_location = response.headers["Location"]
|
||||
redirect_url = self.get_url(endpoint, **values)
|
||||
absolute_url = "http://localhost" + redirect_url
|
||||
assert response_location == redirect_url or response_location == absolute_url
|
||||
|
||||
def assert_response_contains_alert(self, response, category, message=None):
|
||||
assert response.status_code == 200
|
||||
|
||||
@ -155,8 +155,7 @@ def test_read_decline(client, app, db, utils, seeder):
|
||||
"decline": "Ablehnen",
|
||||
},
|
||||
)
|
||||
assert response.status_code == 302
|
||||
assert response.headers["Location"] == "http://localhost/manage"
|
||||
utils.assert_response_redirect(response, "manage")
|
||||
|
||||
with app.app_context():
|
||||
from project.services.admin_unit import (
|
||||
@ -211,8 +210,7 @@ def test_read_new_member_not_registered(client, app, utils, seeder):
|
||||
|
||||
url = "/invitations/%d" % invitation_id
|
||||
response = client.get(url)
|
||||
assert response.status_code == 302
|
||||
assert response.headers["Location"] == "http://localhost/register"
|
||||
utils.assert_response_redirect(response, "security.register")
|
||||
|
||||
|
||||
def test_read_new_member_not_authenticated(client, app, utils, seeder):
|
||||
@ -226,8 +224,7 @@ def test_read_new_member_not_authenticated(client, app, utils, seeder):
|
||||
|
||||
url = "/invitations/%d" % invitation_id
|
||||
response = client.get(url)
|
||||
assert response.status_code == 302
|
||||
assert response.headers["Location"].startswith("http://localhost/login")
|
||||
utils.assert_response_redirect(response, "security.login", next=url)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("user_exists", [True, False])
|
||||
@ -245,9 +242,8 @@ def test_read_currentUserDoesNotMatchInvitationEmail(
|
||||
seeder.create_user(email)
|
||||
|
||||
url = "/invitations/%d" % invitation_id
|
||||
environ, response = client.get(url, follow_redirects=True, as_tuple=True)
|
||||
response = client.get(url, follow_redirects=True)
|
||||
|
||||
assert environ["REQUEST_URI"] == "/profile"
|
||||
utils.assert_response_ok(response)
|
||||
utils.assert_response_contains(
|
||||
response, "Die Einladung wurde für einen anderen Nutzer ausgestellt."
|
||||
|
||||
@ -42,7 +42,7 @@ def test_revoke(client, seeder, utils, app, mocker, db_error):
|
||||
from project.models import OAuth2Token
|
||||
|
||||
oauth2_token = OAuth2Token.query.get(oauth2_token_id)
|
||||
assert oauth2_token.revoked
|
||||
assert oauth2_token.is_revoked() > 0
|
||||
|
||||
# Kann nicht zweimal revoked werden
|
||||
response = utils.get(url)
|
||||
|
||||
@ -25,8 +25,7 @@ def test_organization_invitation_not_authenticated(client, app, utils, seeder):
|
||||
seeder.create_user("invited@test.de")
|
||||
url = utils.get_url("user_organization_invitation", id=invitation_id)
|
||||
response = client.get(url)
|
||||
assert response.status_code == 302
|
||||
assert response.headers["Location"].startswith("http://localhost/login")
|
||||
utils.assert_response_redirect(response, "security.login", next=url)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("user_exists", [True, False])
|
||||
@ -40,9 +39,8 @@ def test_organization_invitation_currentUserDoesNotMatchInvitationEmail(
|
||||
seeder.create_user("invited@test.de")
|
||||
|
||||
url = utils.get_url("user_organization_invitation", id=invitation_id)
|
||||
environ, response = client.get(url, follow_redirects=True, as_tuple=True)
|
||||
response = client.get(url, follow_redirects=True)
|
||||
|
||||
assert environ["REQUEST_URI"] == "/profile"
|
||||
utils.assert_response_ok(response)
|
||||
utils.assert_response_contains(
|
||||
response, "Die Einladung wurde für einen anderen Nutzer ausgestellt."
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user