Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scheduling of Live Events(#77) #92

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

Deep-Chill
Copy link
Contributor

  • Early versions of API endpoints/URLs to add and delete LiveEvents.
  • Gets LiveEvent objects using their scheduled_at time, for adding/deleting as well as marking attendance.
  • Updates Mixin to add a rank property to help correctly add attributes to LiveEvent objects.

Issues:

  • On the URL: http://127.0.0.1:8000/sequences/steve/seq-cert-live-event/1/ if a user is not logged in, they are able to make POST requests and the timer updates, but if logged in as either steve or a superuser it returns djaodjin-pages-vue.js:581 Error sending ping: Forbidden Haven't investigated yet.

- Early versions of API endpoints/URLs to add and delete LiveEvents.
- Gets LiveEvent objects using their scheduled_at time, for adding/deleting as well as marking attendance.
- Updates Mixin to add a rank property to help correctly add attributes to LiveEvent objects.

- Issues:
- - On the URL: `http://127.0.0.1:8000/sequences/steve/seq-cert-live-event/1/` if a user is not logged in, they are able to make POST requests and the timer updates, but if logged in as either steve or a superuser it returns `djaodjin-pages-vue.js:581 Error sending ping: Forbidden` Haven't investigated yet.
- Added LiveEvent creation/deletion API Endpoints
- Added ability to view multiple LiveEvents belonging to the same PageElement on the front-end view. Could implement this with an API endpoint instead and call that  for views individual users. Needs logic updating to be functional, ie. dealing with PageElements without liveevents and other edge cases
- Context-specific comments in code with comments and questions. To be removed before final version.
Currently working on:
- fixing issue where EnumeratedProgress is not keeping track of the exact LiveEvent. Current approach is to add an extra field in the EnumeratedProgress model and use that.
- Ensuring the delete method changes the status of the LiveEvent and doesn't delete it
- Correct URL locations
Added soft delete to LiveEvents, updates to attendee retrieval logic and more.

Notes:
- Need further clarification on URL patterns.
- Not sure if a soft delete is required for LiveEvent attendance.
- A few comments in there, to be removed before merge.
Copy link
Contributor

@smirolo smirolo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is just really the 'live_event_rank' stored in the extra field that require some explanation.

@@ -201,7 +204,7 @@ def post(self, request, *args, **kwargs):
return api_response.Response(serializer.data, status=status_code)


class LiveEventAttendanceAPIView(EnumeratedProgressRetrieveAPIView):
class LiveEventAttendanceAPIView(EnumeratedProgressRetrieveAPIView, DestroyAPIView):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that the inheritance should be (DestroyModelMixin, EnumeratedProgressRetrieveAPIView) otherwise we have two base View classes.

event_rank_kwarg = 'event_rank'

def get_object(self):
return self.progress

def get_serializer_class(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole function might be removed and serializer_class = ... added if necessary.

progress = self.progress
live_event_rank = get_extra(progress, "live_event_rank")

if live_event_rank != self.kwargs.get(self.event_rank_kwarg):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this code. Why is there a live_event_rank in progress.extra? Couldn't the code just be something like get_object_or_404(self.progress.live_events, rank=self.kwargs.get(self.event_rank_kwarg))`?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea here was to check that we're retrieving the correct EnumeratedProgress object, ie. one whose 'live_event_rank' in the extra field matches the live event rank in the URL, if it exists.

For example, one page element has 2 live events(ranks 1 and 2).
We access the URL to retrieve it, api_mark_attendance(args=page element, live event(rank=1))
If we retrieve the progress for that page element, this logic here returns it only if the enumeratedprogress's extra field contains live event(rank=1), meaning progress exists for that specific live event, and wouldn't return anything for api_mark_attendance(args=page element, live event(rank=2)).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Please add a comment along those lines where the EnumeratedProgress.extra field is updated, and make sure if we try to change the value of EnumeratedProgress.extra['live_event_rank'] after it has been set once, there is some kind of warning/error being raised. Thank you!

progress = self.get_object()
element = progress.step
live_event = LiveEvent.objects.filter(element=element.content).first()
live_event = get_object_or_404(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the code I would have expected in previous comment.

progress.viewing_duration <= element.min_viewing_duration):
progress.viewing_duration = element.min_viewing_duration
progress.save()
if live_event.scheduled_at >= datetime_or_now():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Typically prefer to add a statement at_time = datetime_or_now() at the entry point of the method, then use at_time everywhere else. This way we only have one 'now' in the whole HTTP request.

@@ -314,12 +317,18 @@ class EnumeratedProgressMixin(SequenceProgressMixin):

rank_url_kwarg = 'rank'

@property
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great refactor!

location = models.URLField(_("URL to the calendar event"), max_length=2083)
max_attendees = models.IntegerField(default=0)
status = models.CharField(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put the choices as constant defined at the class level. See definition of FORMAT_CHOICES as example.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant write code like:

   CANCELED = 'Canceled'
   SCHEDULED = 'Scheduled'

   EVENT_STATUS_CHOICES = (
     (SCHEDULED, 'Scheduled'),
     (CANCELED, 'Canceled')
   )

such that you write live_event.status = LiveEvent.CANCELED

The idea is that using a named "constant", it is a lot easier to maintain the code than if strings are used all over the place. Ideally you would define SCHEDULED = 0 and status = models.SmallPositiveInteger to speed up SQL queries in the database.

help_text=_("Username of the attendee"))

class Meta(EnumeratedProgressSerializer.Meta):
fields = ('user', 'content', 'viewing_duration', 'min_viewing_duration')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not fields = EnumeratedProgressSerializer.fields + ('user', ...?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it would show the 'rank' repeatedly for each EnumeratedProgress, even though the rank is provided in the URL and therefore it would be the same for each item. And also moved the user field in the front for easier readability.

fields = ('user', 'content', 'viewing_duration', 'min_viewing_duration')
read_only_fields = EnumeratedProgressSerializer.Meta.read_only_fields + ('user',)

def get_user(self, obj):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to return a serialized User here at some point so it is easier for the UI to display the full name of the attendee. It has not yet been done in other parts of the API (ex: Comment serializer) so this most likely should be note/issue for a future refactoring. Good to keep consistency with existing APIs for now.

@@ -71,16 +71,20 @@ def get_context_data(self, **kwargs):
class SequencePageElementView(EnumeratedProgressMixin, TemplateView):

template_name = 'pages/app/sequences/pageelement.html'

# is_live_event and is_certificate flags not being added
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this comment mean, an issue to be fixed or an implementation note to be aware of?

location = models.URLField(_("URL to the calendar event"), max_length=2083)
max_attendees = models.IntegerField(default=0)
status = models.CharField(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant write code like:

   CANCELED = 'Canceled'
   SCHEDULED = 'Scheduled'

   EVENT_STATUS_CHOICES = (
     (SCHEDULED, 'Scheduled'),
     (CANCELED, 'Canceled')
   )

such that you write live_event.status = LiveEvent.CANCELED

The idea is that using a named "constant", it is a lot easier to maintain the code than if strings are used all over the place. Ideally you would define SCHEDULED = 0 and status = models.SmallPositiveInteger to speed up SQL queries in the database.

@Deep-Chill Deep-Chill changed the title [Work-in-Progress] Scheduling of Live Events(#77) Scheduling of Live Events(#77) Feb 20, 2024
Uses the set_extra method. It initializes extra as a dictionary if it doesn't exist.

An alternative could be to initialize extra as and empty string if a progress object is created in `Mixins.py` on `line 333`.
Copy link
Contributor

@smirolo smirolo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor optimization regression within the definition of set_extra.

pages/helpers.py Outdated
# is to set it to some value.
obj.extra = {}
obj.extra.update({attr_name: attr_value})
obj.extra = json.dumps(obj.extra)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want to serialize the extra field here (i.e. json.dumps) since the whole idea of get_extra is to unserialize it only once. Serialization should happen automatically on obj.save().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants