def get_prioritized(queryset, request): # Prefetch related queryset = EventSerializer.setup_eager_loading(queryset) # Get profile if authenticated profile = None if request.user.is_authenticated and hasattr(request.user, 'profile'): profile = request.user.profile # Iterate all events for event in queryset: event.weight = EventPrioritizer(event, profile).compute().weight return sorted(queryset, key=lambda event: (event.weight, event.start_time), reverse=True)
def get_prioritized(queryset, request): now = timezone.now() # Prefetch related queryset = EventSerializer.setup_eager_loading(queryset) # Prefetch followed bodies followed_bodies = None if request.user.is_authenticated and hasattr(request.user, 'profile'): profile = request.user.profile followed_bodies = profile.followed_bodies.all() # Iterate all events for event in queryset: event.weight = BASE # Get difference in days end_time_diff = (event.end_time - now).total_seconds() / 86400 start_time_diff = (event.start_time - now).total_seconds() / 86400 start_time_factor = math.exp((-(start_time_diff / TIME_SD)**2)) # Apply exponential to and penalise finished events if event.end_time < now: event.weight -= FINISHED_PENALTY end_time_factor = math.exp(-abs(end_time_diff) / TIME_L_END) start_time_factor *= end_time_factor start_time_points = WEIGHT_START_TIME * start_time_factor event.weight += int(start_time_points) if followed_bodies: body_bonus = 0 for body in event.bodies.all(): if body_bonus >= BODY_BONUS_MAX: break if body in followed_bodies: body_bonus += int(BODY_FOLLOWING_BONUS + (TIME_DEP_BODY_BONUS * start_time_factor)) event.weight += body_bonus return sorted(queryset, key=lambda event: (-event.weight, event.start_time))
def get_prioritized(queryset, request): now = timezone.now() # Prefetch related queryset = EventSerializer.setup_eager_loading(queryset) # Prefetch followed bodies followed_bodies = None profile = None if request.user.is_authenticated and hasattr(request.user, 'profile'): profile = request.user.profile followed_bodies = profile.followed_bodies.all() # Iterate all events for event in queryset: event.weight = BASE # Get difference in days end_time_diff = (event.end_time - now).total_seconds() / 86400 start_time_diff = (event.start_time - now).total_seconds() / 86400 start_time_factor = math.exp((-(start_time_diff / TIME_SD)**2)) # Event Length penalty event_length = (event.end_time - event.start_time).total_seconds() # factor_b is the number of days as a float factor_b = event_length / 86400 length_penalty = 1 / (1 + TIME_PENALTY_FACTOR * factor_b) # Apply exponential to and penalise finished events if event.end_time < now: event.weight -= FINISHED_PENALTY end_time_factor = math.exp(-abs(end_time_diff) / TIME_L_END) start_time_factor *= end_time_factor start_time_points = WEIGHT_START_TIME * start_time_factor event.weight += int(start_time_points) # Penalize for not being tagged categories_satisfy = [] categories = [] for tag in event.user_tags.all(): if tag.category not in categories: categories.append(tag.category) if tag.category not in categories_satisfy and tag.match(profile): categories_satisfy.append(tag.category) event.weight -= int((len(categories) - len(categories_satisfy)) * NOT_TAG_TARGET_PENALTY) # Grant bonus to followed bodies if followed_bodies: body_bonus = 0 for body in event.bodies.all(): if body_bonus >= BODY_BONUS_MAX: break if body in followed_bodies: body_bonus += int(BODY_FOLLOWING_BONUS + (TIME_DEP_BODY_BONUS * start_time_factor)) event.weight += body_bonus # Apply Length Penalty event.weight *= length_penalty return sorted(queryset, key=lambda event: (event.weight, event.start_time), reverse=True)
class EventViewSet(viewsets.ModelViewSet): # pylint: disable=too-many-ancestors """Event""" queryset = Event.objects.all() queryset = EventSerializer.setup_eager_loading(queryset) serializer_class = EventFullSerializer def get_serializer_context(self): return {'request': self.request} def retrieve(self, request, pk): """Get Event. Get by `uuid` or `str_id`""" event = self.get_event(pk) serialized = EventFullSerializer(event, context={ 'request': request }).data return Response(serialized) def list(self, request): #pylint: disable=unused-argument """List Events. List fresh events prioritized for the current user.""" # Check for date filtered query params start = request.GET.get('start') end = request.GET.get('end') if start is not None and end is not None: # Try date-filtered if we have the params queryset = get_prioritized( self.queryset.filter(start_time__range=(start, end)), request) else: # Respond with recent events queryset = get_fresh_prioritized_events(self.queryset, request) serializer = EventSerializer(queryset, many=True, context={'request': request}) data = serializer.data return Response({'count': len(data), 'data': data}) @login_required_ajax def create(self, request): """Create Event. Needs `AddE` permission for each body to be associated.""" # Prevent events without any body if 'bodies_id' not in request.data or not request.data['bodies_id']: return forbidden_no_privileges() # Check privileges for all bodies if all([ user_has_privilege(request.user.profile, id, 'AddE') for id in request.data['bodies_id'] ]): # Fill in ids of venues request.data['venue_ids'] = create_unreusable_locations( request.data['venue_names']) return super().create(request) return forbidden_no_privileges() @login_required_ajax def update(self, request, pk): """Update Event. Needs BodyRole with `UpdE` for at least one associated body. Disassociating bodies from the event requires the `DelE` permission and associating needs `AddE`""" # Prevent events without any body if 'bodies_id' not in request.data or not request.data['bodies_id']: return forbidden_no_privileges() # Get difference in bodies event = self.get_event(pk) old_bodies_id = [str(x.id) for x in event.bodies.all()] new_bodies_id = request.data['bodies_id'] added_bodies = diff_set(new_bodies_id, old_bodies_id) removed_bodies = diff_set(old_bodies_id, new_bodies_id) # Check if user can add events for new bodies can_add_events = all([ user_has_privilege(request.user.profile, id, 'AddE') for id in added_bodies ]) # Check if user can remove events for removed can_del_events = all([ user_has_privilege(request.user.profile, id, 'DelE') for id in removed_bodies ]) # Check if the user can update event for any of the old bodies can_update = any([ user_has_privilege(request.user.profile, id, 'UpdE') for id in old_bodies_id ]) if can_add_events and can_del_events and can_update: # Create added unreusable venues, unlink deleted ones old_venue_names = [x.name for x in event.venues.all()] new_venue_names = request.data['venue_names'] added_venues = diff_set(new_venue_names, old_venue_names) common_venues = list( set(old_venue_names).intersection(new_venue_names)) common_venue_ids = [ str(x.id) for x in event.venues.filter(name__in=common_venues) ] added_venue_ids = create_unreusable_locations(added_venues) request.data['venue_ids'] = added_venue_ids + common_venue_ids return super().update(request, pk) return forbidden_no_privileges() @login_required_ajax def destroy(self, request, pk): """Delete Event. Needs `DelE` permission for all associated bodies.""" event = self.get_event(pk) if all([ user_has_privilege(request.user.profile, str(body.id), 'DelE') for body in event.bodies.all() ]): return super().destroy(request, pk) return forbidden_no_privileges() def get_event(self, pk): """Get an event from pk uuid or strid.""" try: UUID(pk, version=4) return get_object_or_404(self.queryset, id=pk) except ValueError: return get_object_or_404(self.queryset, str_id=pk)
class EventViewSet(viewsets.ModelViewSet): """Event""" queryset = Event.objects.all() queryset = EventSerializer.setup_eager_loading(queryset) serializer_class = EventFullSerializer def get_serializer_context(self): return {'request': self.request} def retrieve(self, request, pk): """Get Event. Get by `uuid` or `str_id`""" event = self.get_event(pk) serialized = EventFullSerializer(event, context={'request': request}).data return Response(serialized) def list(self, request): """List Events. List fresh events prioritized for the current user.""" # Check for date filtered query params start = request.GET.get('start') end = request.GET.get('end') if start is not None and end is not None: # Try date-filtered if we have the params queryset = get_prioritized(self.queryset.filter( start_time__range=(start, end)), request) else: # Respond with recent events queryset = get_fresh_prioritized_events(self.queryset, request) serializer = EventSerializer(queryset, many=True, context={'request': request}) data = serializer.data return Response({'count': len(data), 'data': data}) @login_required_ajax def create(self, request): """Create Event. Needs `AddE` permission for each body to be associated.""" # Prevent events without any body if 'bodies_id' not in request.data or not request.data['bodies_id']: return forbidden_no_privileges() # Check privileges for all bodies if all([user_has_privilege(request.user.profile, id, 'AddE') for id in request.data['bodies_id']]): # Fill in ids of venues request.data['venue_ids'] = create_unreusable_locations(request.data['venue_names']) return super().create(request) return forbidden_no_privileges() @login_required_ajax def update(self, request, pk): """Update Event. Needs BodyRole with `UpdE` for at least one associated body. Disassociating bodies from the event requires the `DelE` permission and associating needs `AddE`""" # Prevent events without any body if 'bodies_id' not in request.data or not request.data['bodies_id']: return forbidden_no_privileges() # Get the event currently in database event = self.get_event(pk) # Check if difference in bodies is valid if not can_update_bodies(request.data['bodies_id'], event, request.user.profile): return forbidden_no_privileges() # Create added unreusable venues, unlink deleted ones request.data['venue_ids'] = get_update_venue_ids(request.data['venue_names'], event) return super().update(request, pk) @login_required_ajax def destroy(self, request, pk): """Delete Event. Needs `DelE` permission for all associated bodies.""" event = self.get_event(pk) if all([user_has_privilege(request.user.profile, str(body.id), 'DelE') for body in event.bodies.all()]): return super().destroy(request, pk) return forbidden_no_privileges() def get_event(self, pk): """Get an event from pk uuid or strid.""" try: UUID(pk, version=4) return get_object_or_404(self.queryset, id=pk) except ValueError: return get_object_or_404(self.queryset, str_id=pk)