diff --git a/.vscode/settings.json b/.vscode/settings.json
index 7f35355..bf81191 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -3,5 +3,11 @@
"python.formatting.provider": "black",
"python.linting.enabled": true,
"python.linting.pylintEnabled": false,
- "python.linting.flake8Enabled": true
+ "python.linting.flake8Enabled": true,
+ "python.testing.pytestArgs": [
+ "tests"
+ ],
+ "python.testing.unittestEnabled": false,
+ "python.testing.nosetestsEnabled": false,
+ "python.testing.pytestEnabled": true
}
\ No newline at end of file
diff --git a/project/__init__.py b/project/__init__.py
index 6f4026f..faa803c 100644
--- a/project/__init__.py
+++ b/project/__init__.py
@@ -51,7 +51,7 @@ mail_server = os.getenv("MAIL_SERVER")
if mail_server is None:
app.config["MAIL_SUPPRESS_SEND"] = True
app.config["MAIL_DEFAULT_SENDER"] = "test@oveda.de"
-else:
+else: # pragma: no cover
app.config["MAIL_SUPPRESS_SEND"] = False
app.config["MAIL_SERVER"] = mail_server
app.config["MAIL_PORT"] = os.getenv("MAIL_PORT")
@@ -120,5 +120,5 @@ from project.views import (
widget,
)
-if __name__ == "__main__":
+if __name__ == "__main__": # pragma: no cover
app.run()
diff --git a/project/access.py b/project/access.py
index b1f6fce..1ae3c21 100644
--- a/project/access.py
+++ b/project/access.py
@@ -3,6 +3,7 @@ from flask_security import current_user
from flask_security.utils import FsPermNeed
from flask_principal import Permission
from project.models import AdminUnitMember, AdminUnit
+from project.services.admin_unit import get_member_for_admin_unit_by_user_id
def has_current_user_permission(permission):
@@ -10,6 +11,13 @@ def has_current_user_permission(permission):
return user_perm.can()
+def has_admin_unit_member_role(admin_unit_member, role_name):
+ for role in admin_unit_member.roles:
+ if role.name == role_name:
+ return True
+ return False
+
+
def has_admin_unit_member_permission(admin_unit_member, permission):
for role in admin_unit_member.roles:
if permission in role.get_permissions():
@@ -17,16 +25,26 @@ def has_admin_unit_member_permission(admin_unit_member, permission):
return False
+def get_current_user_member_for_admin_unit(admin_unit_id):
+ return get_member_for_admin_unit_by_user_id(admin_unit_id, current_user.id)
+
+
def has_current_user_member_permission_for_admin_unit(admin_unit_id, permission):
- admin_unit_member = AdminUnitMember.query.filter_by(
- admin_unit_id=admin_unit_id, user_id=current_user.id
- ).first()
+ admin_unit_member = get_current_user_member_for_admin_unit(admin_unit_id)
if admin_unit_member is not None:
if has_admin_unit_member_permission(admin_unit_member, permission):
return True
return False
+def has_current_user_member_role_for_admin_unit(admin_unit_id, role_name):
+ admin_unit_member = get_current_user_member_for_admin_unit(admin_unit_id)
+ if admin_unit_member is not None:
+ if has_admin_unit_member_role(admin_unit_member, role_name):
+ return True
+ return False
+
+
def has_current_user_permission_for_admin_unit(admin_unit, permission):
if not current_user.is_authenticated:
return False
@@ -49,44 +67,10 @@ def access_or_401(admin_unit, permission):
abort(401)
-def can_list_admin_unit_members(admin_unit):
- return has_current_user_permission_for_admin_unit(
- admin_unit, "admin_unit.members:read"
- )
-
-
-def can_create_event(admin_unit):
- return has_current_user_permission_for_admin_unit(admin_unit, "event:create")
-
-
-def can_update_event(event):
- return has_current_user_permission_for_admin_unit(event.admin_unit, "event:update")
-
-
-def can_delete_event(event):
- return has_current_user_permission_for_admin_unit(event.admin_unit, "event:delete")
-
-
def can_reference_event(event):
return len(get_admin_units_for_event_reference(event)) > 0
-def can_update_organizer(organizer):
- return get_admin_unit_for_manage(organizer.admin_unit_id) is not None
-
-
-def can_create_admin_unit():
- return current_user.is_authenticated
-
-
-def can_verify_event_for_admin_unit(admin_unit):
- return has_current_user_permission_for_admin_unit(admin_unit, "event:verify")
-
-
-def can_verify_event(event):
- return can_verify_event_for_admin_unit(event.admin_unit)
-
-
def get_admin_units_with_current_user_permission(permission):
result = list()
diff --git a/project/dateutils.py b/project/dateutils.py
index 58d6013..6baf2f3 100644
--- a/project/dateutils.py
+++ b/project/dateutils.py
@@ -35,7 +35,7 @@ def form_input_to_date(date_str, hour=0, minute=0, second=0):
def form_input_from_date(date):
- return date.strftime("%Y-%m-%d")
+ return date.strftime("%Y-%m-%d") if date else ""
def dates_from_recurrence_rule(start, recurrence_rule):
diff --git a/project/forms/planing.py b/project/forms/planing.py
index 01197a9..c40821c 100644
--- a/project/forms/planing.py
+++ b/project/forms/planing.py
@@ -1,4 +1,3 @@
-from flask import request
from flask_babelex import lazy_gettext
from flask_wtf import FlaskForm
from wtforms import HiddenField, StringField, SubmitField, SelectField
@@ -32,6 +31,3 @@ class PlaningForm(FlaskForm):
)
submit = SubmitField(lazy_gettext("Find"))
-
- def is_submitted(self):
- return "submit" in request.args
diff --git a/project/forms/widgets.py b/project/forms/widgets.py
index c43295d..91ea8a5 100644
--- a/project/forms/widgets.py
+++ b/project/forms/widgets.py
@@ -1,5 +1,6 @@
from wtforms import DateTimeField, SelectMultipleField, SelectField
-from wtforms.widgets import html_params, HTMLString, ListWidget, CheckboxInput
+from wtforms.widgets import html_params, ListWidget, CheckboxInput
+from markupsafe import Markup
from wtforms.validators import StopValidation
from datetime import datetime
from flask_babelex import to_user_timezone, gettext
@@ -37,7 +38,7 @@ class CustomDateTimeWidget:
time_minute_params = html_params(name=field.name, id=id + "-minute", **kwargs)
clear_button_id = id + "-clear-button"
- return HTMLString(
+ return Markup(
'
:
'.format(
date_params,
clear_button_id,
@@ -80,7 +81,7 @@ class CustomDateWidget:
date = date_value.strftime("%Y-%m-%d")
date_params = html_params(name=field.name, id=id, value=date, **kwargs)
- return HTMLString(''.format(date_params))
+ return Markup(''.format(date_params))
class CustomDateField(DateTimeField):
diff --git a/project/init_data.py b/project/init_data.py
index dc16803..2633da0 100644
--- a/project/init_data.py
+++ b/project/init_data.py
@@ -1,6 +1,7 @@
from project import app, db
-from project.services.user import upsert_user_role, add_roles_to_user
+from project.services.user import upsert_user_role, add_admin_roles_to_user
from project.services.admin_unit import upsert_admin_unit_member_role
+from project.services.event import upsert_event_category
from project.models import Location
@@ -41,8 +42,27 @@ def create_initial_data():
upsert_user_role("admin", "Administrator", admin_permissions)
upsert_user_role("event_verifier", "Event expert", event_permissions)
- add_roles_to_user("grams.daniel@gmail.com", ["admin", "event_verifier"])
+ add_admin_roles_to_user("grams.daniel@gmail.com")
Location.update_coordinates()
+ upsert_event_category("Art")
+ upsert_event_category("Book")
+ upsert_event_category("Movie")
+ upsert_event_category("Family")
+ upsert_event_category("Festival")
+ upsert_event_category("Religious")
+ upsert_event_category("Shopping")
+ upsert_event_category("Comedy")
+ upsert_event_category("Music")
+ upsert_event_category("Dance")
+ upsert_event_category("Nightlife")
+ upsert_event_category("Theater")
+ upsert_event_category("Dining")
+ upsert_event_category("Conference")
+ upsert_event_category("Meetup")
+ upsert_event_category("Fitness")
+ upsert_event_category("Sports")
+ upsert_event_category("Other")
+
db.session.commit()
diff --git a/project/jsonld.py b/project/jsonld.py
index 6fb6be5..99bc077 100644
--- a/project/jsonld.py
+++ b/project/jsonld.py
@@ -1,5 +1,4 @@
import datetime
-import decimal
from json import JSONEncoder
from flask import url_for
from project.models import EventAttendanceMode, EventStatus
@@ -13,8 +12,6 @@ class DateTimeEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, (datetime.date, datetime.datetime)):
return (obj.astimezone(berlin_tz)).isoformat()
- if isinstance(obj, decimal.Decimal):
- return float(obj)
def get_sd_for_admin_unit(admin_unit):
@@ -29,7 +26,7 @@ def get_sd_for_admin_unit(admin_unit):
return result
-def get_sd_for_organizer_organization(organizer):
+def get_sd_for_organizer(organizer):
result = {}
result["@type"] = "Organization"
result["name"] = organizer.name
@@ -49,10 +46,6 @@ def get_sd_for_organizer_organization(organizer):
return result
-def get_sd_for_organizer(organizer):
- return get_sd_for_organizer_organization(organizer)
-
-
def get_sd_for_location(location):
result = {}
result["@type"] = "PostalAddress"
@@ -84,7 +77,7 @@ def get_sd_for_place(place, use_ref=True):
if place.location:
result["address"] = get_sd_for_location(place.location)
- if place.location.latitude != 0:
+ if place.location.coordinate:
result["geo"] = get_sd_for_geo(place.location)
if place.photo_id:
@@ -134,7 +127,7 @@ def get_sd_for_event_date(event_date):
result["previousStartDate"] = event.previous_start_date
if event.accessible_for_free:
- result["accessible_for_free"] = event.accessible_for_free
+ result["isAccessibleForFree"] = event.accessible_for_free
if event.age_from and event.age_to:
result["typicalAgeRange"] = "%d-%d" % (event.age_from, event.age_to)
diff --git a/project/models.py b/project/models.py
index ed13717..ca52846 100644
--- a/project/models.py
+++ b/project/models.py
@@ -217,7 +217,7 @@ class Location(db.Model, TrackableMixin):
)
).all()
- for location in locations:
+ for location in locations: # pragma: no cover
location.update_coordinate()
db.session.commit()
@@ -242,9 +242,6 @@ class EventPlace(db.Model, TrackableMixin):
description = Column(UnicodeText())
admin_unit_id = db.Column(db.Integer, db.ForeignKey("adminunit.id"), nullable=True)
- def is_empty(self):
- return not self.name
-
@listens_for(EventPlace, "before_insert")
@listens_for(EventPlace, "before_update")
@@ -321,9 +318,6 @@ class EventOrganizer(db.Model, TrackableMixin):
logo = db.relationship("Image", uselist=False)
admin_unit_id = db.Column(db.Integer, db.ForeignKey("adminunit.id"), nullable=True)
- def is_empty(self):
- return not self.name
-
@listens_for(EventOrganizer, "before_insert")
@listens_for(EventOrganizer, "before_update")
@@ -412,12 +406,8 @@ class EventSuggestion(db.Model, TrackableMixin):
@listens_for(EventSuggestion, "before_insert")
@listens_for(EventSuggestion, "before_update")
def purge_event_suggestion(mapper, connect, self):
- if self.organizer and self.organizer.is_empty():
- self.organizer_id = None
if self.organizer_id is not None:
self.organizer_text = None
- if self.event_place and self.event_place.is_empty():
- self.event_place_id = None
if self.event_place_id is not None:
self.event_place_text = None
if self.photo and self.photo.is_empty():
@@ -490,10 +480,6 @@ class Event(db.Model, TrackableMixin):
@listens_for(Event, "before_insert")
@listens_for(Event, "before_update")
def purge_event(mapper, connect, self):
- if self.organizer and self.organizer.is_empty():
- self.organizer_id = None
- if self.event_place and self.event_place.is_empty():
- self.event_place_id = None
if self.photo and self.photo.is_empty():
self.photo_id = None
diff --git a/project/oauth.py b/project/oauth.py
index a5377dd..e12fc59 100644
--- a/project/oauth.py
+++ b/project/oauth.py
@@ -15,7 +15,7 @@ blueprint = make_google_blueprint(
# create/login local user on successful OAuth login
@oauth_authorized.connect_via(blueprint)
-def google_logged_in(blueprint, token):
+def google_logged_in(blueprint, token): # pragma: no cover
if not token:
flash("Failed to log in.", category="error")
return False
@@ -60,7 +60,7 @@ def google_logged_in(blueprint, token):
# notify on OAuth provider error
@oauth_error.connect_via(blueprint)
-def google_error(blueprint, message, response):
+def google_error(blueprint, message, response): # pragma: no cover
msg = "OAuth error from {name}! message={message} response={response}".format(
name=blueprint.name, message=message, response=response
)
diff --git a/project/scrape/form.py b/project/scrape/form.py
new file mode 100644
index 0000000..2f2d1b5
--- /dev/null
+++ b/project/scrape/form.py
@@ -0,0 +1,131 @@
+import urllib.parse
+from werkzeug.datastructures import MultiDict
+
+
+# see https://www.w3.org/TR/html52/sec-forms.html
+class Form:
+ def __init__(self, form):
+ assert form.name == "form" # feed me BeautifulSoup