mirror of
https://github.com/lucaspalomodevelop/eventcally.git
synced 2026-03-13 08:09:37 +00:00
Merge pull request #518 from eventcally/issue/517
Add trackable info to API #517
This commit is contained in:
commit
79f61b66eb
@ -35,7 +35,7 @@ from project.services.event import (
|
||||
get_significant_event_changes,
|
||||
update_event,
|
||||
)
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.services.search_params import EventSearchParams
|
||||
from project.views.event import (
|
||||
send_event_report_mails,
|
||||
send_referenced_event_changed_mails,
|
||||
@ -62,17 +62,20 @@ class EventListResource(BaseResource):
|
||||
@marshal_with(EventListResponseSchema)
|
||||
@require_api_access()
|
||||
def get(self, **kwargs):
|
||||
pagination = (
|
||||
Event.query.join(Event.admin_unit)
|
||||
.filter(
|
||||
and_(
|
||||
Event.public_status == PublicStatus.published,
|
||||
AdminUnit.is_verified,
|
||||
)
|
||||
params = EventSearchParams()
|
||||
params.load_from_request(**kwargs)
|
||||
|
||||
query = Event.query.join(Event.admin_unit).filter(
|
||||
and_(
|
||||
Event.public_status == PublicStatus.published,
|
||||
AdminUnit.is_verified,
|
||||
)
|
||||
.paginate()
|
||||
)
|
||||
return pagination
|
||||
query = params.get_trackable_query(query, Event)
|
||||
query = params.get_trackable_order_by(query, Event)
|
||||
query = query.order_by(Event.min_start)
|
||||
|
||||
return query.paginate()
|
||||
|
||||
|
||||
class EventResource(BaseResource):
|
||||
@ -173,7 +176,7 @@ class EventSearchResource(BaseResource):
|
||||
def get(self, **kwargs):
|
||||
login_api_user()
|
||||
params = EventSearchParams()
|
||||
params.load_from_request()
|
||||
params.load_from_request(**kwargs)
|
||||
pagination = get_events_query(params).paginate()
|
||||
return pagination
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@ from project.api.schemas import (
|
||||
PaginationRequestSchema,
|
||||
PaginationResponseSchema,
|
||||
SQLAlchemyBaseSchema,
|
||||
TrackableRequestSchemaMixin,
|
||||
TrackableSchemaMixin,
|
||||
WriteIdSchemaMixin,
|
||||
)
|
||||
@ -222,7 +223,10 @@ class EventRefSchema(EventIdSchema):
|
||||
|
||||
|
||||
class EventSearchItemSchema(
|
||||
EventRefSchema, EventCurrentUserMixin, EventCurrentOrganizationMixin
|
||||
EventRefSchema,
|
||||
EventCurrentUserMixin,
|
||||
EventCurrentOrganizationMixin,
|
||||
TrackableSchemaMixin,
|
||||
):
|
||||
description = marshmallow.auto_field()
|
||||
date_definitions = fields.List(fields.Nested(EventDateDefinitionSchema))
|
||||
@ -237,27 +241,53 @@ class EventSearchItemSchema(
|
||||
public_status = EnumField(PublicStatus)
|
||||
|
||||
|
||||
class EventListRequestSchema(PaginationRequestSchema):
|
||||
class EventListRequestSchema(PaginationRequestSchema, TrackableRequestSchemaMixin):
|
||||
sort = fields.Str(
|
||||
metadata={"description": "Sort result items."},
|
||||
validate=validate.OneOf(
|
||||
[
|
||||
"-created_at",
|
||||
"-updated_at",
|
||||
"-last_modified_at",
|
||||
"start",
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class EventListRefSchema(EventRefSchema, TrackableSchemaMixin):
|
||||
pass
|
||||
|
||||
|
||||
class EventListResponseSchema(PaginationResponseSchema):
|
||||
items = fields.List(
|
||||
fields.Nested(EventRefSchema), metadata={"description": "Events"}
|
||||
fields.Nested(EventListRefSchema), metadata={"description": "Events"}
|
||||
)
|
||||
|
||||
|
||||
class UserFavoriteEventListRequestSchema(PaginationRequestSchema):
|
||||
pass
|
||||
class UserFavoriteEventListRequestSchema(
|
||||
PaginationRequestSchema, TrackableRequestSchemaMixin
|
||||
):
|
||||
sort = fields.Str(
|
||||
metadata={"description": "Sort result items."},
|
||||
validate=validate.OneOf(
|
||||
[
|
||||
"-created_at",
|
||||
"-updated_at",
|
||||
"-last_modified_at",
|
||||
"start",
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class UserFavoriteEventListResponseSchema(PaginationResponseSchema):
|
||||
items = fields.List(
|
||||
fields.Nested(EventRefSchema), metadata={"description": "Events"}
|
||||
fields.Nested(EventListRefSchema), metadata={"description": "Events"}
|
||||
)
|
||||
|
||||
|
||||
class EventSearchRequestSchema(PaginationRequestSchema):
|
||||
class EventSearchRequestSchema(PaginationRequestSchema, TrackableRequestSchemaMixin):
|
||||
keyword = fields.Str(
|
||||
metadata={"description": "Looks for keyword in name, description and tags."},
|
||||
)
|
||||
@ -267,9 +297,7 @@ class EventSearchRequestSchema(PaginationRequestSchema):
|
||||
},
|
||||
)
|
||||
date_to = fields.Date(
|
||||
metadata={
|
||||
"description": "Looks for events at or before this date, e.g. 2020-12-31."
|
||||
},
|
||||
metadata={"description": "Looks for events before this date, e.g. 2020-12-31."},
|
||||
)
|
||||
coordinate = fields.Str(
|
||||
metadata={
|
||||
@ -294,11 +322,38 @@ class EventSearchRequestSchema(PaginationRequestSchema):
|
||||
)
|
||||
sort = fields.Str(
|
||||
metadata={"description": "Sort result items."},
|
||||
validate=validate.OneOf(
|
||||
[
|
||||
"-created_at",
|
||||
"-updated_at",
|
||||
"-last_modified_at",
|
||||
"-rating",
|
||||
"-reference_created_at",
|
||||
"start",
|
||||
]
|
||||
),
|
||||
)
|
||||
status = fields.List(
|
||||
EnumField(EventStatus),
|
||||
metadata={"description": "Looks for events with this stati."},
|
||||
)
|
||||
postal_code = fields.List(
|
||||
fields.Str(),
|
||||
metadata={"description": "Looks for events with places with this postal code."},
|
||||
)
|
||||
exclude_recurring = fields.Bool(
|
||||
metadata={"description": "Exclude recurring events"},
|
||||
)
|
||||
include_organization_references = fields.Bool(
|
||||
metadata={
|
||||
"description": "Include events referenced by organization. Only valid if there is an organization context."
|
||||
},
|
||||
)
|
||||
organization_references_only = fields.Bool(
|
||||
metadata={
|
||||
"description": "Only events referenced by organization. Only valid if there is an organization context."
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class EventSearchResponseSchema(PaginationResponseSchema):
|
||||
|
||||
@ -16,7 +16,7 @@ from project.api.event_date.schemas import (
|
||||
from project.api.resources import BaseResource, require_api_access
|
||||
from project.models import AdminUnit, Event, EventDate, PublicStatus
|
||||
from project.services.event import get_event_dates_query
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.services.search_params import EventSearchParams
|
||||
from project.views.utils import get_current_admin_unit_for_api
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ class EventDateListResource(BaseResource):
|
||||
@marshal_with(EventDateListResponseSchema)
|
||||
@require_api_access()
|
||||
def get(self, **kwargs):
|
||||
pagination = (
|
||||
query = (
|
||||
EventDate.query.join(EventDate.event)
|
||||
.join(Event.admin_unit)
|
||||
.options(lazyload(EventDate.event))
|
||||
@ -36,9 +36,14 @@ class EventDateListResource(BaseResource):
|
||||
AdminUnit.is_verified,
|
||||
)
|
||||
)
|
||||
.paginate()
|
||||
)
|
||||
return pagination
|
||||
|
||||
params = EventSearchParams()
|
||||
params.load_from_request(**kwargs)
|
||||
query = params.get_trackable_query(query, Event)
|
||||
query = params.get_trackable_order_by(query, Event)
|
||||
query = query.order_by(EventDate.start)
|
||||
return query.paginate()
|
||||
|
||||
|
||||
class EventDateResource(BaseResource):
|
||||
@ -63,9 +68,9 @@ class EventDateSearchResource(BaseResource):
|
||||
def get(self, **kwargs):
|
||||
login_api_user()
|
||||
params = EventSearchParams()
|
||||
params.load_from_request()
|
||||
params.can_read_planned_events = can_use_planning()
|
||||
params.include_admin_unit_references = True
|
||||
params.load_from_request(**kwargs)
|
||||
params.can_read_planned_events = can_use_planning()
|
||||
|
||||
if "not_referenced" in request.args:
|
||||
admin_unit = get_current_admin_unit_for_api()
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from marshmallow import fields
|
||||
from marshmallow import fields, validate
|
||||
|
||||
from project.api import marshmallow
|
||||
from project.api.event.schemas import (
|
||||
@ -7,7 +7,12 @@ from project.api.event.schemas import (
|
||||
EventSearchRequestSchema,
|
||||
)
|
||||
from project.api.fields import CustomDateTimeField
|
||||
from project.api.schemas import PaginationRequestSchema, PaginationResponseSchema
|
||||
from project.api.schemas import (
|
||||
PaginationRequestSchema,
|
||||
PaginationResponseSchema,
|
||||
TrackableRequestSchemaMixin,
|
||||
TrackableSchemaMixin,
|
||||
)
|
||||
from project.models import EventDate
|
||||
|
||||
|
||||
@ -32,13 +37,27 @@ class EventDateRefSchema(marshmallow.SQLAlchemySchema):
|
||||
start = CustomDateTimeField()
|
||||
|
||||
|
||||
class EventDateListRequestSchema(PaginationRequestSchema):
|
||||
class EventDateListRequestSchema(PaginationRequestSchema, TrackableRequestSchemaMixin):
|
||||
sort = fields.Str(
|
||||
metadata={"description": "Sort result items."},
|
||||
validate=validate.OneOf(
|
||||
[
|
||||
"-created_at",
|
||||
"-updated_at",
|
||||
"-last_modified_at",
|
||||
"start",
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class EventDateListRefSchema(EventDateRefSchema, TrackableSchemaMixin):
|
||||
pass
|
||||
|
||||
|
||||
class EventDateListResponseSchema(PaginationResponseSchema):
|
||||
items = fields.List(
|
||||
fields.Nested(EventDateRefSchema), metadata={"description": "Dates"}
|
||||
fields.Nested(EventDateListRefSchema), metadata={"description": "Dates"}
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ from project.api.event_list.schemas import (
|
||||
from project.api.resources import BaseResource, require_api_access
|
||||
from project.models import Event, EventList
|
||||
from project.services.event import get_events_query
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.services.search_params import EventSearchParams
|
||||
|
||||
|
||||
class EventListModelResource(BaseResource):
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from marshmallow import fields
|
||||
from marshmallow import fields, validate
|
||||
|
||||
from project.api import marshmallow
|
||||
from project.api.event.schemas import EventRefSchema, EventWriteIdSchema
|
||||
@ -8,6 +8,8 @@ from project.api.schemas import (
|
||||
PaginationRequestSchema,
|
||||
PaginationResponseSchema,
|
||||
SQLAlchemyBaseSchema,
|
||||
TrackableRequestSchemaMixin,
|
||||
TrackableSchemaMixin,
|
||||
)
|
||||
from project.models import EventReference
|
||||
|
||||
@ -22,7 +24,7 @@ class EventReferenceIdSchema(EventReferenceModelSchema, IdSchemaMixin):
|
||||
pass
|
||||
|
||||
|
||||
class EventReferenceRefSchema(EventReferenceIdSchema):
|
||||
class EventReferenceRefSchema(EventReferenceIdSchema, TrackableSchemaMixin):
|
||||
event = fields.Nested(EventRefSchema)
|
||||
|
||||
|
||||
@ -36,8 +38,13 @@ class EventReferenceDumpSchema(EventReferenceIdSchema):
|
||||
organization_id = fields.Int(attribute="admin_unit_id")
|
||||
|
||||
|
||||
class EventReferenceListRequestSchema(PaginationRequestSchema):
|
||||
pass
|
||||
class EventReferenceListRequestSchema(
|
||||
PaginationRequestSchema, TrackableRequestSchemaMixin
|
||||
):
|
||||
sort = fields.Str(
|
||||
metadata={"description": "Sort result items."},
|
||||
validate=validate.OneOf(["-created_at", "-updated_at", "-last_modified_at"]),
|
||||
)
|
||||
|
||||
|
||||
class EventReferenceListResponseSchema(PaginationResponseSchema):
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from marshmallow import ValidationError, fields
|
||||
from marshmallow_sqlalchemy import fields as msfields
|
||||
|
||||
from project.dateutils import berlin_tz
|
||||
from project.dateutils import berlin_tz, gmt_tz
|
||||
|
||||
|
||||
class NumericStr(fields.String):
|
||||
@ -34,6 +34,22 @@ class CustomDateTimeField(fields.DateTime):
|
||||
return result
|
||||
|
||||
|
||||
class GmtDateTimeField(fields.DateTime):
|
||||
def _serialize(self, value, attr, obj, **kwargs):
|
||||
if value:
|
||||
value = value.replace(tzinfo=gmt_tz)
|
||||
|
||||
return super()._serialize(value, attr, obj, **kwargs)
|
||||
|
||||
def deserialize(self, value, attr, data, **kwargs):
|
||||
result = super().deserialize(value, attr, data, **kwargs)
|
||||
|
||||
if result and result.tzinfo is None:
|
||||
result = gmt_tz.localize(result)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class Owned(msfields.Nested):
|
||||
def _deserialize(self, *args, **kwargs):
|
||||
if (
|
||||
|
||||
@ -77,7 +77,7 @@ from project.api.place.schemas import (
|
||||
)
|
||||
from project.api.resources import BaseResource, require_api_access
|
||||
from project.models import AdminUnit, Event, PublicStatus
|
||||
from project.models.admin_unit import AdminUnitRelation
|
||||
from project.models.admin_unit import AdminUnitInvitation, AdminUnitRelation
|
||||
from project.services.admin_unit import (
|
||||
get_admin_unit_invitation_query,
|
||||
get_admin_unit_query,
|
||||
@ -88,13 +88,20 @@ from project.services.admin_unit import (
|
||||
get_place_query,
|
||||
)
|
||||
from project.services.event import get_event_dates_query, get_events_query, insert_event
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.services.importer.event_importer import EventImporter
|
||||
from project.services.reference import (
|
||||
get_reference_incoming_query,
|
||||
get_reference_outgoing_query,
|
||||
get_relation_outgoing_query,
|
||||
)
|
||||
from project.services.search_params import (
|
||||
AdminUnitSearchParams,
|
||||
EventPlaceSearchParams,
|
||||
EventReferenceSearchParams,
|
||||
EventSearchParams,
|
||||
OrganizerSearchParams,
|
||||
TrackableSearchParams,
|
||||
)
|
||||
from project.views.utils import get_current_admin_unit_for_api, send_mail_async
|
||||
|
||||
|
||||
@ -119,10 +126,10 @@ class OrganizationEventDateSearchResource(BaseResource):
|
||||
admin_unit = AdminUnit.query.get_or_404(id)
|
||||
|
||||
params = EventSearchParams()
|
||||
params.load_from_request()
|
||||
params.include_admin_unit_references = True
|
||||
params.load_from_request(**kwargs)
|
||||
params.admin_unit_id = admin_unit.id
|
||||
params.can_read_private_events = api_can_read_private_events(admin_unit)
|
||||
params.include_admin_unit_references = True
|
||||
|
||||
pagination = get_event_dates_query(params).paginate()
|
||||
return pagination
|
||||
@ -137,7 +144,7 @@ class OrganizationEventSearchResource(BaseResource):
|
||||
admin_unit = AdminUnit.query.get_or_404(id)
|
||||
|
||||
params = EventSearchParams()
|
||||
params.load_from_request()
|
||||
params.load_from_request(**kwargs)
|
||||
params.admin_unit_id = admin_unit.id
|
||||
params.can_read_private_events = api_can_read_private_events(admin_unit)
|
||||
|
||||
@ -151,8 +158,10 @@ class OrganizationEventListResource(BaseResource):
|
||||
@marshal_with(EventListResponseSchema)
|
||||
@require_api_access()
|
||||
def get(self, id, **kwargs):
|
||||
admin_unit = AdminUnit.query.get_or_404(id)
|
||||
params = EventSearchParams()
|
||||
params.load_from_request(**kwargs)
|
||||
|
||||
admin_unit = AdminUnit.query.get_or_404(id)
|
||||
event_filter = Event.admin_unit_id == admin_unit.id
|
||||
|
||||
if not api_can_read_private_events(admin_unit):
|
||||
@ -162,8 +171,12 @@ class OrganizationEventListResource(BaseResource):
|
||||
AdminUnit.is_verified,
|
||||
)
|
||||
|
||||
pagination = Event.query.join(Event.admin_unit).filter(event_filter).paginate()
|
||||
return pagination
|
||||
query = Event.query.join(Event.admin_unit).filter(event_filter)
|
||||
query = params.get_trackable_query(query, Event)
|
||||
query = params.get_trackable_order_by(query, Event)
|
||||
query = query.order_by(Event.min_start)
|
||||
|
||||
return query.paginate()
|
||||
|
||||
@doc(
|
||||
summary="Add new event",
|
||||
@ -223,8 +236,6 @@ class OrganizationListResource(BaseResource):
|
||||
@marshal_with(OrganizationListResponseSchema)
|
||||
@require_api_access()
|
||||
def get(self, **kwargs):
|
||||
keyword = kwargs["keyword"] if "keyword" in kwargs else None
|
||||
|
||||
login_api_user()
|
||||
include_unverified = can_verify_admin_unit()
|
||||
reference_request_for_admin_unit_id = None
|
||||
@ -235,11 +246,11 @@ class OrganizationListResource(BaseResource):
|
||||
if admin_unit:
|
||||
reference_request_for_admin_unit_id = admin_unit.id
|
||||
|
||||
pagination = get_admin_unit_query(
|
||||
keyword,
|
||||
include_unverified,
|
||||
reference_request_for_admin_unit_id=reference_request_for_admin_unit_id,
|
||||
).paginate()
|
||||
params = AdminUnitSearchParams()
|
||||
params.load_from_request(**kwargs)
|
||||
params.include_unverified = include_unverified
|
||||
params.reference_request_for_admin_unit_id = reference_request_for_admin_unit_id
|
||||
pagination = get_admin_unit_query(params).paginate()
|
||||
return pagination
|
||||
|
||||
|
||||
@ -252,9 +263,11 @@ class OrganizationOrganizerListResource(BaseResource):
|
||||
@require_api_access()
|
||||
def get(self, id, **kwargs):
|
||||
admin_unit = AdminUnit.query.get_or_404(id)
|
||||
name = kwargs["name"] if "name" in kwargs else None
|
||||
|
||||
pagination = get_organizer_query(admin_unit.id, name).paginate()
|
||||
params = OrganizerSearchParams()
|
||||
params.load_from_request(**kwargs)
|
||||
params.admin_unit_id = admin_unit.id
|
||||
pagination = get_organizer_query(params).paginate()
|
||||
return pagination
|
||||
|
||||
@doc(
|
||||
@ -285,9 +298,11 @@ class OrganizationPlaceListResource(BaseResource):
|
||||
@require_api_access()
|
||||
def get(self, id, **kwargs):
|
||||
admin_unit = AdminUnit.query.get_or_404(id)
|
||||
name = kwargs["name"] if "name" in kwargs else None
|
||||
|
||||
pagination = get_place_query(admin_unit.id, name).paginate()
|
||||
params = EventPlaceSearchParams()
|
||||
params.load_from_request(**kwargs)
|
||||
params.admin_unit_id = admin_unit.id
|
||||
pagination = get_place_query(params).paginate()
|
||||
return pagination
|
||||
|
||||
@doc(
|
||||
@ -322,7 +337,10 @@ class OrganizationIncomingEventReferenceListResource(BaseResource):
|
||||
def get(self, id, **kwargs):
|
||||
admin_unit = AdminUnit.query.get_or_404(id)
|
||||
|
||||
pagination = get_reference_incoming_query(admin_unit).paginate()
|
||||
params = EventReferenceSearchParams()
|
||||
params.load_from_request(**kwargs)
|
||||
params.admin_unit_id = admin_unit.id
|
||||
pagination = get_reference_incoming_query(params).paginate()
|
||||
return pagination
|
||||
|
||||
@doc(
|
||||
@ -430,8 +448,13 @@ class OrganizationOrganizationInvitationListResource(BaseResource):
|
||||
admin_unit = get_admin_unit_for_manage_or_404(id)
|
||||
access_or_401(admin_unit, "admin_unit:update")
|
||||
|
||||
pagination = get_admin_unit_invitation_query(admin_unit).paginate()
|
||||
return pagination
|
||||
params = TrackableSearchParams()
|
||||
params.load_from_request(**kwargs)
|
||||
|
||||
query = get_admin_unit_invitation_query(admin_unit)
|
||||
query = params.get_trackable_query(query, AdminUnitInvitation)
|
||||
query = params.get_trackable_order_by(query, AdminUnitInvitation)
|
||||
return query.paginate()
|
||||
|
||||
@doc(
|
||||
summary="Add new organization invitation",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from marshmallow import fields, post_dump
|
||||
from marshmallow import fields, post_dump, validate
|
||||
|
||||
from project.access import has_access, login_api_user
|
||||
from project.api import marshmallow
|
||||
@ -9,6 +9,8 @@ from project.api.schemas import (
|
||||
PaginationRequestSchema,
|
||||
PaginationResponseSchema,
|
||||
SQLAlchemyBaseSchema,
|
||||
TrackableRequestSchemaMixin,
|
||||
TrackableSchemaMixin,
|
||||
WriteIdSchemaMixin,
|
||||
)
|
||||
from project.models import AdminUnit
|
||||
@ -66,15 +68,23 @@ class OrganizationRefSchema(OrganizationIdSchema):
|
||||
name = marshmallow.auto_field()
|
||||
|
||||
|
||||
class OrganizationListRefSchema(OrganizationRefSchema):
|
||||
class OrganizationListRefSchema(OrganizationRefSchema, TrackableSchemaMixin):
|
||||
short_name = marshmallow.auto_field()
|
||||
is_verified = fields.Boolean()
|
||||
|
||||
|
||||
class OrganizationListRequestSchema(PaginationRequestSchema):
|
||||
class OrganizationListRequestSchema(
|
||||
PaginationRequestSchema, TrackableRequestSchemaMixin
|
||||
):
|
||||
keyword = fields.Str(
|
||||
metadata={"description": "Looks for keyword in name and short name."},
|
||||
)
|
||||
sort = fields.Str(
|
||||
metadata={"description": "Sort result items."},
|
||||
validate=validate.OneOf(
|
||||
["-created_at", "-updated_at", "-last_modified_at", "name"]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class OrganizationListResponseSchema(PaginationResponseSchema):
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from marshmallow import fields
|
||||
from marshmallow import fields, validate
|
||||
|
||||
from project.api import marshmallow
|
||||
from project.api.organization.schemas import OrganizationRefSchema
|
||||
@ -7,6 +7,7 @@ from project.api.schemas import (
|
||||
PaginationRequestSchema,
|
||||
PaginationResponseSchema,
|
||||
SQLAlchemyBaseSchema,
|
||||
TrackableRequestSchemaMixin,
|
||||
TrackableSchemaMixin,
|
||||
)
|
||||
from project.models import AdminUnitInvitation
|
||||
@ -40,13 +41,26 @@ class OrganizationInvitationRefSchema(OrganizationInvitationIdSchema):
|
||||
email = marshmallow.auto_field()
|
||||
|
||||
|
||||
class OrganizationInvitationListRequestSchema(PaginationRequestSchema):
|
||||
class OrganizationInvitationListRequestSchema(
|
||||
PaginationRequestSchema, TrackableRequestSchemaMixin
|
||||
):
|
||||
sort = fields.Str(
|
||||
metadata={"description": "Sort result items."},
|
||||
validate=validate.OneOf(
|
||||
["-created_at", "-updated_at", "-last_modified_at", "name"]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class OrganizationInvitationListRefSchema(
|
||||
OrganizationInvitationRefSchema, TrackableSchemaMixin
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class OrganizationInvitationListResponseSchema(PaginationResponseSchema):
|
||||
items = fields.List(
|
||||
fields.Nested(OrganizationInvitationRefSchema),
|
||||
fields.Nested(OrganizationInvitationListRefSchema),
|
||||
metadata={"description": "Organization invitations"},
|
||||
)
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ from project.api.schemas import (
|
||||
PaginationRequestSchema,
|
||||
PaginationResponseSchema,
|
||||
SQLAlchemyBaseSchema,
|
||||
TrackableRequestSchemaMixin,
|
||||
TrackableSchemaMixin,
|
||||
WriteIdSchemaMixin,
|
||||
)
|
||||
@ -67,15 +68,25 @@ class OrganizerRefSchema(OrganizerIdSchema):
|
||||
name = marshmallow.auto_field()
|
||||
|
||||
|
||||
class OrganizerListRequestSchema(PaginationRequestSchema):
|
||||
class OrganizerListRequestSchema(PaginationRequestSchema, TrackableRequestSchemaMixin):
|
||||
name = fields.Str(
|
||||
metadata={"description": "Looks for name."},
|
||||
)
|
||||
sort = fields.Str(
|
||||
metadata={"description": "Sort result items."},
|
||||
validate=validate.OneOf(
|
||||
["-created_at", "-updated_at", "-last_modified_at", "name"]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class OrganizerListRefSchema(OrganizerRefSchema, TrackableSchemaMixin):
|
||||
pass
|
||||
|
||||
|
||||
class OrganizerListResponseSchema(PaginationResponseSchema):
|
||||
items = fields.List(
|
||||
fields.Nested(OrganizerRefSchema), metadata={"description": "Organizers"}
|
||||
fields.Nested(OrganizerListRefSchema), metadata={"description": "Organizers"}
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ from project.api.schemas import (
|
||||
PaginationRequestSchema,
|
||||
PaginationResponseSchema,
|
||||
SQLAlchemyBaseSchema,
|
||||
TrackableRequestSchemaMixin,
|
||||
TrackableSchemaMixin,
|
||||
WriteIdSchemaMixin,
|
||||
)
|
||||
@ -64,15 +65,25 @@ class PlaceSearchItemSchema(PlaceRefSchema):
|
||||
location = fields.Nested(LocationSearchItemSchema)
|
||||
|
||||
|
||||
class PlaceListRequestSchema(PaginationRequestSchema):
|
||||
class PlaceListRequestSchema(PaginationRequestSchema, TrackableRequestSchemaMixin):
|
||||
name = fields.Str(
|
||||
metadata={"description": "Looks for name."},
|
||||
)
|
||||
sort = fields.Str(
|
||||
metadata={"description": "Sort result items."},
|
||||
validate=validate.OneOf(
|
||||
["-created_at", "-updated_at", "-last_modified_at", "name"]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class PlaceListRefSchema(PlaceRefSchema, TrackableSchemaMixin):
|
||||
pass
|
||||
|
||||
|
||||
class PlaceListResponseSchema(PaginationResponseSchema):
|
||||
items = fields.List(
|
||||
fields.Nested(PlaceRefSchema), metadata={"description": "Places"}
|
||||
fields.Nested(PlaceListRefSchema), metadata={"description": "Places"}
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ from marshmallow import ValidationError, fields, missing, validate
|
||||
from marshmallow.decorators import pre_load
|
||||
|
||||
from project.api import marshmallow
|
||||
from project.api.fields import GmtDateTimeField
|
||||
|
||||
|
||||
class SQLAlchemyBaseSchema(marshmallow.SQLAlchemySchema):
|
||||
@ -39,8 +40,21 @@ class WriteIdSchemaMixin(object):
|
||||
|
||||
|
||||
class TrackableSchemaMixin(object):
|
||||
created_at = marshmallow.auto_field(dump_only=True)
|
||||
updated_at = marshmallow.auto_field(dump_only=True)
|
||||
created_at = GmtDateTimeField(dump_only=True)
|
||||
updated_at = GmtDateTimeField(dump_only=True)
|
||||
|
||||
|
||||
class TrackableRequestSchemaMixin(object):
|
||||
created_at_from = GmtDateTimeField(
|
||||
metadata={
|
||||
"description": "Items created at or after this date time in GTM, e.g. 2020-12-31T00:00:00."
|
||||
},
|
||||
)
|
||||
created_at_to = GmtDateTimeField(
|
||||
metadata={
|
||||
"description": "Items created before this date time in GTM, e.g. 2020-12-31T00:00:00."
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class ErrorResponseSchema(marshmallow.Schema):
|
||||
|
||||
@ -21,7 +21,7 @@ from project.api.resources import BaseResource, require_api_access
|
||||
from project.models import AdminUnitInvitation, Event
|
||||
from project.services.admin_unit import get_admin_unit_organization_invitations_query
|
||||
from project.services.event import get_events_query
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.services.search_params import EventSearchParams, TrackableSearchParams
|
||||
from project.utils import strings_are_equal_ignoring_case
|
||||
|
||||
|
||||
@ -41,11 +41,14 @@ class UserOrganizationInvitationListResource(BaseResource):
|
||||
def get(self, **kwargs):
|
||||
login_api_user_or_401()
|
||||
|
||||
pagination = get_admin_unit_organization_invitations_query(
|
||||
current_user.email
|
||||
).paginate()
|
||||
params = TrackableSearchParams()
|
||||
params.load_from_request(**kwargs)
|
||||
|
||||
return pagination
|
||||
query = get_admin_unit_organization_invitations_query(current_user.email)
|
||||
query = params.get_trackable_query(query, AdminUnitInvitation)
|
||||
query = params.get_trackable_order_by(query, AdminUnitInvitation)
|
||||
|
||||
return query.paginate()
|
||||
|
||||
|
||||
class UserOrganizationInvitationResource(BaseResource):
|
||||
@ -92,8 +95,15 @@ class UserFavoriteEventListResource(BaseResource):
|
||||
|
||||
login_api_user_or_401()
|
||||
|
||||
pagination = get_favorite_events_query(current_user.id).paginate()
|
||||
return pagination
|
||||
query = get_favorite_events_query(current_user.id)
|
||||
|
||||
params = EventSearchParams()
|
||||
params.load_from_request(**kwargs)
|
||||
query = params.get_trackable_query(query, Event)
|
||||
query = params.get_trackable_order_by(query, Event)
|
||||
query = query.order_by(Event.min_start)
|
||||
|
||||
return query.paginate()
|
||||
|
||||
|
||||
class UserFavoriteEventSearchResource(BaseResource):
|
||||
@ -108,7 +118,7 @@ class UserFavoriteEventSearchResource(BaseResource):
|
||||
login_api_user_or_401()
|
||||
|
||||
params = EventSearchParams()
|
||||
params.load_from_request()
|
||||
params.load_from_request(**kwargs)
|
||||
params.favored_by_user_id = current_user.id
|
||||
|
||||
pagination = get_events_query(params).paginate()
|
||||
|
||||
@ -2,7 +2,6 @@ from sqlalchemy import Column, Integer, String, Unicode
|
||||
from sqlalchemy.orm import deferred
|
||||
|
||||
from project import db
|
||||
from project.dateutils import gmt_tz
|
||||
from project.models.iowned import IOwned
|
||||
from project.models.trackable_mixin import TrackableMixin
|
||||
|
||||
@ -23,13 +22,6 @@ class Image(db.Model, TrackableMixin, IOwned):
|
||||
def is_empty(self):
|
||||
return not self.data
|
||||
|
||||
def get_hash(self):
|
||||
return (
|
||||
int(self.updated_at.replace(tzinfo=gmt_tz).timestamp() * 1000)
|
||||
if self.updated_at
|
||||
else 0
|
||||
)
|
||||
|
||||
def get_file_extension(self):
|
||||
return self.encoding_format.split("/")[-1] if self.encoding_format else "png"
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import datetime
|
||||
|
||||
from sqlalchemy import Column, DateTime, ForeignKey
|
||||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
from sqlalchemy.orm import backref, declared_attr, deferred, relationship
|
||||
|
||||
from project.dateutils import gmt_tz
|
||||
from project.models.functions import _current_user_id_or_none
|
||||
|
||||
|
||||
@ -18,7 +20,6 @@ class TrackableMixin(object):
|
||||
return deferred(
|
||||
Column(
|
||||
DateTime,
|
||||
default=datetime.datetime.utcnow,
|
||||
onupdate=datetime.datetime.utcnow,
|
||||
),
|
||||
group="trackable",
|
||||
@ -50,7 +51,6 @@ class TrackableMixin(object):
|
||||
Column(
|
||||
"updated_by_id",
|
||||
ForeignKey("user.id", ondelete="SET NULL"),
|
||||
default=_current_user_id_or_none,
|
||||
onupdate=_current_user_id_or_none,
|
||||
),
|
||||
group="trackable",
|
||||
@ -64,3 +64,14 @@ class TrackableMixin(object):
|
||||
remote_side="User.id",
|
||||
backref=backref("updated_%s" % cls.__tablename__, lazy=True),
|
||||
)
|
||||
|
||||
@hybrid_property
|
||||
def last_modified_at(self):
|
||||
return self.updated_at if self.updated_at else self.created_at
|
||||
|
||||
def get_hash(self):
|
||||
return (
|
||||
int(self.last_modified_at.replace(tzinfo=gmt_tz).timestamp() * 1000)
|
||||
if self.last_modified_at
|
||||
else 0
|
||||
)
|
||||
|
||||
@ -24,6 +24,11 @@ from project.services.reference import (
|
||||
get_newest_reference_requests,
|
||||
get_newest_references,
|
||||
)
|
||||
from project.services.search_params import (
|
||||
AdminUnitSearchParams,
|
||||
EventPlaceSearchParams,
|
||||
OrganizerSearchParams,
|
||||
)
|
||||
|
||||
|
||||
def insert_admin_unit_for_user(admin_unit, user, invitation=None):
|
||||
@ -175,59 +180,66 @@ def get_admin_unit_member(id):
|
||||
return AdminUnitMember.query.filter_by(id=id).first()
|
||||
|
||||
|
||||
def get_admin_unit_query(
|
||||
keyword=None,
|
||||
include_unverified=False,
|
||||
only_verifier=False,
|
||||
reference_request_for_admin_unit_id=None,
|
||||
):
|
||||
def get_admin_unit_query(params: AdminUnitSearchParams):
|
||||
query = AdminUnit.query
|
||||
|
||||
if not include_unverified:
|
||||
if not params.include_unverified:
|
||||
query = query.filter(AdminUnit.is_verified)
|
||||
|
||||
if only_verifier:
|
||||
if params.only_verifier:
|
||||
only_verifier_filter = and_(
|
||||
AdminUnit.can_verify_other, AdminUnit.incoming_verification_requests_allowed
|
||||
)
|
||||
query = query.filter(only_verifier_filter)
|
||||
|
||||
if reference_request_for_admin_unit_id:
|
||||
if params.reference_request_for_admin_unit_id:
|
||||
request_filter = and_(
|
||||
AdminUnit.id != reference_request_for_admin_unit_id,
|
||||
AdminUnit.id != params.reference_request_for_admin_unit_id,
|
||||
AdminUnit.incoming_reference_requests_allowed,
|
||||
)
|
||||
query = query.filter(request_filter)
|
||||
|
||||
if keyword:
|
||||
like_keyword = "%" + keyword + "%"
|
||||
order_keyword = keyword + "%"
|
||||
query = params.get_trackable_query(query, AdminUnit)
|
||||
|
||||
if params.keyword:
|
||||
like_keyword = "%" + params.keyword + "%"
|
||||
order_keyword = params.keyword + "%"
|
||||
keyword_filter = or_(
|
||||
AdminUnit.name.ilike(like_keyword),
|
||||
AdminUnit.short_name.ilike(like_keyword),
|
||||
)
|
||||
query = query.filter(keyword_filter).order_by(
|
||||
query = query.filter(keyword_filter)
|
||||
|
||||
query = params.get_trackable_order_by(query, AdminUnit)
|
||||
query = query.order_by(
|
||||
AdminUnit.name.ilike(order_keyword).desc(),
|
||||
AdminUnit.short_name.ilike(order_keyword).desc(),
|
||||
func.lower(AdminUnit.name),
|
||||
)
|
||||
else:
|
||||
query = params.get_trackable_order_by(query, AdminUnit)
|
||||
query = query.order_by(func.lower(AdminUnit.name))
|
||||
|
||||
return query
|
||||
|
||||
|
||||
def get_organizer_query(admin_unit_id, name=None):
|
||||
query = EventOrganizer.query.filter(EventOrganizer.admin_unit_id == admin_unit_id)
|
||||
def get_organizer_query(params: OrganizerSearchParams):
|
||||
query = EventOrganizer.query.filter(
|
||||
EventOrganizer.admin_unit_id == params.admin_unit_id
|
||||
)
|
||||
|
||||
if name:
|
||||
like_name = "%" + name + "%"
|
||||
order_name = name + "%"
|
||||
query = params.get_trackable_query(query, EventOrganizer)
|
||||
|
||||
if params.name:
|
||||
like_name = "%" + params.name + "%"
|
||||
order_name = params.name + "%"
|
||||
query = params.get_trackable_order_by(query, EventOrganizer)
|
||||
query = query.filter(EventOrganizer.name.ilike(like_name)).order_by(
|
||||
EventOrganizer.name.ilike(order_name).desc(),
|
||||
func.lower(EventOrganizer.name),
|
||||
)
|
||||
else:
|
||||
query = params.get_trackable_order_by(query, EventOrganizer)
|
||||
query = query.order_by(func.lower(EventOrganizer.name))
|
||||
|
||||
return query
|
||||
@ -243,13 +255,15 @@ def get_custom_widget_query(admin_unit_id, name=None):
|
||||
return query.order_by(func.lower(CustomWidget.name))
|
||||
|
||||
|
||||
def get_place_query(admin_unit_id, name=None):
|
||||
query = EventPlace.query.filter(EventPlace.admin_unit_id == admin_unit_id)
|
||||
def get_place_query(params: EventPlaceSearchParams):
|
||||
query = EventPlace.query.filter(EventPlace.admin_unit_id == params.admin_unit_id)
|
||||
query = params.get_trackable_query(query, EventPlace)
|
||||
|
||||
if name:
|
||||
like_name = "%" + name + "%"
|
||||
if params.name:
|
||||
like_name = "%" + params.name + "%"
|
||||
query = query.filter(EventPlace.name.ilike(like_name))
|
||||
|
||||
query = params.get_trackable_order_by(query, EventPlace)
|
||||
return query.order_by(func.lower(EventPlace.name))
|
||||
|
||||
|
||||
@ -378,7 +392,7 @@ def create_ical_events_for_admin_unit(
|
||||
|
||||
from project.dateutils import get_today
|
||||
from project.services.event import create_ical_events_for_search
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.services.search_params import EventSearchParams
|
||||
|
||||
params = EventSearchParams()
|
||||
params.date_from = get_today() - relativedelta(months=1)
|
||||
|
||||
@ -45,8 +45,8 @@ from project.models import (
|
||||
UserFavoriteEvents,
|
||||
sanitize_allday_instance,
|
||||
)
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.services.reference import get_event_reference, upsert_event_reference
|
||||
from project.services.search_params import EventSearchParams
|
||||
from project.utils import get_pending_changes, get_place_str
|
||||
from project.views.utils import truncate
|
||||
|
||||
@ -65,6 +65,8 @@ def upsert_event_category(category_name):
|
||||
|
||||
|
||||
def fill_event_filter(event_filter, params: EventSearchParams):
|
||||
event_filter = params.fill_trackable_filter(event_filter, Event)
|
||||
|
||||
if params.keyword:
|
||||
tq = func.websearch_to_tsquery("german", params.keyword)
|
||||
event_filter = and_(
|
||||
@ -196,19 +198,25 @@ def fill_event_admin_unit_filter(event_filter, params: EventSearchParams):
|
||||
admin_unit_reference = None
|
||||
|
||||
if params.admin_unit_id:
|
||||
if params.include_admin_unit_references:
|
||||
if params.include_admin_unit_references or params.admin_unit_references_only:
|
||||
admin_unit_refs_subquery = EventReference.query.filter(
|
||||
EventReference.admin_unit_id == params.admin_unit_id
|
||||
).subquery()
|
||||
admin_unit_reference = aliased(EventReference, admin_unit_refs_subquery)
|
||||
|
||||
event_filter = and_(
|
||||
event_filter,
|
||||
or_(
|
||||
Event.admin_unit_id == params.admin_unit_id,
|
||||
if params.admin_unit_references_only:
|
||||
event_filter = and_(
|
||||
event_filter,
|
||||
admin_unit_reference.id.isnot(None),
|
||||
),
|
||||
)
|
||||
)
|
||||
else:
|
||||
event_filter = and_(
|
||||
event_filter,
|
||||
or_(
|
||||
Event.admin_unit_id == params.admin_unit_id,
|
||||
admin_unit_reference.id.isnot(None),
|
||||
),
|
||||
)
|
||||
else:
|
||||
event_filter = and_(
|
||||
event_filter, Event.admin_unit_id == params.admin_unit_id
|
||||
@ -271,21 +279,9 @@ def get_event_dates_query(params: EventSearchParams):
|
||||
.filter(event_filter)
|
||||
)
|
||||
|
||||
if params.sort == "-rating":
|
||||
if admin_unit_reference:
|
||||
result = result.order_by(
|
||||
case(
|
||||
(
|
||||
admin_unit_reference.rating.isnot(None),
|
||||
admin_unit_reference.rating,
|
||||
),
|
||||
else_=Event.rating,
|
||||
).desc()
|
||||
)
|
||||
else:
|
||||
result = result.order_by(Event.rating.desc())
|
||||
|
||||
result = fill_event_query_order(result, admin_unit_reference, params)
|
||||
result = result.order_by(EventDate.start)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@ -395,17 +391,38 @@ def get_events_query(params: EventSearchParams):
|
||||
isouter=True,
|
||||
)
|
||||
|
||||
result = (
|
||||
result.options(
|
||||
contains_eager(Event.event_place).contains_eager(EventPlace.location),
|
||||
joinedload(Event.categories),
|
||||
joinedload(Event.organizer),
|
||||
joinedload(Event.photo),
|
||||
joinedload(Event.admin_unit),
|
||||
)
|
||||
.filter(event_filter)
|
||||
.order_by(Event.min_start)
|
||||
)
|
||||
result = result.options(
|
||||
contains_eager(Event.event_place).contains_eager(EventPlace.location),
|
||||
joinedload(Event.categories),
|
||||
joinedload(Event.organizer),
|
||||
joinedload(Event.photo),
|
||||
joinedload(Event.admin_unit),
|
||||
).filter(event_filter)
|
||||
|
||||
result = fill_event_query_order(result, admin_unit_reference, params)
|
||||
result = result.order_by(Event.min_start)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def fill_event_query_order(result, admin_unit_reference, params: EventSearchParams):
|
||||
result = params.get_trackable_order_by(result, Event)
|
||||
|
||||
if params.sort == "-rating":
|
||||
if admin_unit_reference:
|
||||
result = result.order_by(
|
||||
case(
|
||||
(
|
||||
admin_unit_reference.rating.isnot(None),
|
||||
admin_unit_reference.rating,
|
||||
),
|
||||
else_=Event.rating,
|
||||
).desc()
|
||||
)
|
||||
else:
|
||||
result = result.order_by(Event.rating.desc())
|
||||
elif params.sort == "-reference_created_at" and admin_unit_reference:
|
||||
result = result.order_by(admin_unit_reference.created_at.desc())
|
||||
|
||||
return result
|
||||
|
||||
@ -551,8 +568,8 @@ def populate_ical_event_with_event(
|
||||
if model_event.created_at:
|
||||
ical_event.add("dtstamp", model_event.created_at)
|
||||
|
||||
if model_event.updated_at:
|
||||
ical_event.add("last-modified", model_event.updated_at)
|
||||
if model_event.last_modified_at:
|
||||
ical_event.add("last-modified", model_event.last_modified_at)
|
||||
|
||||
if model_event.status and model_event.status == EventStatus.cancelled:
|
||||
ical_event.add("status", "CANCELLED")
|
||||
|
||||
@ -27,7 +27,7 @@ def create_ical_events_for_organizer(
|
||||
|
||||
from project.dateutils import get_today
|
||||
from project.services.event import create_ical_events_for_search
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.services.search_params import EventSearchParams
|
||||
|
||||
params = EventSearchParams()
|
||||
params.date_from = get_today() - relativedelta(months=1)
|
||||
|
||||
@ -10,6 +10,7 @@ from project.models import (
|
||||
EventReferenceRequestReviewStatus,
|
||||
)
|
||||
from project.models.admin_unit import AdminUnit
|
||||
from project.services.search_params import EventReferenceSearchParams
|
||||
|
||||
|
||||
def create_event_reference_for_request(request):
|
||||
@ -36,12 +37,24 @@ def upsert_event_reference(event_id: int, admin_unit_id: int):
|
||||
return result
|
||||
|
||||
|
||||
def get_reference_incoming_query(admin_unit):
|
||||
return EventReference.query.filter(EventReference.admin_unit_id == admin_unit.id)
|
||||
def get_reference_incoming_query(params: EventReferenceSearchParams):
|
||||
result = EventReference.query
|
||||
|
||||
if params.admin_unit_id:
|
||||
result = result.filter(EventReference.admin_unit_id == params.admin_unit_id)
|
||||
|
||||
result = params.get_trackable_query(result, EventReference)
|
||||
result = params.get_trackable_order_by(result, EventReference)
|
||||
result = result.order_by(EventReference.created_at.desc())
|
||||
return result
|
||||
|
||||
|
||||
def get_reference_outgoing_query(admin_unit):
|
||||
return EventReference.query.join(Event).filter(Event.admin_unit_id == admin_unit.id)
|
||||
return (
|
||||
EventReference.query.join(Event)
|
||||
.filter(Event.admin_unit_id == admin_unit.id)
|
||||
.order_by(EventReference.created_at.desc())
|
||||
)
|
||||
|
||||
|
||||
def get_reference_requests_incoming_query(admin_unit):
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
from typing import Type
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from flask import request
|
||||
from sqlalchemy import and_
|
||||
|
||||
from project.dateutils import (
|
||||
date_set_end_of_day,
|
||||
@ -7,10 +10,100 @@ from project.dateutils import (
|
||||
form_input_to_date,
|
||||
get_today,
|
||||
)
|
||||
from project.models.trackable_mixin import TrackableMixin
|
||||
|
||||
|
||||
class EventSearchParams(object):
|
||||
class BaseSearchParams(object):
|
||||
def __init__(self):
|
||||
self.sort = None
|
||||
|
||||
def load_from_request(self, **kwargs):
|
||||
self.sort = kwargs.get("sort", self.sort)
|
||||
|
||||
|
||||
class TrackableSearchParams(BaseSearchParams):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.created_at_from = None
|
||||
self.created_at_to = None
|
||||
|
||||
def load_from_request(self, **kwargs):
|
||||
super().load_from_request(**kwargs)
|
||||
|
||||
self.created_at_from = kwargs.get("created_at_from", self.created_at_from)
|
||||
self.created_at_to = kwargs.get("created_at_to", self.created_at_to)
|
||||
|
||||
def get_trackable_query(self, query, klass: Type[TrackableMixin]):
|
||||
filter = self.fill_trackable_filter(1 == 1, klass)
|
||||
return query.filter(filter)
|
||||
|
||||
def fill_trackable_filter(self, filter, klass: Type[TrackableMixin]):
|
||||
if self.created_at_from:
|
||||
filter = and_(filter, klass.created_at >= self.created_at_from)
|
||||
|
||||
if self.created_at_to:
|
||||
filter = and_(filter, klass.created_at < self.created_at_to)
|
||||
|
||||
return filter
|
||||
|
||||
def get_trackable_order_by(self, query, klass: Type[TrackableMixin]):
|
||||
if self.sort == "-created_at":
|
||||
query = query.order_by(klass.created_at.desc())
|
||||
elif self.sort == "-updated_at":
|
||||
query = query.order_by(klass.updated_at.desc().nulls_last())
|
||||
elif self.sort == "-last_modified_at":
|
||||
query = query.order_by(klass.last_modified_at.desc())
|
||||
|
||||
return query
|
||||
|
||||
|
||||
class EventReferenceSearchParams(TrackableSearchParams):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.admin_unit_id = None
|
||||
|
||||
|
||||
class AdminUnitSearchParams(TrackableSearchParams):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.keyword = None
|
||||
self.include_unverified = False
|
||||
self.only_verifier = False
|
||||
self.reference_request_for_admin_unit_id = None
|
||||
|
||||
def load_from_request(self, **kwargs):
|
||||
super().load_from_request(**kwargs)
|
||||
|
||||
self.keyword = kwargs.get("keyword", self.keyword)
|
||||
|
||||
|
||||
class OrganizerSearchParams(TrackableSearchParams):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.admin_unit_id = None
|
||||
self.name = None
|
||||
|
||||
def load_from_request(self, **kwargs):
|
||||
super().load_from_request(**kwargs)
|
||||
|
||||
self.name = kwargs.get("name", self.name)
|
||||
|
||||
|
||||
class EventPlaceSearchParams(TrackableSearchParams):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.admin_unit_id = None
|
||||
self.name = None
|
||||
|
||||
def load_from_request(self, **kwargs):
|
||||
super().load_from_request(**kwargs)
|
||||
|
||||
self.name = kwargs.get("name", self.name)
|
||||
|
||||
|
||||
class EventSearchParams(TrackableSearchParams):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._date_from = None
|
||||
self._date_to = None
|
||||
self._date_from_str = None
|
||||
@ -18,6 +111,7 @@ class EventSearchParams(object):
|
||||
self._coordinate = None
|
||||
self.admin_unit_id = None
|
||||
self.include_admin_unit_references = None
|
||||
self.admin_unit_references_only = None
|
||||
self.can_read_private_events = None
|
||||
self.can_read_planned_events = None
|
||||
self.keyword = None
|
||||
@ -29,7 +123,6 @@ class EventSearchParams(object):
|
||||
self.event_place_id = None
|
||||
self.event_list_id = None
|
||||
self.weekday = None
|
||||
self.sort = None
|
||||
self.status = None
|
||||
self.public_status = None
|
||||
self.favored_by_user_id = None
|
||||
@ -112,7 +205,7 @@ class EventSearchParams(object):
|
||||
return None
|
||||
|
||||
def load_bool_param(self, param: str):
|
||||
return request.args[param] == "y"
|
||||
return request.args[param].lower() in ("true", "t", "yes", "y", "on", "1")
|
||||
|
||||
def load_status_list_param(self):
|
||||
stati = self.load_list_param("status")
|
||||
@ -146,7 +239,9 @@ class EventSearchParams(object):
|
||||
|
||||
return result
|
||||
|
||||
def load_from_request(self):
|
||||
def load_from_request(self, **kwargs):
|
||||
super().load_from_request(**kwargs)
|
||||
|
||||
if "date_from" in request.args:
|
||||
self.date_from_str = request.args["date_from"]
|
||||
|
||||
@ -183,9 +278,6 @@ class EventSearchParams(object):
|
||||
if "postal_code" in request.args:
|
||||
self.postal_code = self.load_list_param("postal_code")
|
||||
|
||||
if "sort" in request.args:
|
||||
self.sort = request.args["sort"]
|
||||
|
||||
if "organization_id" in request.args:
|
||||
self.admin_unit_id = request.args["organization_id"]
|
||||
|
||||
@ -200,3 +292,13 @@ class EventSearchParams(object):
|
||||
|
||||
if "exclude_recurring" in request.args:
|
||||
self.exclude_recurring = self.load_bool_param("exclude_recurring")
|
||||
|
||||
if "include_organization_references" in request.args:
|
||||
self.include_admin_unit_references = self.load_bool_param(
|
||||
"include_organization_references"
|
||||
)
|
||||
|
||||
if "organization_references_only" in request.args:
|
||||
self.admin_unit_references_only = self.load_bool_param(
|
||||
"organization_references_only"
|
||||
)
|
||||
@ -24,7 +24,7 @@ def generate_sitemap(pinggoogle: bool):
|
||||
today = get_today()
|
||||
events = (
|
||||
Event.query.join(Event.admin_unit)
|
||||
.options(load_only(Event.id, Event.updated_at))
|
||||
.options(load_only(Event.id, Event.last_modified_at))
|
||||
.filter(Event.dates.any(EventDate.start >= today))
|
||||
.filter(
|
||||
and_(
|
||||
@ -38,7 +38,11 @@ def generate_sitemap(pinggoogle: bool):
|
||||
|
||||
for event in events:
|
||||
loc = url_for("event", event_id=event.id)
|
||||
lastmod = event.updated_at.strftime("%Y-%m-%d") if event.updated_at else None
|
||||
lastmod = (
|
||||
event.last_modified_at.strftime("%Y-%m-%d")
|
||||
if event.last_modified_at
|
||||
else None
|
||||
)
|
||||
lastmod_tag = f"<lastmod>{lastmod}</lastmod>" if lastmod else ""
|
||||
buf.write(f"<url><loc>{loc}</loc>{lastmod_tag}</url>")
|
||||
|
||||
|
||||
@ -366,7 +366,6 @@
|
||||
|
||||
{% macro render_audit(tracking_mixing, show_user=False) %}
|
||||
{% set created_at = tracking_mixing.created_at | datetimeformat('short') %}
|
||||
{% set updated_at = tracking_mixing.updated_at | datetimeformat('short') %}
|
||||
|
||||
{% if show_user %}
|
||||
{{ _('Created at %(created_at)s by %(created_by)s.', created_at=created_at, created_by=tracking_mixing.created_by.email) }}
|
||||
@ -374,11 +373,14 @@
|
||||
{{ _('Created at %(created_at)s.', created_at=created_at) }}
|
||||
{% endif %}
|
||||
|
||||
{% if created_at != updated_at %}
|
||||
{% if show_user %}
|
||||
{{ _('Last updated at %(updated_at)s by %(updated_by)s.', updated_at=updated_at, updated_by=tracking_mixing.updated_by.email) }}
|
||||
{% else %}
|
||||
{{ _('Last updated at %(updated_at)s.', updated_at=updated_at) }}
|
||||
{% if tracking_mixing.updated_at %}
|
||||
{% set updated_at = tracking_mixing.updated_at | datetimeformat('short') %}
|
||||
{% if created_at != updated_at %}
|
||||
{% if show_user %}
|
||||
{{ _('Last updated at %(updated_at)s by %(updated_by)s.', updated_at=updated_at, updated_by=tracking_mixing.updated_by.email) }}
|
||||
{% else %}
|
||||
{{ _('Last updated at %(updated_at)s.', updated_at=updated_at) }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
@ -13,7 +13,7 @@ from project.services.event import (
|
||||
get_meta_data,
|
||||
get_upcoming_event_dates,
|
||||
)
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.services.search_params import EventSearchParams
|
||||
from project.views.event import get_event_category_choices, get_user_rights
|
||||
from project.views.utils import (
|
||||
flash_errors,
|
||||
|
||||
@ -14,7 +14,7 @@ from project.utils import make_dir
|
||||
@app.route("/image/<int:id>/<hash>")
|
||||
def image(id, hash=None):
|
||||
image = Image.query.options(
|
||||
load_only(Image.id, Image.encoding_format, Image.updated_at)
|
||||
load_only(Image.id, Image.encoding_format, Image.last_modified_at)
|
||||
).get_or_404(id)
|
||||
|
||||
# Dimensions
|
||||
|
||||
@ -43,8 +43,8 @@ from project.services.admin_unit import (
|
||||
get_member_for_admin_unit_by_user_id,
|
||||
)
|
||||
from project.services.event import get_events_query
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.services.event_suggestion import get_event_reviews_query
|
||||
from project.services.search_params import EventSearchParams
|
||||
from project.utils import get_place_str
|
||||
from project.views.event import get_event_category_choices
|
||||
from project.views.utils import (
|
||||
|
||||
@ -4,7 +4,7 @@ from flask_security import auth_required
|
||||
from project import app
|
||||
from project.access import can_use_planning
|
||||
from project.forms.planning import PlanningForm
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.services.search_params import EventSearchParams
|
||||
from project.views.event import get_event_category_choices
|
||||
from project.views.utils import permission_missing
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@ from flask import abort, flash, redirect, render_template, url_for
|
||||
from flask_babel import gettext
|
||||
from flask_security import auth_required
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.sql import desc
|
||||
|
||||
from project import app, db
|
||||
from project.access import (
|
||||
@ -22,6 +21,7 @@ from project.services.reference import (
|
||||
get_reference_incoming_query,
|
||||
get_reference_outgoing_query,
|
||||
)
|
||||
from project.services.search_params import EventReferenceSearchParams
|
||||
from project.views.utils import flash_errors, get_pagination_urls, handleSqlError
|
||||
|
||||
|
||||
@ -112,11 +112,9 @@ def event_reference_update(id):
|
||||
@auth_required()
|
||||
def manage_admin_unit_references_incoming(id):
|
||||
admin_unit = get_admin_unit_for_manage_or_404(id)
|
||||
references = (
|
||||
get_reference_incoming_query(admin_unit)
|
||||
.order_by(desc(EventReference.created_at))
|
||||
.paginate()
|
||||
)
|
||||
params = EventReferenceSearchParams()
|
||||
params.admin_unit_id = admin_unit.id
|
||||
references = get_reference_incoming_query(params).paginate()
|
||||
|
||||
return render_template(
|
||||
"manage/references_incoming.html",
|
||||
@ -130,11 +128,7 @@ def manage_admin_unit_references_incoming(id):
|
||||
@auth_required()
|
||||
def manage_admin_unit_references_outgoing(id):
|
||||
admin_unit = get_admin_unit_for_manage_or_404(id)
|
||||
references = (
|
||||
get_reference_outgoing_query(admin_unit)
|
||||
.order_by(desc(EventReference.created_at))
|
||||
.paginate()
|
||||
)
|
||||
references = get_reference_outgoing_query(admin_unit).paginate()
|
||||
|
||||
return render_template(
|
||||
"manage/references_outgoing.html",
|
||||
|
||||
@ -15,6 +15,7 @@ from project.models import (
|
||||
)
|
||||
from project.models.admin_unit import AdminUnit
|
||||
from project.services.admin_unit import get_admin_unit_query
|
||||
from project.services.search_params import AdminUnitSearchParams
|
||||
from project.services.verification import get_verification_requests_incoming_query
|
||||
from project.views.utils import (
|
||||
flash_errors,
|
||||
@ -79,7 +80,10 @@ def manage_admin_unit_verification_requests_outgoing(id):
|
||||
@manage_required("verification_request:create")
|
||||
def manage_admin_unit_verification_requests_outgoing_create_select(id):
|
||||
admin_unit = g.manage_admin_unit
|
||||
admin_units = get_admin_unit_query(only_verifier=True).paginate()
|
||||
|
||||
params = AdminUnitSearchParams()
|
||||
params.only_verifier = True
|
||||
admin_units = get_admin_unit_query(params).paginate()
|
||||
|
||||
return render_template(
|
||||
"manage/verification_requests_outgoing_create_select.html",
|
||||
|
||||
@ -19,9 +19,9 @@ from project.services.event import (
|
||||
get_event_date_with_details_or_404,
|
||||
get_event_dates_query,
|
||||
)
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.services.event_suggestion import insert_event_suggestion
|
||||
from project.services.place import get_event_places
|
||||
from project.services.search_params import EventSearchParams
|
||||
from project.views.event import get_event_category_choices
|
||||
from project.views.utils import (
|
||||
flash_errors,
|
||||
|
||||
@ -61,6 +61,15 @@ def test_search(client, seeder: Seeder, utils: UtilActions, app, db):
|
||||
seeder.create_event(admin_unit_id, draft=True)
|
||||
seeder.create_event_unverified()
|
||||
|
||||
url = utils.get_url("api_v1_event_date_search", sort="-created_at")
|
||||
response = utils.get_json_ok(url)
|
||||
|
||||
url = utils.get_url("api_v1_event_date_search", sort="-updated_at")
|
||||
response = utils.get_json_ok(url)
|
||||
|
||||
url = utils.get_url("api_v1_event_date_search", sort="-last_modified_at")
|
||||
response = utils.get_json_ok(url)
|
||||
|
||||
url = utils.get_url("api_v1_event_date_search", sort="-rating")
|
||||
response = utils.get_json_ok(url)
|
||||
assert len(response.json["items"]) == 1
|
||||
@ -84,6 +93,13 @@ def test_search(client, seeder: Seeder, utils: UtilActions, app, db):
|
||||
)
|
||||
response = utils.get_json_ok(url)
|
||||
|
||||
url = utils.get_url(
|
||||
"api_v1_event_date_search",
|
||||
created_at_from="2023-07-07T00:00:00",
|
||||
created_at_to="2023-07-08T00:00:00",
|
||||
)
|
||||
response = utils.get_json_ok(url)
|
||||
|
||||
url = utils.get_url(
|
||||
"api_v1_event_date_search", coordinate="51.9077888,10.4333312", distance=500
|
||||
)
|
||||
@ -139,6 +155,14 @@ def test_search(client, seeder: Seeder, utils: UtilActions, app, db):
|
||||
response = utils.get_json_ok(url)
|
||||
assert len(response.json["items"]) == 0
|
||||
|
||||
seeder.create_any_reference(admin_unit_id)
|
||||
url = utils.get_url(
|
||||
"api_v1_event_date_search",
|
||||
organization_id=admin_unit_id,
|
||||
sort="-reference_created_at",
|
||||
)
|
||||
response = utils.get_json_ok(url)
|
||||
|
||||
|
||||
def test_search_public_status(client, seeder: Seeder, utils: UtilActions, app, db):
|
||||
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
|
||||
|
||||
@ -126,6 +126,21 @@ def test_event_search(client, seeder: Seeder, utils: UtilActions):
|
||||
or response.json["items"][1]["public_status"] == "draft"
|
||||
)
|
||||
|
||||
seeder.create_any_reference(admin_unit_id)
|
||||
url = utils.get_url(
|
||||
"api_v1_organization_event_search",
|
||||
id=admin_unit_id,
|
||||
include_organization_references="yes",
|
||||
)
|
||||
response = utils.get_json_ok(url)
|
||||
|
||||
url = utils.get_url(
|
||||
"api_v1_organization_event_search",
|
||||
id=admin_unit_id,
|
||||
organization_references_only="yes",
|
||||
)
|
||||
response = utils.get_json_ok(url)
|
||||
|
||||
|
||||
def test_organizers(client, seeder: Seeder, utils: UtilActions):
|
||||
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
|
||||
@ -420,7 +435,14 @@ def test_references_incoming(client, seeder: Seeder, utils: UtilActions):
|
||||
url = utils.get_url(
|
||||
"api_v1_organization_incoming_event_reference_list",
|
||||
id=admin_unit_id,
|
||||
name="crew",
|
||||
)
|
||||
utils.get_json_ok(url)
|
||||
|
||||
url = utils.get_url(
|
||||
"api_v1_organization_incoming_event_reference_list",
|
||||
id=admin_unit_id,
|
||||
created_at_from="2023-07-07T00:00:00",
|
||||
created_at_to="2023-07-08T00:00:00",
|
||||
)
|
||||
utils.get_json_ok(url)
|
||||
|
||||
|
||||
@ -199,7 +199,7 @@ def test_get_events_query(client, seeder, app):
|
||||
|
||||
with app.app_context():
|
||||
from project.services.event import get_events_query
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.services.search_params import EventSearchParams
|
||||
|
||||
params = EventSearchParams()
|
||||
params.admin_unit_id = admin_unit_id
|
||||
@ -253,7 +253,7 @@ def test_get_events_fulltext(
|
||||
|
||||
with app.app_context():
|
||||
from project.services.event import get_events_query
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.services.search_params import EventSearchParams
|
||||
|
||||
params = EventSearchParams()
|
||||
params.keyword = keyword
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
def test_date_str(client, seeder, utils):
|
||||
from project.dateutils import create_berlin_date
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.services.search_params import EventSearchParams
|
||||
|
||||
params = EventSearchParams()
|
||||
params.date_from = create_berlin_date(2030, 12, 30, 0)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user