Skip to content

Commit

Permalink
Merge branch 'development' of https://github.com/breatheco-de/apiv2 i…
Browse files Browse the repository at this point in the history
…nto development
  • Loading branch information
jefer94 committed Jan 30, 2025
2 parents 5581e86 + 2bff715 commit 4198ada
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 18 deletions.
202 changes: 202 additions & 0 deletions breathecode/events/tests/urls/tests_me_event_checkin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
from datetime import datetime, timedelta, timezone

import capyc.pytest as capy
from django.urls.base import reverse_lazy
from django.utils import timezone


def serialize_event(event):
return {
"id": event.id,
"title": event.title,
"starting_at": (
event.starting_at.strftime("%Y-%m-%dT%H:%M:%S.%f") + "Z"
if isinstance(event.starting_at, datetime)
else None
),
"ending_at": (
event.ending_at.strftime("%Y-%m-%dT%H:%M:%S.%f") + "Z" if isinstance(event.ending_at, datetime) else None
),
"event_type": {
"id": event.event_type.id,
"slug": event.event_type.slug,
"name": event.event_type.name,
"technologies": event.event_type.technologies,
},
"slug": event.slug,
"excerpt": event.excerpt,
"lang": event.lang,
"url": event.url,
"banner": event.banner,
"description": event.description,
"capacity": event.capacity,
"status": event.status,
"host": event.host,
"ended_at": (event.ended_at.strftime("%Y-%m-%dT%H:%M:%S.%f") + "Z" if event.ended_at else None),
"online_event": event.online_event,
"is_public": event.is_public,
"venue": (
None
if not event.venue
else {
"id": event.venue.id,
"title": event.venue.title,
"street_address": event.venue.street_address,
"city": event.venue.city.name,
"zip_code": event.venue.zip_code,
"state": event.venue.state,
"updated_at": event.venue.updated_at.isoformat(),
}
),
"academy": (
None
if not event.academy
else {
"id": event.academy.id,
"slug": event.academy.slug,
"name": event.academy.name,
"city": {"name": event.academy.city.name} if event.academy.city else None,
}
),
"sync_with_eventbrite": event.sync_with_eventbrite,
"eventbrite_sync_status": event.eventbrite_sync_status,
"eventbrite_sync_description": event.eventbrite_sync_description,
"tags": event.tags,
"asset_slug": event.asset_slug,
"host_user": (
None
if not event.host_user
else {
"id": event.host_user.id,
"first_name": event.host_user.first_name,
"last_name": event.host_user.last_name,
"profile": getattr(event.host_user, "profile", None),
}
),
"author": (
None
if not event.author
else {
"id": event.author.id,
"first_name": event.author.first_name,
"last_name": event.author.last_name,
}
),
"asset": None,
}


def test_filter_by_past_events_of_a_user(client: capy.Client, database: capy.Database, fake: capy.Fake):
url = reverse_lazy("events:me_event_checkin")

model = database.create(
city=1,
country=1,
user=1,
academy={
"slug": fake.slug(),
"name": fake.name(),
"logo_url": "https://example.com/logo.jpg",
"street_address": "Address",
},
event_type=[
{
"slug": fake.slug(),
"name": fake.name(),
"description": "description1",
"technologies": "python, flask",
},
{
"slug": fake.slug(),
"name": fake.name(),
"description": "description2",
"technologies": "flask, pandas",
},
{
"slug": fake.slug(),
"name": fake.name(),
"description": "description3",
"technologies": "javascript, java",
},
],
event={
"title": "My event",
"capacity": 100,
"banner": "https://example.com/banner.jpg",
"starting_at": timezone.now() - timedelta(hours=2),
"ending_at": timezone.now(),
"status": "ACTIVE",
"event_type_id": 1,
},
event_checkin={
"email": "fake@4geeksacademy.com",
"attendee": 1,
"event": 2,
},
)
client.force_authenticate(model.user)
response = client.get(f"{url}?past=true")
json = response.json()

expected = [serialize_event(model.event)]

assert response.status_code == 200
assert expected == json


def test_filter_by_future_events_of_a_user(client: capy.Client, database: capy.Database, fake: capy.Fake):
url = reverse_lazy("events:me_event_checkin")

model = database.create(
city=1,
country=1,
user=1,
academy={
"slug": fake.slug(),
"name": fake.name(),
"logo_url": "https://example.com/logo.jpg",
"street_address": "Address",
},
event_type=[
{
"slug": fake.slug(),
"name": fake.name(),
"description": "description1",
"technologies": "python, flask",
},
{
"slug": fake.slug(),
"name": fake.name(),
"description": "description2",
"technologies": "flask, pandas",
},
{
"slug": fake.slug(),
"name": fake.name(),
"description": "description3",
"technologies": "javascript, java",
},
],
event={
"title": "My event",
"capacity": 100,
"banner": "https://example.com/banner.jpg",
"starting_at": timezone.now() + timedelta(hours=2),
"ending_at": timezone.now() + timedelta(hours=6),
"status": "ACTIVE",
"event_type_id": 1,
},
event_checkin={
"email": "fake@4geeksacademy.com",
"attendee": 1,
"event": 2,
},
)
client.force_authenticate(model.user)
response = client.get(f"{url}?upcoming=true")
json = response.json()

expected = [serialize_event(model.event)]

assert response.status_code == 200
assert expected == json
33 changes: 18 additions & 15 deletions breathecode/events/urls.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,41 @@
from django.urls import path

from .syndication import LatestEventsFeed
from .views import (
AcademyEventCheckinView,
AcademyEventJoinView,
AcademyEventTypeView,
AcademyEventView,
AcademyLiveClassJoinView,
AcademyLiveClassView,
AcademyOrganizationOrganizerView,
AcademyOrganizationView,
AcademyOrganizerView,
AcademyVenueView,
EventCheckinView,
EventMeCheckinView,
EventMeView,
EventPublicView,
EventTypeView,
EventTypeVisibilitySettingView,
EventView,
EventTypeView,
AcademyEventCheckinView,
MeLiveClassView,
get_events,
eventbrite_webhook,
AcademyEventView,
AcademyVenueView,
ICalCohortsView,
ICalEventView,
ICalStudentView,
AcademyOrganizationView,
MeLiveClassView,
OrganizationWebhookView,
AcademyOrganizerView,
AcademyOrganizationOrganizerView,
EventCheckinView,
AcademyLiveClassJoinView,
UserEventCheckinView,
eventbrite_webhook,
get_events,
join_event,
EventMeCheckinView,
join_live_class,
EventPublicView,
)
from .syndication import LatestEventsFeed

app_name = "events"
urlpatterns = [
path("", EventView.as_view(), name="root"),
path("me", EventMeView.as_view(), name="me"),
path("me/event/checkin", UserEventCheckinView.as_view(), name="me_event_checkin"),
path("me/event/<int:event_id>/checkin", EventMeCheckinView.as_view(), name="me_event_id_checkin"),
path("me/event/<int:event_id>/join", join_event, name="me_event_id_join"),
path("me/event/<int:event_id>", EventMeView.as_view(), name="me_event_id"),
Expand Down
28 changes: 28 additions & 0 deletions breathecode/events/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,34 @@ def post(self, request, format=None):
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class UserEventCheckinView(APIView):
"""
Return future and past events of the user. Accepts query parameters 'upcoming' and 'past' for filtering.
If no parameters are provided, returns both upcoming and past events.
"""

def get(self, request):
user = request.user

user_checkins = EventCheckin.objects.filter(attendee=user).select_related("event")
event_ids = user_checkins.values_list("event_id", flat=True)

events = Event.objects.filter(id__in=event_ids)

lookup = {}

if self.request.GET.get("upcoming", "") == "true":
lookup["ending_at__gte"] = timezone.now()
elif self.request.GET.get("past", "") == "true":
lookup["starting_at__lte"] = timezone.now()

events = events.filter(**lookup)
events = events.order_by("starting_at")

serializer = EventSmallSerializer(events, many=True, context={"user": user})
return Response(serializer.data)


class EventMeView(APIView):
extensions = APIViewExtensions(cache=EventCache, cache_per_user=True, paginate=True)

Expand Down
8 changes: 7 additions & 1 deletion breathecode/payments/tests/urls/tests_consumable_checkout.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,13 @@ def test__resourse_is_required(self):
self.client.force_authenticate(model.user)

url = reverse_lazy("payments:consumable_checkout")
data = {"service": 1, "how_many": 1, "academy": 1}
data = {
"service": 1,
"how_many": 1,
"academy": 1,
"mentorship_service_set": 1,
"event_type_set": 1,
}
response = self.client.post(url, data, format="json")
self.client.force_authenticate(model.user)

Expand Down
4 changes: 2 additions & 2 deletions breathecode/payments/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1639,7 +1639,7 @@ def post(self, request):
mentorship_service_set = request.data.get("mentorship_service_set")
event_type_set = request.data.get("event_type_set")

if [mentorship_service_set, event_type_set].count(None) != 1:
if mentorship_service_set is not None and event_type_set is not None:
raise ValidationException(
translation(
lang,
Expand Down Expand Up @@ -1672,7 +1672,7 @@ def post(self, request):
code=400,
)

elif service.type not in ["MENTORSHIP_SERVICE_SET", "EVENT_TYPE_SET"]:
elif service.type not in ["MENTORSHIP_SERVICE_SET", "EVENT_TYPE_SET", "VOID"]:
raise ValidationException(
translation(
lang,
Expand Down

0 comments on commit 4198ada

Please sign in to comment.