8000 refactor: use new meeting.Registration model by rpcross · Pull Request #8902 · ietf-tools/datatracker · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

refactor: use new meeting.Registration model #8902

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 41 additions & 25 deletions ietf/ietfauth/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,13 @@
from ietf.group.factories import GroupFactory, RoleFactory
from ietf.group.models import Group, Role, RoleName
from ietf.ietfauth.utils import has_role
from ietf.meeting.factories import MeetingFactory
from ietf.meeting.factories import MeetingFactory, RegistrationFactory, RegistrationTicketFactory
from ietf.nomcom.factories import NomComFactory
from ietf.person.factories import PersonFactory, EmailFactory, UserFactory, PersonalApiKeyFactory
from ietf.person.models import Person, Email
from ietf.person.tasks import send_apikey_usage_emails_task
from ietf.review.factories import ReviewRequestFactory, ReviewAssignmentFactory
from ietf.review.models import ReviewWish, UnavailablePeriod
from ietf.stats.models import MeetingRegistration
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
from ietf.utils.test_utils import TestCase, login_testing_unauthorized
from ietf.utils.timezone import date_today
Expand Down Expand Up @@ -1016,11 +1015,15 @@ def test_oidc_code_auth(self):
EmailFactory(person=person)
email_list = person.email_set.all().values_list('address', flat=True)
meeting = MeetingFactory(type_id='ietf', date=date_today())
MeetingRegistration.objects.create(
meeting=meeting, person=None, first_name=person.first_name(), last_name=person.last_name(),
email=email_list[0], ticket_type='full_week', reg_type='remote', affiliation='Some Company',
)

reg_person = RegistrationFactory(
meeting=meeting,
person=person,
first_name=person.first_name(),
last_name=person.last_name(),
email=email_list[0],
affiliation='Some Company',
with_ticket={'attendance_type_id': 'remote', 'ticket_type_id': 'week_pass'},
)
# Get access authorisation
session = {}
session["state"] = rndstr()
Expand Down Expand Up @@ -1073,35 +1076,48 @@ def test_oidc_code_auth(self):
for key in ['iss', 'sub', 'aud', 'exp', 'iat', 'auth_time', 'nonce', 'at_hash']:
self.assertIn(key, access_token_info['id_token'])

# Get userinfo, check keys present
# Get userinfo, check keys present, most common scenario
userinfo = client.do_user_info_request(state=params["state"], scope=args['scope'])
for key in [ 'email', 'family_name', 'given_name', 'meeting', 'name', 'pronouns', 'roles',
'ticket_type', 'reg_type', 'affiliation', 'picture', 'dots', ]:
self.assertIn(key, userinfo)
self.assertTrue(userinfo[key])
self.assertIn('remote', set(userinfo['reg_type'].split()))
self.assertNotIn('hackathon', set(userinfo['reg_type'].split()))
self.assertNotIn('hackathon_onsite', set(userinfo['reg_type'].split()))
self.assertIn(active_group.acronym, [i[1] for i in userinfo['roles']])
self.assertNotIn(closed_group.acronym, [i[1] for i in userinfo['roles']])

# Create another registration, with a different email
MeetingRegistration.objects.create(
meeting=meeting, person=None, first_name=person.first_name(), last_name=person.last_name(),
email=email_list[1], ticket_type='one_day', reg_type='hackathon', affiliation='Some Company, Inc',
)
# Create a registration, with only email, no person (rare if at all)
reg_person.delete()
reg_email = RegistrationFactory(
meeting=meeting,
person=None,
first_name=person.first_name(),
last_name=person.last_name(),
email=email_list[1],
affiliation='Some Company, Inc',
with_ticket={'attendance_type_id': 'hackathon_onsite', 'ticket_type_id': 'one_day'},
)
userinfo = client.do_user_info_request(state=params["state"], scope=args['scope'])
self.assertIn('hackathon', set(userinfo['reg_type'].split()))
self.assertIn('remote', set(userinfo['reg_type'].split()))
self.assertIn('full_week', set(userinfo['ticket_type'].split()))
self.assertIn('Some Company', userinfo['affiliation'])

# Create a third registration, with a composite reg type
MeetingRegistration.objects.create(
meeting=meeting, person=None, first_name=person.first_name(), last_name=person.last_name(),
email=email_list[1], ticket_type='one_day', reg_type='hackathon remote', affiliation='Some Company, Inc',
)
self.assertIn('hackathon_onsite', set(userinfo['reg_type'].split()))
self.assertNotIn('remote', set(userinfo['reg_type'].split()))
self.assertIn('one_day', set(userinfo['ticket_type'].split()))
self.assertIn('Some Company, Inc', userinfo['affiliation'])

# Test with multiple tickets
reg_email.delete()
creg = RegistrationFactory(
meeting=meeting,
person=None,
first_name=person.first_name(),
last_name=person.last_name(),
email=email_list[1],
affiliation='Some Company, Inc',
with_ticket={'attendance_type_id': 'hackathon_remote', 'ticket_type_id': 'week_pass'},
)
RegistrationTicketFactory(registration=creg, attendance_type_id='remote', ticket_type_id='week_pass')
userinfo = client.do_user_info_request(state=params["state"], scope=args['scope'])
self.assertEqual(set(userinfo['reg_type'].split()), set(['remote', 'hackathon']))
self.assertEqual(set(userinfo['reg_type'].split()), set(['remote', 'hackathon_remote']))

# Check that ending a session works
r = client.do_end_session_request(state=params["state"], scope=args['scope'])
Expand Down
20 changes: 11 additions & 9 deletions ietf/ietfauth/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,13 +346,14 @@ def scope_pronouns(self):
)

def scope_registration(self):
# import here to avoid circular imports
from ietf.meeting.helpers import get_current_ietf_meeting
from ietf.stats.models import MeetingRegistration
from ietf.meeting.models import Registration
meeting = get_current_ietf_meeting()
person = self.user.person
email_list = person.email_set.values_list('address')
q = Q(person=person, meeting=meeting) | Q(email__in=email_list, meeting=meeting)
regs = MeetingRegistration.objects.filter(q).distinct()
regs = Registration.objects.filter(q).distinct()
for reg in regs:
if not reg.person_id:
reg.person = person
Expand All @@ -363,19 +364,20 @@ def scope_registration(self):
ticket_types = set([])
reg_types = set([])
for reg in regs:
ticket_types.add(reg.ticket_type)
reg_types.add(reg.reg_type)
for ticket in reg.tickets.all():
ticket_types.add(ticket.ticket_type.slug)
reg_types.add(ticket.attendance_type.slug)
info = {
'meeting': meeting.number,
'meeting': meeting.number,
# full_week, one_day, student:
'ticket_type': ' '.join(ticket_types),
'ticket_type': ' '.join(ticket_types),
# onsite, remote, hackathon_onsite, hackathon_remote:
'reg_type': ' '.join(reg_types),
'affiliation': ([ reg.affiliation for reg in regs if reg.affiliation ] or [''])[0],
'reg_type': ' '.join(reg_types),
'affiliation': ([reg.affiliation for reg in regs if reg.affiliation] or [''])[0],
}

return info

def can_request_rfc_publication(user, doc):
"""Answers whether this user has an appropriate role to send this document to the RFC Editor for publication as an RFC.

Expand Down
23 changes: 21 additions & 2 deletions ietf/meeting/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,15 @@ class Meta:


class RegistrationFactory(factory.django.DjangoModelFactory):
"""
This will create an associated onsite week_pass ticket by default.
Methods of calling:

RegistrationFactory() create a ticket with defaults, onsite
RegistrationFactory(with_ticket=True) same as above
RegistrationFactory(with_ticket={'attendance_type_id': 'remote'}) creates ticket with overrides
RegistrationFactory(with_ticket=False) does not create a ticket
"""
class Meta:
model = Registration
skip_postgeneration_save = True
Expand All @@ -335,12 +344,22 @@ class Meta:
attended = False
checkedin = False

@factory.post_generation
def with_ticket(self, create, extracted, **kwargs):
if not create:
return
if extracted is False:
# Explicitly disable ticket creation
return
ticket_kwargs = extracted if isinstance(extracted, dict) else {}
RegistrationTicketFactory(registration=self, **ticket_kwargs)


class RegistrationTicketFactory(factory.django.DjangoModelFactory):
class Meta:
model = RegistrationTicket
skip_postgeneration_save = True

registration = factory.SubFactory(RegistrationFactory)
attendance_type_id = 'onsite'
ticket_type_id = 'week_pass'
attendance_type_id = factory.LazyAttribute(lambda _: 'onsite')
ticket_type_id = factory.LazyAttribute(lambda _: 'week_pass')
29 changes: 18 additions & 11 deletions ietf/meeting/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,9 @@ def get_proceedings_materials(self):
).order_by('type__order')

def get_attendance(self):
"""Get the meeting attendance from the MeetingRegistrations
"""Get the meeting attendance from the Registrations

Returns a NamedTuple with onsite and online attributes. Returns None if the record is unavailable
Returns a NamedTuple with onsite and remote attributes. Returns None if the record is unavailable
for this meeting.
"""
number = self.get_number()
Expand All @@ -247,10 +247,10 @@ def get_attendance(self):
# We've separated session attendance off to ietf.meeting.Attended, but need to report attendance at older
# meetings correctly.

attended_per_meetingregistration = (
Q(meetingregistration__meeting=self) & (
Q(meetingregistration__attended=True) |
Q(meetingregistration__checkedin=True)
attended_per_meeting_registration = (
Q(registration__meeting=self) & (
Q(registration__attended=True) |
Q(registration__checkedin=True)
)
)
attended_per_meeting_attended = (
Expand All @@ -260,11 +260,11 @@ def get_attendance(self):
# is good enough, just attending e.g. a training session is also good enough
)
attended = Person.objects.filter(
attended_per_meetingregistration | attended_per_meeting_attended
attended_per_meeting_registration | attended_per_meeting_attended
).distinct()

onsite=set(attended.filter(meetingregistration__meeting=self, meetingregistration__reg_type='onsite'))
remote=set(attended.filter(meetingregistration__meeting=self, meetingregistration__reg_type='remote'))
onsite = set(attended.filter(registration__meeting=self, registration__tickets__attendance_type__slug='onsite'))
remote = set(attended.filter(registration__meeting=self, registration__tickets__attendance_type__slug='remote'))
remote.difference_update(onsite)

return Attendance(
Expand Down Expand Up @@ -1487,10 +1487,10 @@ def __str__(self):

class RegistrationManager(models.Manager):
def onsite(self):
return self.get_queryset().filter(registrationticket__attendance_type__slug='onsite')
return self.get_queryset().filter(tickets__attendance_type__slug='onsite')

def remote(self):
return self.get_queryset().filter(registrationticket__attendance_type__slug='remote').exclude(registrationticket__attendance_type__slug='onsite')
return self.get_queryset().filter(tickets__attendance_type__slug='remote').exclude(tickets__attendance_type__slug='onsite')

class Registration(models.Model):
"""Registration attendee records from the IETF registration system"""
Expand All @@ -1513,6 +1513,13 @@ class Registration(models.Model):
def __str__(self):
return "{} {}".format(self.first_name, self.last_name)

@property
def attendance_type(self):
if self.tickets.filter(attendance_type__slug='onsite').exists():
return 'onsite'
elif self.tickets.filter(attendance_type__slug='remote').exists():
return 'remote'

class RegistrationTicket(models.Model):
registration = ForeignKey(Registration, related_name='tickets')
attendance_type = ForeignKey(AttendanceTypeName, on_delete=models.PROTECT)
Expand Down
34 changes: 17 additions & 17 deletions ietf/meeting/tests_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import ietf.meeting.models
from ietf.group.factories import GroupFactory, GroupHistoryFactory
from ietf.meeting.factories import MeetingFactory, SessionFactory, AttendedFactory, SessionPresentationFactory
from ietf.meeting.factories import RegistrationFactory
from ietf.meeting.models import Session
from ietf.stats.factories import MeetingRegistrationFactory
from ietf.utils.test_utils import TestCase
from ietf.utils.timezone import date_today, datetime_today

Expand All @@ -21,41 +21,41 @@ class MeetingTests(TestCase):
def test_get_attendance_pre110(self):
"""Pre-110 meetings do not calculate attendance"""
meeting = MeetingFactory(type_id='ietf', number='109')
MeetingRegistrationFactory.create_batch(3, meeting=meeting, reg_type='')
MeetingRegistrationFactory.create_batch(4, meeting=meeting, reg_type='remote')
MeetingRegistrationFactory.create_batch(5, meeting=meeting, reg_type='in_person')
RegistrationFactory.create_batch(3, meeting=meeting, with_ticket={'attendance_type_id': 'unknown'})
RegistrationFactory.create_batch(4, meeting=meeting, with_ticket={'attendance_type_id': 'remote'})
RegistrationFactory.create_batch(5, meeting=meeting, with_ticket={'attendance_type_id': 'onsite'})
self.assertIsNone(meeting.get_attendance())

def test_get_attendance_110(self):
"""Look at attendance as captured at 110"""
meeting = MeetingFactory(type_id='ietf', number='110')

# start with attendees that should be ignored
MeetingRegistrationFactory.create_batch(3, meeting=meeting, reg_type='', attended=True)
MeetingRegistrationFactory(meeting=meeting, reg_type='', attended=False)
RegistrationFactory.create_batch(3, meeting=meeting, with_ticket={'attendance_type_id': 'unknown'}, attended=True)
RegistrationFactory(meeting=meeting, with_ticket={'attendance_type_id': 'unknown'}, attended=False)
attendance = meeting.get_attendance()
self.assertIsNotNone(attendance)
self.assertEqual(attendance.remote, 0)
self.assertEqual(attendance.onsite, 0)

# add online attendees with at least one who registered but did not attend
MeetingRegistrationFactory.create_batch(4, meeting=meeting, reg_type='remote', attended=True)
MeetingRegistrationFactory(meeting=meeting, reg_type='remote', attended=False)
RegistrationFactory.create_batch(4, meeting=meeting, with_ticket={'attendance_type_id': 'remote'}, attended=True)
RegistrationFactory(meeting=meeting, with_ticket={'attendance_type_id': 'remote'}, attended=False)
attendance = meeting.get_attendance()
self.assertIsNotNone(attendance)
self.assertEqual(attendance.remote, 4)
self.assertEqual(attendance.onsite, 0)

# and the same for onsite attendees
MeetingRegist CF3E rationFactory.create_batch(5, meeting=meeting, reg_type='onsite', attended=True)
MeetingRegistrationFactory(meeting=meeting, reg_type='in_person', attended=False)
RegistrationFactory.create_batch(5, meeting=meeting, with_ticket={'attendance_type_id': 'onsite'}, attended=True)
RegistrationFactory(meeting=meeting, with_ticket={'attendance_type_id': 'onsite'}, attended=False)
attendance = meeting.get_attendance()
self.assertIsNotNone(attendance)
self.assertEqual(attendance.remote, 4)
self.assertEqual(attendance.onsite, 5)

# and once more after removing all the online attendees
meeting.meetingregistration_set.filter(reg_type='remote').delete()
meeting.registration_set.remote().delete()
attendance = meeting.get_attendance()
self.assertIsNotNone(attendance)
self.assertEqual(attendance.remote, 0)
Expand All @@ -64,11 +64,11 @@ def test_get_attendance_110(self):
def test_get_attendance_113(self):
"""Simulate IETF 113 attendance gathering data"""
meeting = MeetingFactory(type_id='ietf', number='113')
MeetingRegistrationFactory(meeting=meeting, reg_type='onsite', attended=True, checkedin=False)
MeetingRegistrationFactory(meeting=meeting, reg_type='onsite', attended=False, checkedin=True)
p1 = MeetingRegistrationFactory(meeting=meeting, reg_type='onsite', attended=False, checkedin=False).person
RegistrationFactory(meeting=meeting, with_ticket={'attendance_type_id': 'onsite'}, attended=True, checkedin=False)
RegistrationFactory(meeting=meeting, with_ticket={'attendance_type_id': 'onsite'}, attended=False, checkedin=True)
p1 = RegistrationFactory(meeting=meeting, with_ticket={'attendance_type_id': 'onsite'}, attended=False, checkedin=False).person
AttendedFactory(session__meeting=meeting, person=p1)
p2 = MeetingRegistrationFactory(meeting=meeting, reg_type='remote', attended=False, checkedin=False).person
p2 = RegistrationFactory(meeting=meeting, with_ticket={'attendance_type_id': 'remote'}, attended=False, checkedin=False).person
AttendedFactory(session__meeting=meeting, person=p2)
attendance = meeting.get_attendance()
self.assertEqual(attendance.onsite, 3)
Expand All @@ -82,9 +82,9 @@ def test_get_attendance_keeps_meetings_distinct(self):

# Create a person who attended a remote session for first_mtg and onsite for second_mtg without
# checking in for either.
p = MeetingRegistrationFactory(meeting=second_mtg, reg_type='onsite', attended=False, checkedin=False).person
p = RegistrationFactory(meeting=second_mtg, with_ticket={'attendance_type_id': 'onsite'}, attended=False, checkedin=False).person
AttendedFactory(session__meeting=first_mtg, person=p)
MeetingRegistrationFactory(meeting=first_mtg, person=p, reg_type='remote', attended=False, checkedin=False)
RegistrationFactory(meeting=first_mtg, person=p, with_ticket={'attendance_type_id': 'remote'}, attended=False, checkedin=False)
AttendedFactory(session__meeting=second_mtg, person=p)

att = first_mtg.get_attendance()
Expand Down
Loading
Loading
0