def allocate_ranks_and_add_warnings_for_cycle(cycle_start_dt):
    start_dt, end_dt = calculate_start_and_end_dt_for_cycle(cycle_start_dt)

    gnt_demoted_warning_type = MemberWarningType.objects.get(
        name__iexact="Grunt Demoted")
    res_promoted_warning_type = MemberWarningType.objects.get(
        name__iexact="Reservist Promoted")

    events = Event.all_for_time_period(start_dt, end_dt)
    event_count = events.count()
    min_gnt_event_count = round(float(event_count) / 3.0)

    members = Member.active_members(include_recruits=False)
    gnt_rank = Rank.objects.get(name__iexact="gnt")
    res_rank = Rank.objects.get(name__iexact="res")

    for member in members:
        member_rank = member.rank

        if member_rank != gnt_rank and member_rank != res_rank:
            # Other ranks are not subject to attendance restrictions
            continue

        gnt_adequate, message = Attendance.was_adequate_for_period(
            member,
            events,
            start_dt,
            end_dt,
            min_total_events=min_gnt_event_count,
            adequate_if_absent=False)

        if member_rank == res_rank and gnt_adequate:
            rank_message = "%s has been promoted to Grunt due to attending at least %s events between %s and %s." % (
                member.name, min_gnt_event_count,
                start_dt.strftime("%Y-%m-%d"), end_dt.strftime("%Y-%m-%d"))

            if warning_exists(member, res_promoted_warning_type, rank_message):
                continue

            # Promote to grunt
            member.rank = gnt_rank
            member.save()

            create_or_update_warning(member, res_promoted_warning_type, True,
                                     rank_message)

        elif member_rank == gnt_rank and not gnt_adequate:
            rank_message = "%s has been demoted to Reservist due to attending less than %s events between %s and %s." \
                           % (member.name, min_gnt_event_count, start_dt.strftime("%Y-%m-%d"),
                              end_dt.strftime("%Y-%m-%d"))

            if warning_exists(member, gnt_demoted_warning_type, rank_message):
                continue

            # Demote to reservist
            member.rank = res_rank
            member.save()

            create_or_update_warning(member, gnt_demoted_warning_type, True,
                                     rank_message)
def add_and_update_low_recruit_attendances():
    """

    :param month_dt:
    :return:
    """
    low_attendance_warning_type = MemberWarningType.objects.get(
        name__iexact="Low Attendance (Recruit)")

    members = Member.recruits()

    for member in members:
        start_dt = member.join_date
        end_dt, event_count = member.get_recruit_event_attendance_deadline_and_count(
        )

        events = Event.all_for_time_period(start_dt, end_dt)
        adequate, message = Attendance.was_adequate_for_period(
            member,
            events,
            start_dt,
            end_dt,
            adequate_if_absent=False,
            min_total_events=event_count)

        create_or_update_warning(member, low_attendance_warning_type,
                                 not adequate, message)
def allocate_ranks_and_add_warnings_for_cycle(cycle_start_dt):
    start_dt, end_dt = calculate_start_and_end_dt_for_cycle(cycle_start_dt)

    gnt_demoted_warning_type = MemberWarningType.objects.get(name__iexact="Grunt Demoted")
    res_promoted_warning_type = MemberWarningType.objects.get(name__iexact="Reservist Promoted")

    events = Event.all_for_time_period(start_dt, end_dt)
    event_count = events.count()
    min_gnt_event_count = round(float(event_count) / 3.0)

    members = Member.active_members(include_recruits=False)
    gnt_rank = Rank.objects.get(name__iexact="gnt")
    res_rank = Rank.objects.get(name__iexact="res")

    for member in members:
        member_rank = member.rank

        if member_rank != gnt_rank and member_rank != res_rank:
            # Other ranks are not subject to attendance restrictions
            continue

        gnt_adequate, message = Attendance.was_adequate_for_period(member, events, start_dt, end_dt,
                                                                   min_total_events=min_gnt_event_count,
                                                                   adequate_if_absent=False)

        if member_rank == res_rank and gnt_adequate:
            rank_message = "%s has been promoted to Grunt due to attending at least %s events between %s and %s." % (
                member.name, min_gnt_event_count, start_dt.strftime("%Y-%m-%d"),
                end_dt.strftime("%Y-%m-%d"))

            if warning_exists(member, res_promoted_warning_type, rank_message):
                continue

            # Promote to grunt
            member.rank = gnt_rank
            member.save()

            create_or_update_warning(member, res_promoted_warning_type, True,
                                     rank_message)

        elif member_rank == gnt_rank and not gnt_adequate:
            rank_message = "%s has been demoted to Reservist due to attending less than %s events between %s and %s." \
                           % (member.name, min_gnt_event_count, start_dt.strftime("%Y-%m-%d"),
                              end_dt.strftime("%Y-%m-%d"))

            if warning_exists(member, gnt_demoted_warning_type, rank_message):
                continue

            # Demote to reservist
            member.rank = res_rank
            member.save()

            create_or_update_warning(member, gnt_demoted_warning_type, True,
                                     rank_message)
def add_and_update_low_member_attendances_for_cycle(cycle_start_dt):
    """

    :param month_dt:
    :return:
    """
    low_attendance_warning_type = MemberWarningType.objects.get(name__iexact="Low Attendance")
    start_dt, end_dt = calculate_start_and_end_dt_for_cycle(cycle_start_dt)

    events = Event.all_for_time_period(start_dt, end_dt)

    members = Member.active_members(include_recruits=False)

    for member in members:
        adequate, message = Attendance.was_adequate_for_period(member, events, start_dt, end_dt,
                                                               adequate_if_absent=True)

        create_or_update_warning(member, low_attendance_warning_type, not adequate, message)
def add_and_update_low_recruit_attendances():
    """

    :param month_dt:
    :return:
    """
    low_attendance_warning_type = MemberWarningType.objects.get(name__iexact="Low Attendance (Recruit)")

    members = Member.recruits()

    for member in members:
        start_dt = member.join_date
        end_dt, event_count = member.get_recruit_event_attendance_deadline_and_count()

        events = Event.all_for_time_period(start_dt, end_dt)
        adequate, message = Attendance.was_adequate_for_period(member, events, start_dt, end_dt,
                                                               adequate_if_absent=False,
                                                               min_total_events=event_count)

        create_or_update_warning(member, low_attendance_warning_type, not adequate, message)
def add_and_update_low_member_attendances_for_cycle(cycle_start_dt):
    """

    :param month_dt:
    :return:
    """
    low_attendance_warning_type = MemberWarningType.objects.get(
        name__iexact="Low Attendance")
    start_dt, end_dt = calculate_start_and_end_dt_for_cycle(cycle_start_dt)

    events = Event.all_for_time_period(start_dt, end_dt)

    members = Member.active_members(include_recruits=False)

    for member in members:
        adequate, message = Attendance.was_adequate_for_period(
            member, events, start_dt, end_dt, adequate_if_absent=True)

        create_or_update_warning(member, low_attendance_warning_type,
                                 not adequate, message)