1010import re
1111import tempfile
1212
13+ from collections import defaultdict
1314from email import message_from_string , message_from_bytes
1415from email .header import decode_header
1516from email .iterators import typed_subpart_iterator
2829from ietf .group .models import Group , Role
2930from ietf .person .models import Email , Person
3031from ietf .mailtrigger .utils import gather_address_lists
31- from ietf .meeting .models import Meeting
32+ from ietf .meeting .models import Meeting , Attended
3233from ietf .utils .pipe import pipe
3334from ietf .utils .mail import send_mail_text , send_mail , get_payload_text
3435from ietf .utils .log import log
@@ -510,6 +511,8 @@ def list_eligible(nomcom=None, date=None, base_qs=None):
510511 return list_eligible_8788 (date = eligibility_date , base_qs = base_qs )
511512 elif eligibility_date .year in (2021 ,2022 ):
512513 return list_eligible_8989 (date = eligibility_date , base_qs = base_qs )
514+ elif eligibility_date .year > 2022 :
515+ return list_eligible_8989bis (date = eligibility_date , base_qs = base_qs )
513516 else :
514517 return Person .objects .none ()
515518
@@ -536,20 +539,26 @@ def list_eligible_8713(date, base_qs=None):
536539 if not base_qs :
537540 base_qs = Person .objects .all ()
538541 previous_five = previous_five_meetings (date )
539- return remove_disqualified (three_of_five_eligible (previous_five = previous_five , queryset = base_qs ))
542+ return remove_disqualified (three_of_five_eligible_8713 (previous_five = previous_five , queryset = base_qs ))
540543
541544def list_eligible_8788 (date , base_qs = None ):
542545 if not base_qs :
543546 base_qs = Person .objects .all ()
544547 previous_five = Meeting .objects .filter (number__in = ['102' ,'103' ,'104' ,'105' ,'106' ])
545- return remove_disqualified (three_of_five_eligible (previous_five = previous_five , queryset = base_qs ))
548+ return remove_disqualified (three_of_five_eligible_8713 (previous_five = previous_five , queryset = base_qs ))
546549
547550def get_8989_eligibility_querysets (date , base_qs ):
551+ return get_threerule_eligibility_querysets (date , base_qs , three_of_five_callable = three_of_five_eligible_8713 )
552+
553+ def get_8989bis_eligibility_querysets (date , base_qs ):
554+ return get_threerule_eligibility_querysets (date , base_qs , three_of_five_callable = three_of_five_eligible_8989bis )
555+
556+ def get_threerule_eligibility_querysets (date , base_qs , three_of_five_callable ):
548557 if not base_qs :
549558 base_qs = Person .objects .all ()
550559
551560 previous_five = previous_five_meetings (date )
552- three_of_five_qs = new_three_of_five_eligible (previous_five = previous_five , queryset = base_qs )
561+ three_of_five_qs = three_of_five_callable (previous_five = previous_five , queryset = base_qs )
553562
554563 # If date is Feb 29, neither 3 nor 5 years ago has a Feb 29. Use Feb 28 instead.
555564 if date .month == 2 and date .day == 29 :
@@ -564,7 +573,7 @@ def get_8989_eligibility_querysets(date, base_qs):
564573 Q (role__name_id__in = ('chair' ,'secr' ),
565574 role__group__state_id = 'active' ,
566575 role__group__type_id = 'wg' ,
567- role__group__time__lte = date ,
576+ role__group__time__lte = date , ## TODO - inspect - lots of things affect group__time...
568577 )
569578 # was an officer since the given date (I think this is wrong - it looks at when roles _start_, not when roles end)
570579 | Q (rolehistory__group__time__gte = three_years_ago ,
@@ -589,7 +598,15 @@ def list_eligible_8989(date, base_qs=None):
589598 if not base_qs :
590599 base_qs = Person .objects .all ()
591600 three_of_five_qs , officer_qs , author_qs = get_8989_eligibility_querysets (date , base_qs )
592- # Would be nice to use queryset union here, but the annotations in the three existing querysets make that difficult
601+ three_of_five_pks = three_of_five_qs .values_list ('pk' ,flat = True )
602+ officer_pks = officer_qs .values_list ('pk' ,flat = True )
603+ author_pks = author_qs .values_list ('pk' ,flat = True )
604+ return remove_disqualified (Person .objects .filter (pk__in = set (three_of_five_pks ).union (set (officer_pks )).union (set (author_pks ))))
605+
606+ def list_eligible_8989bis (date , base_qs = None ):
607+ if not base_qs :
608+ base_qs = Person .objects .all ()
609+ three_of_five_qs , officer_qs , author_qs = get_8989bis_eligibility_querysets (date , base_qs )
593610 three_of_five_pks = three_of_five_qs .values_list ('pk' ,flat = True )
594611 officer_pks = officer_qs .values_list ('pk' ,flat = True )
595612 author_pks = author_qs .values_list ('pk' ,flat = True )
@@ -624,28 +641,34 @@ def previous_five_meetings(date = None):
624641 date = datetime .date .today ()
625642 return Meeting .objects .filter (type = 'ietf' ,date__lte = date ).order_by ('-date' )[:5 ]
626643
627- def three_of_five_eligible (previous_five , queryset = None ):
644+ def three_of_five_eligible_8713 (previous_five , queryset = None ):
628645 """ Return a list of Person records who attended at least
629646 3 of the 5 type_id='ietf' meetings before the given
630647 date. Does not disqualify anyone based on held roles.
648+ This variant bases the calculation on MeetingRegistration.attended
631649 """
632650 if queryset is None :
633651 queryset = Person .objects .all ()
634652 return queryset .filter (meetingregistration__meeting__in = list (previous_five ),meetingregistration__attended = True ).annotate (mtg_count = Count ('meetingregistration' )).filter (mtg_count__gte = 3 )
635653
636- def new_three_of_five_eligible (previous_five , queryset = None ):
637- """ Return a list of Person records who attended at least
654+ def three_of_five_eligible_8989bis (previous_five , queryset = None ):
655+ """ Return a list of Person records who attended at least
638656 3 of the 5 type_id='ietf' meetings before the given
639657 date. Does not disqualify anyone based on held roles.
640- This 'new' variant bases the calculation on the Meeting.Session model rather than Stats. MeetingRegistration
658+ This variant bases the calculation on Meeting.Session and MeetingRegistration.checked_in
641659 Leadership will have to create a new RFC specifying eligibility (RFC8989 is timing out) before it can be used.
642660 """
643661 if queryset is None :
644662 queryset = Person .objects .all ()
645- return queryset .filter (
646- Q (attended__session__meeting__in = list (previous_five )),
647- Q (attended__session__type = 'plenary' )| Q (attended__session__group__type__in = ['wg' ,'rg' ])
648- ).annotate (mtg_count = Count ('attended__session__meeting' ,distinct = True )).filter (mtg_count__gte = 3 )
663+
664+ counts = defaultdict (lambda : 0 )
665+ for meeting in previous_five :
666+ checked_in = meeting .meetingregistration_set .filter (reg_type = 'onsite' , checkedin = True ).values_list ('person' , flat = True )
667+ sessions = meeting .session_set .filter (Q (type = 'plenary' ) | Q (group__type__in = ['wg' , 'rg' ]))
668+ attended = Attended .objects .filter (session__in = sessions ).values_list ('person' , flat = True )
669+ for id in set (checked_in ) | set (attended ):
670+ counts [id ] += 1
671+ return queryset .filter (pk__in = [id for id , count in counts .items () if count >= 3 ])
649672
650673def suggest_affiliation (person ):
651674 recent_meeting = person .meetingregistration_set .order_by ('-meeting__date' ).first ()
0 commit comments