Beispiel #1
0
class MembershipFactory(DjangoModelFactory):
    class Meta:
        model = models.Membership

    membership_expires = factory.LazyAttribute(
        lambda _obj: date_utils.local_date() + timedelta(days=365))
    waiver_expires = factory.LazyAttribute(
        lambda _obj: date_utils.local_date() + timedelta(days=365))
Beispiel #2
0
    def setUp(cls):
        """ Use some convenience dates.

        All we care about when testing is that the dates are in the past or the
        future. Create two of each so we can be sure the right date was put
        into the right part of the response.
        """
        cls.future = local_date() + timedelta(days=1)
        cls.future2 = local_date() + timedelta(days=2)
        cls.past = local_date() - timedelta(days=1)
        cls.past2 = local_date() - timedelta(days=2)
Beispiel #3
0
    def setUp(cls):
        """ Use some convenience dates.

        All we care about when testing is that the dates are in the past or the
        future. Create two of each so we can be sure the right date was put
        into the right part of the response.
        """
        cls.future = local_date() + timedelta(days=1)
        cls.future2 = local_date() + timedelta(days=2)
        cls.past = local_date() - timedelta(days=1)
        cls.past2 = local_date() - timedelta(days=2)
Beispiel #4
0
    def get_context_data(self, **kwargs):
        """Sort trips into past and present trips."""
        context = super().get_context_data(**kwargs)
        trips = context[self.context_object_name]
        today = local_date()
        context['today'] = today

        on_or_after_date, context[
            'date_invalid'] = self._optionally_filter_from_args()
        if on_or_after_date:
            context['on_or_after_date'] = on_or_after_date
            trips = trips.filter(trip_date__gte=on_or_after_date)

        # Get approximately one year prior for use in paginating back in time.
        # (need not be exact/handle leap years)
        context['one_year_prior'] = (on_or_after_date
                                     or today) - timedelta(days=365)

        # By default, just show upcoming trips.
        context['current_trips'] = trips.filter(trip_date__gte=today)
        # However, if we've explicitly opted in to showing past trips, include them
        if self.include_past_trips or on_or_after_date:
            context['past_trips'] = trips.filter(trip_date__lt=today)
            if not on_or_after_date:
                # We're on the special 'all trips' view, so there are no add'l previous trips
                context['one_year_prior'] = None
        return context
Beispiel #5
0
def bumped_from_waitlist(sender, instance, using, **kwargs):
    """ Notify previously waitlisted participants if they're on trip.

    If the trip happened in the past, it's safe to assume it's just the
    leader modifying the participant list. Don't notify in this case.

    When a waitlist signup is deleted, it generally means the participant
    is on the trip. The only other case is a complete trip deletion,
    or a manual signup deletion. In either case, these actions are only
    triggered by admins. Notifications will only be sent out if the
    corresponding signup is now on the trip.
    """
    wl_signup, signup = instance, instance.signup
    if getattr(signup, 'skip_signals', False):
        return

    if not wl_signup.signup.on_trip:
        return  # Could just be deleted, don't want to falsely notify
    trip = signup.trip
    if trip.trip_date < local_date():
        return  # Trip was yesterday or earlier, no point notifying
    trip_link = get_trip_link(trip)
    return  # TODO: There are issues with this going out incorrectly
    send_mail("You're signed up for {}".format(trip),
              "You're on {}! If you can't make it, please remove yourself "
              "from the trip so others can join.".format(trip_link),
              trip.creator.email, [signup.participant.email],
              fail_silently=True)
Beispiel #6
0
def trip_info(context, trip, show_participants_if_no_itinerary=False):
    participant = context['viewing_participant']

    # After a sufficiently long waiting period, hide medical information
    # (We could need medical info a day or two after a trip was due back)
    # Some trips last for multiple days (trip date is Friday, return is Sunday)
    # Because we only record a single trip date, give a few extra days' buffer
    is_old_trip = date_utils.local_date() > (trip.trip_date +
                                             timedelta(days=5))

    return {
        'trip':
        trip,
        'participants': (trip.signed_up_participants.filter(
            signup__on_trip=True).select_related(
                'emergency_info__emergency_contact')),
        'trip_leaders':
        (trip.leaders.select_related('emergency_info__emergency_contact')),
        'cars':
        get_cars(trip),
        'show_participants_if_no_itinerary':
        show_participants_if_no_itinerary,
        'hide_sensitive_info':
        is_old_trip,
        'is_trip_leader':
        perm_utils.leader_on_trip(participant, trip),
    }
Beispiel #7
0
def unapproved_trip_count(activity):
    today = date_utils.local_date()
    return models.Trip.objects.filter(
        trip_date__gte=today,
        activity=activity,
        chair_approved=False
    ).count()
Beispiel #8
0
def format_membership(email, membership_expires, waiver_expires):
    person = repr_blank_membership()
    membership, waiver = person['membership'], person['waiver']
    membership['email'] = email

    for component, expires in [
        (membership, membership_expires),
        (waiver, waiver_expires),
    ]:
        component['expires'] = expires
        component['active'] = bool(expires and expires >= local_date())

    # Generate a human-readable status
    if membership['active']:  # Membership is active and up-to-date
        if not waiver['expires']:
            status = "Missing Waiver"
        elif not waiver['active']:
            status = "Waiver Expired"
        else:
            status = "Active"
    else:
        status = "Missing Membership" if waiver['active'] else "Expired"

    person['status'] = status

    return person
    def wimp(self):
        """ Return the current WIMP, if there is one & it's appropriate to display them. """
        participant = self.object

        # Regardless of time of year, or any upcoming WS trips, admins always see WIMP
        if self.request.user.is_superuser:
            return wimp.current_wimp()

        # If there aren't any upcoming trips (or trips today), don't show WIMP
        # This will ensure that we hide the WIMP when:
        # - It's not Winter School
        # - Winter School just ended, but `is_currently_iap()` can't tell
        # - The weekend just ended, and we don't yet have a new WIMP
        ws_trips = models.Trip.objects.filter(
            program=enums.Program.WINTER_SCHOOL.value,
            trip_date__gte=date_utils.local_date(),
        )
        if not ws_trips:
            return None

        # Participants don't need to know the WIMP, only leaders/chairs do
        if not (participant.can_lead(enums.Program.WINTER_SCHOOL)
                or perm_utils.is_chair(self.request.user,
                                       enums.Activity.WINTER_SCHOOL)):
            return None

        return wimp.current_wimp()
def _feedback_eligible_trips(participant):
    """ Recent completed trips where participants were not given feedback. """
    today = local_date()
    one_month_ago = today - timedelta(days=30)
    recent_trips = participant.trips_led.filter(trip_date__lt=today,
                                                trip_date__gt=one_month_ago)
    return recent_trips.filter(signup__on_trip=True).distinct()
Beispiel #11
0
    def _lecture_info(self, participant, user_viewing: bool) -> Dict[str, bool]:
        """Describe the participant's lecture attendance, if applicable."""
        can_set_attendance = self.can_set_attendance(participant)

        # There are only *two* times of year where it's important to show "yes, you attended"
        # 1. The enrollment period where participants can record attendance (2nd lecture)
        # 2. The first week of WS, after lectures but before weekend trips
        #    (this is when participants may not have recorded attendance correctly)
        #    In later weeks, we'll enforce lecture attendance as part of trip signup.
        show_attendance = date_utils.is_currently_iap() and (
            can_set_attendance or date_utils.ws_lectures_complete()
        )

        if show_attendance:
            attended_lectures = participant.attended_lectures(date_utils.ws_year())

            # We don't need to tell participants "You attended lectures!" later in WS.
            # This is because signup rules enforce lecture attendance *after* week 1.
            if user_viewing and models.Trip.objects.filter(
                program=enums.Program.WINTER_SCHOOL.value,
                trip_date__gte=date_utils.jan_1(),
                trip_date__lt=date_utils.local_date(),
            ):
                show_attendance = False
        else:  # Skip unnecessary db queries
            attended_lectures = False  # Maybe they actually did, but we're not showing.

        return {
            'can_set_attendance': can_set_attendance,
            'show_attendance': show_attendance,
            'attended_lectures': attended_lectures,
        }
Beispiel #12
0
def format_membership(email, membership_expires, waiver_expires):
    person = repr_blank_membership()
    membership, waiver = person['membership'], person['waiver']
    membership['email'] = email

    for component, expires in [
        (membership, membership_expires),
        (waiver, waiver_expires),
    ]:
        component['expires'] = expires
        component['active'] = bool(expires and expires >= local_date())

    # Generate a human-readable status
    if membership['active']:  # Membership is active and up-to-date
        if not waiver['expires']:
            status = "Missing Waiver"
        elif not waiver['active']:
            status = "Waiver Expired"
        else:
            status = "Active"
    else:
        status = "Missing Membership" if waiver['active'] else "Expired"

    person['status'] = status

    return person
    def test_newest_waiver_taken(self, matching_memberships):
        """ If an old membership has an active waiver, use it! """
        one_month_later = local_date() + timedelta(days=30)
        memberships_by_email = {
            '*****@*****.**': {
                'membership': {
                    'expires': date(2011, 1, 1),
                    'active': False,
                    'email': '*****@*****.**',
                },
                'waiver': {
                    'expires': None,
                    'active': False
                },
                'status': 'Expired',
            },
            '*****@*****.**': {
                'membership': {
                    'expires': date(2012, 2, 2),
                    'active': False,
                    'email': '*****@*****.**',
                },
                'waiver': {
                    'expires': None,
                    'active': False
                },
                'status': 'Expired',
            },
            '*****@*****.**': {
                'membership': {
                    'expires': date(2013, 3, 3),
                    'active': False,
                    'email': '*****@*****.**',
                },
                'waiver': {
                    'expires': None,
                    'active': False
                },
                'status': 'Expired',
            },
        }
        matching_memberships.return_value = memberships_by_email

        # All waivers are expired or missing, so we take the newest membership
        self.assertEqual(
            geardb.membership_expiration(list(memberships_by_email)),
            memberships_by_email['*****@*****.**'],
        )

        # Give the middle membership an active waiver, even though it's not the newest
        middle = memberships_by_email['*****@*****.**']
        middle['waiver'].update(expires=one_month_later, active=True)
        middle['status'] = 'Membership Expired'

        # '*****@*****.**' is not the newest membership, but it has an active waiver
        # (and all other memberships do not have an active waiver)
        self.assertEqual(
            geardb.membership_expiration(list(memberships_by_email)),
            memberships_by_email['*****@*****.**'],
        )
Beispiel #14
0
 def __init__(self, participant, runner):
     """
     :param runner: An instance of LotteryRunner
     """
     self.today = local_date()
     parent = super(WinterSchoolParticipantHandler, self)
     parent.__init__(participant, runner, min_drivers=2, allow_pairs=True)
Beispiel #15
0
 def ranked_signups(self):
     today = local_date()
     lotto_signups = Q(participant=self.request.participant,
                       trip__algorithm='lottery',
                       trip__trip_date__gt=today)
     future_signups = models.SignUp.objects.filter(lotto_signups)
     ranked = future_signups.order_by('order', 'time_created')
     return ranked.select_related('trip')
 def _create_application_and_approve():
     application = factories.HikingLeaderApplicationFactory.create(
         # Note that the form logic usually handles this
         year=date_utils.local_date().year, )
     factories.LeaderRatingFactory.create(
         activity=enums.Activity.HIKING.value,
         participant=application.participant)
     return application
Beispiel #17
0
def is_the_wimp(user, participant):
    """Return True if the user has any upcoming WIMP trips."""
    if perm_utils.in_any_group(user, ['WIMP'], allow_superusers=True):
        return True
    if not participant:
        return False
    today = local_date()
    return participant.wimp_trips.filter(trip_date__gte=today).exists()
Beispiel #18
0
    def update_rescinds_approval(self) -> bool:
        trip = self.object
        activity_enum = trip.required_activity_enum()
        if activity_enum is None:
            return False  # No required activity, thus no chair to rescind"

        return (trip.chair_approved and trip.trip_date >= local_date()
                and not perm_utils.is_chair(self.request.user, activity_enum))
 def warn_if_no_ranked_trips(self):
     """ Warn the user if there are future signups, and none are ranked. """
     manager = models.SignUp.objects
     future_signups = manager.filter(participant=self.request.participant,
                                     trip__trip_date__gte=local_date())
     some_trips_ranked = future_signups.filter(order__isnull=False).count()
     if future_signups.count() > 1 and not some_trips_ranked:
         msg = "You haven't " + self.prefs_link("ranked upcoming trips.")
         messages.warning(self.request, msg, extra_tags='safe')
Beispiel #20
0
    def get_context_data(self, **kwargs):
        """ Sort trips into past and present trips. """
        context_data = super().get_context_data(**kwargs)
        trips = context_data[self.context_object_name]

        today = local_date()
        context_data['current_trips'] = trips.filter(trip_date__gte=today)
        context_data['past_trips'] = trips.filter(trip_date__lt=today)
        return context_data
Beispiel #21
0
def send_sao_itineraries():
    """ Email trip itineraries to the Student Activities Office.

    This task should be run daily, so that it will always send SAO
    this information _before_ the trip actually starts.
    """
    tomorrow = date_utils.local_date() + timedelta(days=1)
    trips = models.Trip.objects.filter(trip_date=tomorrow, info__isnull=False)
    for trip in trips.select_related('info').prefetch_related('leaders'):
        send_email_to_funds(trip)
Beispiel #22
0
 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.activity == 'winter_school',
         "feedback_list": self.feedback_list
     }
Beispiel #23
0
 def ranked_signups(self):
     # NOTE: In the future, we may support multi-trip lotteries outside WS
     # For now, though, this tool is only for ranking WS trips
     return (models.SignUp.objects.filter(
         participant=self.request.participant,
         on_trip=False,
         trip__algorithm='lottery',
         trip__program=enums.Program.WINTER_SCHOOL.value,
         trip__trip_date__gt=local_date(),
     ).order_by('order', 'time_created', 'pk').select_related('trip'))
Beispiel #24
0
def simple_trip_list(
    trip_list, max_title_chars=45, max_description_chars=120, collapse_date=False
):
    return {
        'today': date_utils.local_date(),
        'trip_list': trip_list,
        'max_title_chars': max_title_chars,
        'max_description_chars': max_description_chars,
        'collapse_date': collapse_date,
    }
Beispiel #25
0
    def number_trips_led(self, participant):
        """ Return the number of trips the participant has recently led.

        (If we considered all the trips the participant has ever led,
        participants could easily jump the queue every Winter School if they
        just lead a few trips once and then stop).
        """
        last_year = local_date() - timedelta(days=365)
        within_last_year = Q(trip_date__gt=last_year, trip_date__lt=self.today)
        return participant.trips_led.filter(within_last_year).count()
Beispiel #26
0
 def get_queryset(self):
     """Filter to only *upcoming* trips needing approval, sort by those with itinerary!."""
     all_trips = super().get_queryset()
     return (all_trips.filter(
         activity=self.kwargs['activity'],
         trip_date__gte=local_date(),
         chair_approved=False,
     ).select_related('info').prefetch_related(
         'leaders', 'leaders__leaderrating_set').order_by(
             *models.Trip.ordering_for_approval))
Beispiel #27
0
 def get_queryset(self):
     """ Filter to only *upcoming* trips needing approval, sort by those with itinerary!. """
     all_trips = super().get_queryset()
     return (all_trips.filter(
         activity=self.kwargs['activity'],
         trip_date__gte=local_date(),
         chair_approved=False,
     ).select_related('info').prefetch_related('leaders',
                                               'leaders__leaderrating_set')
             # TODO (Django 2): Be more explicit about nulls last
             .order_by('trip_date', 'info'))
Beispiel #28
0
 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,
     }
Beispiel #29
0
def trip_edit_buttons(trip, participant, user, hide_approve=False):
    available_at = date_utils.itinerary_available_at(trip.trip_date)
    return {
        'trip': trip,
        'is_chair': perm_utils.chair_or_admin(user, trip.activity),
        'is_creator': trip.creator == participant,
        'is_trip_leader': perm_utils.leader_on_trip(participant, trip, False),
        'hide_approve': hide_approve,  # Hide approval even if user is a chair
        'itinerary_available_at': available_at,
        'available_today': available_at.date() == date_utils.local_date(),
        'info_form_available': date_utils.local_now() >= available_at,
    }
Beispiel #30
0
def trip_edit_buttons(trip, participant, user, hide_approve=False):
    available_at = date_utils.itinerary_available_at(trip.trip_date)
    return {
        'trip': trip,
        'is_chair': perm_utils.chair_or_admin(user, trip.activity),
        'is_creator': trip.creator == participant,
        'is_trip_leader': perm_utils.leader_on_trip(participant, trip, False),
        'hide_approve': hide_approve,  # Hide approval even if user is a chair
        'itinerary_available_at': available_at,
        'available_today': available_at.date() == date_utils.local_date(),
        'info_form_available': date_utils.local_now() >= available_at
    }
Beispiel #31
0
def leader_signup_is_allowed(trip, participant):
    """ Determine whether or not to display the leader signup form.

    Note: This is not validation - the user's ultimate ability to sign
    up by a leader is determined by the logic in the models and views.
    """
    if not (participant and participant.is_leader):
        return False
    trip_upcoming = local_date() <= trip.trip_date

    return (trip_upcoming and trip.allow_leader_signups
            and participant.can_lead(trip.activity))
Beispiel #32
0
def outstanding_items(emails, rented_on_or_before=None):
    """ Yield all items that are currently checked out to the members.

    This method supports listing items for an individual participant (who may
    have multiple emails/gear accounts) as well as all participants on a trip.
    """
    if not emails:
      return

    # Email capitalization in the database may differ from what users report
    # Map back to the case supplied in arguments for easier mapping
    to_original_case = {email.lower(): email for email in emails}

    cursor = connections['geardb'].cursor()

    rental_date_clause = ''
    if rented_on_or_before:
        rental_date_clause = 'and checkedout <= %(rented_on_or_before)s'

    cursor.execute(
        f'''
        select lower(p.email),
               lower(pe.alternate_email),
               g.id,
               gt.type_name,
               gt.rental_amount,
               date(convert_tz(r.checkedout, '+00:00', '-05:00')) as checkedout
          from rentals          r
               join people      p on p.id = r.person_id
               join gear        g on g.id = r.gear_id
               join gear_types gt on g.type = gt.id
               left join gear_peopleemails pe on p.id = pe.person_id
         where r.returned is null
           {rental_date_clause}
           and (p.email in %(emails)s or pe.alternate_email in %(emails)s)
         group by p.email, pe.alternate_email, g.id, gt.type_name, gt.rental_amount, r.checkedout
        ''', {
            'emails': tuple(emails),
            'rented_on_or_before': rented_on_or_before,
        })

    for main, alternate, gear_id, name, cost, checkedout in cursor.fetchall():
        email = main if main in to_original_case else alternate

        yield {
            'email': to_original_case[email],
            'id': gear_id,
            'name': name,
            'cost': cost,
            'checkedout': checkedout,
            'overdue': (local_date() - checkedout > timedelta(weeks=10)),
        }
Beispiel #33
0
 def ranked_signups(self):
     # NOTE: In the future, we may support multi-trip lotteries outside WS
     # For now, though, this tool is only for ranking WS trips
     return (
         models.SignUp.objects.filter(
             participant=self.request.participant,
             trip__algorithm='lottery',
             trip__activity='winter_school',
             trip__trip_date__gt=local_date(),
         )
         .order_by('order', 'time_created')
         .select_related('trip')
     )
Beispiel #34
0
def simple_trip_list(
    trip_list,
    max_title_chars=45,
    max_description_chars=120,
    collapse_date=False,  # True: Instead of showing the date column, show beneath title
):
    return {
        'today': date_utils.local_date(),
        'trip_list': trip_list,
        'max_title_chars': max_title_chars,
        'max_description_chars': max_description_chars,
        'collapse_date': collapse_date,
    }
Beispiel #35
0
    def test_newest_waiver_taken(self, matching_memberships):
        """ If an old membership has an active waiver, use it! """
        one_month_later = local_date() + timedelta(days=30)
        memberships_by_email = {
            '*****@*****.**': {
                'membership': {
                    'expires': date(2011, 1, 1),
                    'active': False,
                    'email': '*****@*****.**',
                },
                'waiver': {'expires': None, 'active': False},
                'status': 'Expired',
            },
            '*****@*****.**': {
                'membership': {
                    'expires': date(2012, 2, 2),
                    'active': False,
                    'email': '*****@*****.**',
                },
                'waiver': {'expires': None, 'active': False},
                'status': 'Expired',
            },
            '*****@*****.**': {
                'membership': {
                    'expires': date(2013, 3, 3),
                    'active': False,
                    'email': '*****@*****.**',
                },
                'waiver': {'expires': None, 'active': False},
                'status': 'Expired',
            },
        }
        matching_memberships.return_value = memberships_by_email

        # All waivers are expired or missing, so we take the newest membership
        self.assertEqual(
            geardb.membership_expiration(list(memberships_by_email)),
            memberships_by_email['*****@*****.**'],
        )

        # Give the middle membership an active waiver, even though it's not the newest
        middle = memberships_by_email['*****@*****.**']
        middle['waiver'].update(expires=one_month_later, active=True)
        middle['status'] = 'Membership Expired'

        # '*****@*****.**' is not the newest membership, but it has an active waiver
        # (and all other memberships do not have an active waiver)
        self.assertEqual(
            geardb.membership_expiration(list(memberships_by_email)),
            memberships_by_email['*****@*****.**'],
        )
Beispiel #36
0
    def get_context_data(self, **kwargs):
        """ Get a trip info form for display as readonly. """
        trip = self.object
        participant = self.request.participant
        context_data = self.get_trip_info(trip)
        context_data['is_trip_leader'] = perm_utils.leader_on_trip(participant, trip)
        context_data['info_form'] = self.get_info_form(trip)

        # After a sufficiently long waiting period, hide medical information
        # (We could need medical info a day or two after a trip was due back)
        # Some trips last for multiple days (trip date is Friday, return is Sunday)
        # Because we only record a single trip date, give a few extra days' buffer
        is_past_trip = local_date() > (trip.trip_date + timedelta(days=5))
        context_data['hide_sensitive_info'] = is_past_trip
        return context_data
Beispiel #37
0
def send_sao_itineraries():
    """ Email trip itineraries to the Student Activities Office.

    This task should be run daily, so that it will always send SAO
    this information _before_ the trip actually starts.
    """
    tomorrow = date_utils.local_date() + timedelta(days=1)
    trips = models.Trip.objects.filter(trip_date=tomorrow, info__isnull=False)
    logger.info(
        "Sending itineraries for %d trips taking place tomorrow, %s",
        trips.count(),
        tomorrow,
    )
    for trip in trips.select_related('info').prefetch_related('leaders'):
        send_email_to_funds(trip)
Beispiel #38
0
def leader_signup_is_allowed(trip, participant):
    """ Determine whether or not to display the leader signup form.

    Note: This is not validation - the user's ultimate ability to sign
    up by a leader is determined by the logic in the models and views.
    """
    if not (participant and participant.is_leader):
        return False
    trip_upcoming = local_date() <= trip.trip_date

    return (
        trip_upcoming
        and trip.allow_leader_signups
        and participant.can_lead(trip.activity)
    )
Beispiel #39
0
def wimp_trips(participant, user):
    """ Give a quick list of the trips that the participant is a WIMP for. """
    today = date_utils.local_date()
    next_week = today + timedelta(days=7)
    # Use Python to avoid an extra query into groups
    wimp_all = any(g.name == 'WIMP' for g in user.groups.all())

    all_wimp_trips = models.Trip.objects if wimp_all else participant.wimp_trips
    upcoming_trips = all_wimp_trips.filter(
        trip_date__gte=today, trip_date__lte=next_week
    )
    upcoming_trips = upcoming_trips.select_related('info')

    return {
        'can_wimp_all_trips': wimp_all,
        'upcoming_trips': upcoming_trips.order_by('trip_date', 'name'),
    }
    def warn_if_no_ranked_trips(self):
        """ Warn the user if there are future signups, and none are ranked.

        Some participants don't understand the significance of signing up for
        multiple trips: Namely, they miss the fact that there's an easy way to
        set which ones are your favorite! This reminder gives them a quick link
        to let them rank their most preferred trips.
        """
        # Only consider signups for lottery trips in the future
        future_signups = models.SignUp.objects.filter(
            participant=self.request.participant,
            on_trip=False,
            trip__algorithm='lottery',
            trip__trip_date__gte=dateutils.local_date(),
        ).values_list('order', flat=True)
        some_trips_ranked = any(order for order in future_signups)

        if len(future_signups) > 1 and not some_trips_ranked:
            msg = "You haven't " + self.prefs_link("ranked upcoming trips.")
            messages.warning(self.request, msg, extra_tags='safe')
def complain_if_missing_feedback(request):
    """ Create messages if the leader should supply feedback.

    We request that leaders leave feedback on all trips they've led.
    """
    if not ws.utils.perms.is_leader(request.user):
        return

    participant = request.participant

    today = dateutils.local_date()
    one_month_ago = today - timedelta(days=30)

    recent_trips_without_feedback = (
        participant.trips_led.filter(trip_date__lt=today, trip_date__gt=one_month_ago)
        .exclude(feedback__leader=participant)
        .values_list('pk', 'name')
    )

    for trip_pk, name in recent_trips_without_feedback:
        trip_url = reverse('review_trip', args=(trip_pk,))
        msg = f'Please supply feedback for <a href="{trip_url}">{escape(name)}</a>'
        messages.warning(request, msg, extra_tags='safe')
Beispiel #42
0
 def application_year(self):
     if self.activity == 'winter_school':
         return ws_year()
     return local_date().year
Beispiel #43
0
 def one_year_later(self):
     return local_date() + timedelta(days=365)
Beispiel #44
0
def unapproved_trip_count(activity):
    today = date_utils.local_date()
    return models.Trip.objects.filter(
        trip_date__gte=today, activity=activity, chair_approved=False
    ).count()
Beispiel #45
0
def outstanding_items(emails, rented_on_or_before=None):
    """ Yield all items that are currently checked out to the members.

    This method supports listing items for an individual participant (who may
    have multiple emails/gear accounts) as well as all participants on a trip.
    """
    if not emails:
        return

    # Email capitalization in the database may differ from what users report
    # Map back to the case supplied in arguments for easier mapping
    to_original_case = {email.lower(): email for email in emails}

    cursor = connections['geardb'].cursor()

    rental_date_clause = ''
    if rented_on_or_before:
        rental_date_clause = 'and checkedout <= %(rented_on_or_before)s'

    cursor.execute(
        f'''
        select lower(p.email),
               -- Using proper array types here would be nice, but MySQL lacks them...
               group_concat(distinct lower(pe.alternate_email) separator ','),
               g.id,
               gt.type_name,
               gt.rental_amount,
               date(convert_tz(r.checkedout, '+00:00', '-05:00')) as checkedout
          from rentals          r
               join people      p on p.id = r.person_id
               join gear        g on g.id = r.gear_id
               join gear_types gt on g.type = gt.id
               left join geardb_peopleemails pe on p.id = pe.person_id
         where r.returned is null
           {rental_date_clause}
           and (p.email in %(emails)s or pe.alternate_email in %(emails)s)
           -- It's possible for there to be extra alternate email records matching the primary email
           -- Omit these so we don't get needless duplicates
           and (pe.alternate_email is null or p.email != pe.alternate_email)
         group by p.email, g.id, gt.type_name, gt.rental_amount, r.checkedout
        ''',
        {'emails': tuple(emails), 'rented_on_or_before': rented_on_or_before},
    )

    for main, alternate_emails, gear_id, name, cost, checkedout in cursor.fetchall():
        if main in to_original_case:
            email = main
        else:
            # Because either main or alternate email were matched, this should never happen.
            assert alternate_emails, "Alternate emails were unexpectedly empty!"
            alternates = alternate_emails.split(',')
            try:
                email = next(e for e in alternates if e in to_original_case)
            except StopIteration:
                # This method is a generator - raising StopIteration would stop iteration
                raise ValueError("Expected at least one email to match!")

        yield {
            'email': to_original_case[email],
            'id': gear_id,
            'name': name,
            'cost': cost,
            'checkedout': checkedout,
            'overdue': (local_date() - checkedout > timedelta(weeks=10)),
        }
Beispiel #46
0
 def items(self):
     upcoming_trips = Trip.objects.filter(trip_date__gte=local_date())
     return upcoming_trips.order_by('-trip_date')
Beispiel #47
0
 def get_queryset(self):
     trips = super().get_queryset().order_by('trip_date')
     today = local_date()
     return trips.filter(trip_date__gte=today)
Beispiel #48
0
 def get_queryset(self):
     """ All upcoming trips of this activity type. """
     return models.Trip.objects.filter(
         activity=self.activity, trip_date__gte=local_date()
     )