|
| 1 | +from collections import namedtuple |
| 2 | +from logging import getLogger |
| 3 | + |
| 4 | +from sqlalchemy import exc, text |
| 5 | + |
| 6 | +from .models import db, REQUIRED_MISC_SIGNATURES |
| 7 | +from .packet import get_number_required, get_misc_signatures |
| 8 | + |
| 9 | +LOGGER = getLogger(__name__) |
| 10 | + |
| 11 | + |
| 12 | +def current_packets(member, intro=False, onfloor=False): |
| 13 | + """ |
| 14 | + Get a list of currently open packets with the signed state of each packet. |
| 15 | + :param member: the member currently viewing all packets |
| 16 | + :param intro: true if current member is an intro member |
| 17 | + :param other: true if current member is off floor or alumni |
| 18 | + :return: <tuple> a list of packets that are currently open, and their attributes |
| 19 | + """ |
| 20 | + |
| 21 | + # Tuple for compatibility with UI code. Should be refactored or deleted altogether later |
| 22 | + SPacket = namedtuple('spacket', ['rit_username', 'name', 'did_sign', 'total_signatures', 'required_signatures']) |
| 23 | + |
| 24 | + packets = [] |
| 25 | + required = get_number_required() |
| 26 | + |
| 27 | + if intro and onfloor: |
| 28 | + required -= 1 |
| 29 | + |
| 30 | + signed_packets = get_signed_packets(member, intro, onfloor) |
| 31 | + misc_signatures = get_misc_signatures() |
| 32 | + |
| 33 | + try: |
| 34 | + for pkt in query_packets_with_signed(): |
| 35 | + signed = signed_packets.get(pkt.username) |
| 36 | + misc = misc_signatures.get(pkt.username) |
| 37 | + if signed is None: |
| 38 | + signed = False |
| 39 | + if misc is None: |
| 40 | + misc = 0 |
| 41 | + if misc > REQUIRED_MISC_SIGNATURES: |
| 42 | + misc = REQUIRED_MISC_SIGNATURES |
| 43 | + packets.append(SPacket(pkt.username, pkt.name, signed, pkt.received + misc, required)) |
| 44 | + |
| 45 | + except exc.SQLAlchemyError as e: |
| 46 | + LOGGER.error(e) |
| 47 | + raise e |
| 48 | + |
| 49 | + return packets |
| 50 | + |
| 51 | + |
| 52 | +def get_signed_packets(member, intro=False, onfloor=False): |
| 53 | + """ |
| 54 | + Get a list of all packets that a member has signed |
| 55 | + :param member: member retrieving prior packet signatures |
| 56 | + :param intro: is the member an intro member? |
| 57 | + :param onfloor: is the member on floor? |
| 58 | + :return: <dict> usernames mapped to signed status |
| 59 | + """ |
| 60 | + signed_packets = {} |
| 61 | + |
| 62 | + try: |
| 63 | + if intro and onfloor: |
| 64 | + for signature in query_signed_intromember(member): |
| 65 | + signed_packets[signature.username] = signature.signed |
| 66 | + |
| 67 | + if not intro: |
| 68 | + if onfloor: |
| 69 | + for signature in query_signed_upperclassman(member): |
| 70 | + signed_packets[signature.username] = signature.signed |
| 71 | + |
| 72 | + else: |
| 73 | + for signature in query_signed_alumni(member): |
| 74 | + signed_packets[signature.username] = bool(signature.signed) |
| 75 | + |
| 76 | + except exc.SQLAlchemyError as e: |
| 77 | + LOGGER.error(e) |
| 78 | + raise e |
| 79 | + |
| 80 | + return signed_packets |
| 81 | + |
| 82 | + |
| 83 | +def query_packets_with_signed(): |
| 84 | + """ |
| 85 | + Query the database and return a list of currently open packets and the number of signatures they currently have |
| 86 | + :return: a list of results: intro members with open packets, their name, username, and number of signatures received |
| 87 | + """ |
| 88 | + try: |
| 89 | + return db.engine.execute(""" |
| 90 | + SELECT packets.username AS username, packets.name AS name, coalesce(packets.sigs_recvd, 0) AS received |
| 91 | + FROM ( ( SELECT freshman.rit_username |
| 92 | + AS username, freshman.name AS name, packet.id AS id, packet.start AS start, packet.end AS end |
| 93 | + FROM freshman INNER JOIN packet ON freshman.rit_username = packet.freshman_username) AS a |
| 94 | + LEFT JOIN ( SELECT totals.id AS id, coalesce(sum(totals.signed), 0) AS sigs_recvd |
| 95 | + FROM ( SELECT packet.id AS id, coalesce(count(signature_fresh.signed), 0) AS signed |
| 96 | + FROM packet FULL OUTER JOIN signature_fresh ON signature_fresh.packet_id = packet.id |
| 97 | + WHERE signature_fresh.signed = TRUE AND packet.start < now() AND now() < packet.end |
| 98 | + GROUP BY packet.id |
| 99 | + UNION SELECT packet.id AS id, coalesce(count(signature_upper.signed), 0) AS signed FROM packet |
| 100 | + FULL OUTER JOIN signature_upper ON signature_upper.packet_id = packet.id |
| 101 | + WHERE signature_upper.signed = TRUE AND packet.start < now() AND now() < packet.end |
| 102 | + GROUP BY packet.id ) totals GROUP BY totals.id ) AS b ON a.id = b.id ) AS packets |
| 103 | + WHERE packets.start < now() AND now() < packets.end; |
| 104 | + """) |
| 105 | + |
| 106 | + except exc.SQLAlchemyError: |
| 107 | + raise exc.SQLAlchemyError("Error: Failed to get open packets with signatures received from database") |
| 108 | + |
| 109 | + |
| 110 | +def query_signed_intromember(member): |
| 111 | + """ |
| 112 | + Query the database and return the list of packets signed by the given intro member |
| 113 | + :param member: the user making the query |
| 114 | + :return: list of results matching the query |
| 115 | + """ |
| 116 | + |
| 117 | + s = text( |
| 118 | + "SELECT DISTINCT packet.freshman_username AS username, signature_fresh.signed AS signed FROM packet " |
| 119 | + "INNER JOIN signature_fresh ON packet.id = signature_fresh.packet_id " |
| 120 | + "WHERE signature_fresh.freshman_username = :member;") |
| 121 | + try: |
| 122 | + return db.engine.execute(s, member=member) |
| 123 | + |
| 124 | + except exc.SQLAlchemyError: |
| 125 | + raise exc.SQLAlchemyError("Error: Failed to get intromember's signatures from database") |
| 126 | + |
| 127 | + |
| 128 | +def query_signed_upperclassman(member): |
| 129 | + """ |
| 130 | + Query the database and return the list of packets signed by the given upperclassman |
| 131 | + :param member: the user making the query |
| 132 | + :return: list of results matching the query |
| 133 | + """ |
| 134 | + |
| 135 | + s = text("SELECT DISTINCT packet.freshman_username AS username, signature_upper.signed AS signed FROM packet " |
| 136 | + "INNER JOIN signature_upper ON packet.id = signature_upper.packet_id " |
| 137 | + "WHERE signature_upper.member = :member;") |
| 138 | + |
| 139 | + try: |
| 140 | + return db.engine.execute(s, member=member) |
| 141 | + |
| 142 | + except exc.SQLAlchemyError: |
| 143 | + raise exc.SQLAlchemyError("Error: Failed to get upperclassman's signatures from database") |
| 144 | + |
| 145 | + |
| 146 | +def query_signed_alumni(member): |
| 147 | + """ |
| 148 | + Query the database and return the list of packets signed by the given alumni/off-floor |
| 149 | + :param member: the user making the query |
| 150 | + :return: list of results matching the query |
| 151 | + """ |
| 152 | + |
| 153 | + s = text("SELECT DISTINCT packet.freshman_username AS username, signature_misc.member AS signed FROM packet " |
| 154 | + "LEFT OUTER JOIN signature_misc ON packet.id = signature_misc.packet_id " |
| 155 | + "WHERE signature_misc.member = :member OR signature_misc.member ISNULL;") |
| 156 | + |
| 157 | + try: |
| 158 | + return db.engine.execute(s, member=member) |
| 159 | + |
| 160 | + except exc.SQLAlchemyError: |
| 161 | + raise exc.SQLAlchemyError("Error: Failed to get alumni's signatures from database") |
0 commit comments