class MembershipStatusesView(View): def post(self, request, *args, **kwargs): """ Return a mapping of participant IDs to membership statuses. """ postdata = json.loads(self.request.body) par_pks = postdata.get('participant_ids') if not isinstance(par_pks, list): return JsonResponse({'message': 'Bad request'}, status=400) # Span databases to map from participants -> users -> email addresses participants = models.Participant.objects.filter(pk__in=par_pks) user_to_par = dict(participants.values_list('user_id', 'pk')) email_addresses = EmailAddress.objects.filter(user_id__in=user_to_par) email_to_user = dict(email_addresses.values_list('email', 'user_id')) # Gives email -> membership info for all matches matches = geardb_utils.matching_memberships(email_to_user) # Default to blank memberships in case not found no_membership = geardb_utils.repr_blank_membership() participant_memberships = {pk: no_membership for pk in par_pks} # Update participants where matching membership information was found for email, membership in matches.iteritems(): par_pk = user_to_par[email_to_user[email]] # We might overwrite a previous membership record, but that will # only happen if the user has memberships under 2+ emails # (Older memberships come first, so this will safely yield the newest) participant_memberships[par_pk] = membership return JsonResponse({'memberships': participant_memberships}) @method_decorator(group_required('leaders')) def dispatch(self, request, *args, **kwargs): return super(MembershipStatusesView, self).dispatch(request, *args, **kwargs)
class MembershipStatsView(TemplateView): template_name = 'stats/membership.html' @method_decorator(group_required('leaders')) def dispatch(self, request, *args, **kwargs): # TODO: Restrict to BOD only return super().dispatch(request, *args, **kwargs)
class CreateTripView(CreateView): model = models.Trip form_class = forms.TripForm template_name = 'trips/create.html' def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['initial'] = kwargs.get('initial', {}) if not self.request.user.is_superuser: allowed_programs = list(self.request.participant.allowed_programs) kwargs['allowed_programs'] = allowed_programs if is_currently_iap( ) and enums.Program.WINTER_SCHOOL in allowed_programs: kwargs['initial'][ 'program'] = enums.Program.WINTER_SCHOOL.value else: # The first program may not be open to the leader. # We restrict choices, so ensure leader can lead this program. allowed_program = next(iter(allowed_programs)) kwargs['initial']['program'] = allowed_program.value return kwargs def get_success_url(self): return reverse('view_trip', args=(self.object.pk, )) def get_initial(self): """Default with trip creator among leaders.""" initial = super().get_initial().copy() # It's possible for WSC to create trips while not being a leader if perm_utils.is_leader(self.request.user): initial['leaders'] = [self.request.participant] return initial def form_valid(self, form): """After is_valid(), assign creator from User, add empty waitlist.""" creator = self.request.participant trip = form.save(commit=False) trip.creator = creator trip.last_updated_by = creator trip.activity = trip.get_legacy_activity() return super().form_valid(form) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['is_currently_iap'] = is_currently_iap() # There is separate logic for determining if we allow choosing the WS program. # Rather than duplicate that logic here, just see if it's a selectable choice. form: forms.TripForm = context['form'] context['can_select_ws_program'] = any( enums.Program(value) == enums.Program.WINTER_SCHOOL for category, choices in form.fields['program'].choices for value, label in choices) return context @method_decorator(group_required('leaders')) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs)
class RawMembershipStatsView(View): def get(self, request, *args, **kwargs): return JsonResponse( {'members': list(geardb_utils.membership_information().values())}) @method_decorator(group_required('leaders')) def dispatch(self, request, *args, **kwargs): # TODO: Restrict to BOD only return super().dispatch(request, *args, **kwargs)
class LeaderSignUpView(BaseSignUpView): model = models.LeaderSignUp form_class = forms.LeaderSignUpForm def get_errors(self, signup): errors = super().get_errors(signup) if not signup.participant.can_lead(signup.trip.activity): errors.append("Can't lead {} trips!".format(signup.trip.activity)) return errors @method_decorator(group_required('leaders')) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs)
class WinterSchoolSettingsView(CreateView): form_class = forms.WinterSchoolSettingsForm template_name = 'chair/settings.html' def get_form_kwargs(self): """ Load existing settings. """ kwargs = super().get_form_kwargs() kwargs['instance'] = models.WinterSchoolSettings.load() return kwargs def get_success_url(self): messages.success(self.request, "Updated Winter School settings!") return reverse('ws_settings') @method_decorator(group_required('WSC')) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs)
class ParticipantLookupView(TemplateView, FormView): template_name = 'participants/view.html' form_class = forms.ParticipantLookupForm def get_context_data(self, **kwargs): context = super().get_context_data() context['user_viewing'] = False context['lookup_form'] = self.get_form(self.form_class) return context def form_valid(self, form): participant = form.cleaned_data['participant'] return redirect(reverse('view_participant', args=(participant.id, ))) @method_decorator(group_required('leaders')) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs)
class CreateTripView(CreateView): model = models.Trip form_class = forms.TripForm template_name = 'trips/create.html' def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['initial'] = kwargs.get('initial', {}) if not self.request.user.is_superuser: allowed_programs = list(self.request.participant.allowed_programs) kwargs['allowed_programs'] = allowed_programs if is_currently_iap( ) and enums.Program.WINTER_SCHOOL in allowed_programs: kwargs['initial'][ 'program'] = enums.Program.WINTER_SCHOOL.value else: # The first program may not be open to the leader. # We restrict choices, so ensure leader can lead this program. allowed_program = next(iter(allowed_programs)) kwargs['initial']['program'] = allowed_program.value return kwargs def get_success_url(self): return reverse('view_trip', args=(self.object.pk, )) def get_initial(self): """ Default with trip creator among leaders. """ initial = super().get_initial().copy() # It's possible for WSC to create trips while not being a leader if perm_utils.is_leader(self.request.user): initial['leaders'] = [self.request.participant] return initial def form_valid(self, form): """ After is_valid(), assign creator from User, add empty waitlist. """ creator = self.request.participant trip = form.save(commit=False) trip.creator = creator trip.activity = trip.get_legacy_activity() return super().form_valid(form) @method_decorator(group_required('leaders')) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs)
class AllTripsMedicalView(ListView): model = models.Trip template_name = 'trips/all/medical.html' context_object_name = 'trips' def get_queryset(self): trips = super().get_queryset().order_by('trip_date') today = local_date() return trips.filter(trip_date__gte=today) def get_context_data(self, **kwargs): context_data = super().get_context_data(**kwargs) context_data['wimps'] = wimp.active_wimps() return context_data @method_decorator(group_required('WSC', 'WIMP')) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs)
class CreateTripView(CreateView): model = models.Trip form_class = forms.TripForm template_name = 'trips/create.html' def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['initial'] = kwargs.get('initial', {}) if not self.request.user.is_superuser: allowed_activities = self.request.participant.allowed_activities kwargs['allowed_activities'] = allowed_activities if is_winter_school() and 'winter_school' in allowed_activities: kwargs['initial']['activity'] = 'winter_school' else: # The first activity may not be open to the leader. # We restrict choices, so ensure leader can lead this activity. kwargs['initial']['activity'] = kwargs['allowed_activities'][0] return kwargs def get_success_url(self): return reverse('view_trip', args=(self.object.pk, )) def get_initial(self): """ Default with trip creator among leaders. """ initial = super().get_initial().copy() # It's possible for WSC to create trips while not being a leader if perm_utils.is_leader(self.request.user): initial['leaders'] = [self.request.participant] return initial def form_valid(self, form): """ After is_valid(), assign creator from User, add empty waitlist. """ creator = self.request.participant trip = form.save(commit=False) trip.creator = creator return super().form_valid(form) @method_decorator(group_required('leaders')) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs)
class AllLeadersView(ListView): model = models.Participant context_object_name = 'leaders' template_name = 'leaders/all.html' def get_queryset(self): """ Returns all leaders with active ratings. """ return models.Participant.leaders.get_queryset() def get_context_data(self, **kwargs): context_data = super().get_context_data(**kwargs) context_data['activities'] = [(activity_enum.value, activity_enum.label) for activity_enum in enums.Activity if activity_enum != enums.Activity.CABIN] return context_data @method_decorator(group_required('leaders')) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs)
class AllLeadersView(ListView): model = models.Participant context_object_name = 'leaders' template_name = 'leaders/all.html' def get_queryset(self): """ Returns all leaders with active ratings. """ return models.Participant.leaders.get_queryset() def get_context_data(self, **kwargs): context_data = super().get_context_data(**kwargs) closed_activities = models.LeaderRating.CLOSED_ACTIVITY_CHOICES activities = [(val, label) for (val, label) in closed_activities if val != 'cabin'] context_data['activities'] = activities return context_data @method_decorator(group_required('leaders')) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs)
class AllTripsMedicalView(ListView, TripMedical): model = models.Trip template_name = 'trips/all/medical.html' context_object_name = 'trips' def get_queryset(self): trips = super().get_queryset().order_by('trip_date') today = local_date() return trips.filter(trip_date__gte=today) def get_context_data(self, **kwargs): context_data = super().get_context_data(**kwargs) by_trip = (self.get_trip_info(trip) for trip in self.get_queryset()) all_trips = [(c['trip'], c['participants'], c['trip_leaders'], c['cars'], c['info_form']) for c in by_trip] context_data['all_trips'] = all_trips return context_data @method_decorator(group_required('WSC', 'WIMP')) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs)
class RawMembershipStatsView(View): @staticmethod def _all_members_info() -> Iterator[Dict[str, Union[str, int]]]: for info in geardb_utils.membership_information().values(): flat_info: Dict[str, Union[str, int]] = { 'last_known_affiliation': info.last_known_affiliation, 'num_rentals': info.num_rentals, } # TODO (Python 3.11, PEP 655): Could TypedDict w/ NotRequired fields # Alternatively, could just fix the janky JS to handle. if info.trips_information: flat_info.update(info.trips_information._asdict()) yield flat_info def get(self, request, *args, **kwargs): return JsonResponse({'members': list(self._all_members_info())}) @method_decorator(group_required('leaders')) def dispatch(self, request, *args, **kwargs): # TODO: Restrict to BOD only return super().dispatch(request, *args, **kwargs)
class LeaderSignUpView(BaseSignUpView): model = models.LeaderSignUp form_class = forms.LeaderSignUpForm def get_errors(self, signup): errors = super().get_errors(signup) trip = signup.trip if not signup.participant.can_lead(trip.program_enum): errors.append(f"Can't lead {trip.get_program_display()} trips!") if not trip.allow_leader_signups: errors.append("Trip is not currently accepting leader signups.") if models.LeaderSignUp.objects.filter( trip=signup.trip, participant=signup.participant ).exists(): errors.append( "Already signed up as a leader on this trip! " "Contact the trip organizer to be re-added." ) return errors @method_decorator(group_required('leaders')) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs)
class ReviewTripView(DetailView): model = models.Trip template_name = 'trips/review.html' success_msg = "Thanks for your feedback!" object: models.Trip @property def posted_feedback(self): """Convert named fields of POST data to participant -> feedback mapping. If the form data was garbled (intentionally or otherwise), this method will raise ValueError or TypeError (on either 'split' or `int`) """ for key, comments in self.request.POST.items(): if not (key.startswith("par_") or key.startswith("flake_")): continue feedback_type, par_pk = key.split('_') showed_up = feedback_type == 'par' yield int(par_pk), comments.strip(), showed_up def post(self, request, *args, **kwargs): """Create or update all feedback passed along in form data.""" trip = self.object = self.get_object() if trip.feedback_window_passed: messages.warning( self.request, "Trip feedback window has passed. Feedback may not be updated.", ) return redirect(reverse('review_trip', args=(trip.pk, ))) leader = self.request.participant try: posted_feedback = list(self.posted_feedback) except (TypeError, ValueError): # This should never happen, but look at doing this more nicely? return HttpResponseBadRequest("Invalid form contents") # Create or update feedback for all feedback passed in the form existing_feedback = { feedback.participant.pk: feedback for feedback in self.get_existing_feedback() } for pk, comments, showed_up in posted_feedback: blank_feedback = showed_up and not comments existing = existing_feedback.get(pk) if existing and blank_feedback: existing.delete() continue if existing is not None: feedback = existing elif blank_feedback: continue # Don't create new feedback saying nothing useful else: kwargs = { 'leader': leader, 'trip': trip, 'participant': models.Participant.objects.get(pk=pk), } feedback = models.Feedback.objects.create(**kwargs) feedback.comments = comments feedback.showed_up = showed_up feedback.save() messages.success(self.request, self.success_msg) return redirect(reverse('home')) @property def trip_participants(self): accepted_signups = self.object.signup_set.filter(on_trip=True) accepted_signups = accepted_signups.select_related('participant') return [signup.participant for signup in accepted_signups] def get_existing_feedback(self): leader = self.request.participant return models.Feedback.everything.filter(trip=self.object, leader=leader) @property def feedback_list(self): feedback = self.get_existing_feedback() par_comments = dict(feedback.values_list('participant__pk', 'comments')) return [(par, par_comments.get(par.pk, '')) for par in self.trip_participants] def get_context_data(self, **kwargs): today = local_date() trip = self.object = self.get_object() return { "trip": trip, "feedback_window_passed": trip.feedback_window_passed, "trip_completed": today >= trip.trip_date, "feedback_required": trip.program_enum == enums.Program.WINTER_SCHOOL, "feedback_list": self.feedback_list, } @method_decorator(group_required('leaders')) def dispatch(self, request, *args, **kwargs): trip = self.get_object() if not perm_utils.leader_on_trip(request.participant, trip, False): return render(request, 'not_your_trip.html', {'trip': trip}) return super().dispatch(request, *args, **kwargs)
name='help-rentals', ), url( r'^help/participants/weather/$', TemplateView.as_view(template_name='help/participants/weather.html'), name='help-weather', ), url( r'^help/participants/maps/$', TemplateView.as_view(template_name='help/participants/maps.html'), name='help-maps', ), # Trip Logistics (for leaders) url( r'^help/leaders/trip_admin/$', group_required('leaders', 'WSC')(TemplateView.as_view( template_name='help/leaders/trip_admin.html')), name='help-trip_admin', ), url( r'^help/leaders/checklist/$', group_required('leaders', 'WSC')( TemplateView.as_view(template_name='help/leaders/checklist.html')), name='help-checklist', ), url( r'^help/leaders/example_emails/$', group_required('leaders', 'WSC')(TemplateView.as_view( template_name='help/leaders/example_emails.html')), name='help-example_emails', ), url(
), url( r'^help/participants/weather/$', TemplateView.as_view(template_name='help/participants/weather.html'), name='help-weather', ), url( r'^help/participants/maps/$', TemplateView.as_view(template_name='help/participants/maps.html'), name='help-maps', ), # Trip Logistics (for leaders) url( r'^help/leaders/trip_admin/$', group_required('leaders', 'WSC')( TemplateView.as_view(template_name='help/leaders/trip_admin.html') ), name='help-trip_admin', ), url( r'^help/leaders/checklist/$', group_required('leaders', 'WSC')( TemplateView.as_view(template_name='help/leaders/checklist.html') ), name='help-checklist', ), url( r'^help/leaders/example_emails/$', group_required('leaders', 'WSC')( TemplateView.as_view(template_name='help/leaders/example_emails.html') ),
url(r'^help/participants/personal_info/$', TemplateView.as_view( template_name='help/participants/personal_info.html'), name='help-personal_info'), url(r'^help/participants/lottery/$', TemplateView.as_view(template_name='help/participants/lottery.html'), name='help-lottery'), url(r'^help/participants/signups/$', TemplateView.as_view(template_name='help/participants/signups.html'), name='help-signups'), url(r'^help/participants/leading_trips/$', TemplateView.as_view( template_name='help/participants/leading_trips.html'), name='help-leading_trips'), url(r'^help/leaders/feedback/$', group_required('leaders', 'WSC')( TemplateView.as_view(template_name='help/leaders/feedback.html')), name='help-feedback'), url(r'^help/leaders/trip_admin/$', group_required('leaders', 'WSC')(TemplateView.as_view( template_name='help/leaders/trip_admin.html')), name='help-trip_admin'), url(r'^help/wsc/wsc/$', group_required('WSC')( TemplateView.as_view(template_name='help/wsc/wsc.html')), name='help-wsc'), # API (must have account in system) url(r'^trips/(?P<pk>\d+)/overflow.json$', api_views.CheckTripOverflowView.as_view(), name='json-check_trip_overflow'), url(r'^leaders.json/(?:(?P<activity>.+)/)?$',