feat: separate MeetingRegistration rows for each registration type. updates the registration API (#3641)
* Registration API Update - change MeetingRegistration.reg_type field to hold only one type - allow multiple MeetingRegistration records per person/meeting (one for each reg_type) * Fix scope claims * Add meeting 114 to MeetingRegistration migration * fix: update stats views for MeetingRegistration model use changes * refactor: remove unused imports
This commit is contained in:
parent
df27d0f3cf
commit
698f031b7f
|
@ -279,23 +279,23 @@ class CustomApiTests(TestCase):
|
||||||
def test_api_new_meeting_registration(self):
|
def test_api_new_meeting_registration(self):
|
||||||
meeting = MeetingFactory(type_id='ietf')
|
meeting = MeetingFactory(type_id='ietf')
|
||||||
reg = {
|
reg = {
|
||||||
'apikey': 'invalid',
|
'apikey': 'invalid',
|
||||||
'affiliation': "Alguma Corporação",
|
'affiliation': "Alguma Corporação",
|
||||||
'country_code': 'PT',
|
'country_code': 'PT',
|
||||||
'email': 'foo@example.pt',
|
'email': 'foo@example.pt',
|
||||||
'first_name': 'Foo',
|
'first_name': 'Foo',
|
||||||
'last_name': 'Bar',
|
'last_name': 'Bar',
|
||||||
'meeting': meeting.number,
|
'meeting': meeting.number,
|
||||||
'reg_type': 'hackathon',
|
'reg_type': 'hackathon',
|
||||||
'ticket_type': '',
|
'ticket_type': '',
|
||||||
}
|
}
|
||||||
url = urlreverse('ietf.api.views.api_new_meeting_registration')
|
url = urlreverse('ietf.api.views.api_new_meeting_registration')
|
||||||
r = self.client.post(url, reg)
|
r = self.client.post(url, reg)
|
||||||
self.assertContains(r, 'Invalid apikey', status_code=403)
|
self.assertContains(r, 'Invalid apikey', status_code=403)
|
||||||
oidcp = PersonFactory(user__is_staff=True)
|
oidcp = PersonFactory(user__is_staff=True)
|
||||||
# Make sure 'oidcp' has an acceptable role
|
# Make sure 'oidcp' has an acceptable role
|
||||||
RoleFactory(name_id='robot', person=oidcp, email=oidcp.email(), group__acronym='secretariat')
|
RoleFactory(name_id='robot', person=oidcp, email=oidcp.email(), group__acronym='secretariat')
|
||||||
key = PersonalApiKey.objects.create(person=oidcp, endpoint=url)
|
key = PersonalApiKey.objects.create(person=oidcp, endpoint=url)
|
||||||
reg['apikey'] = key.hash()
|
reg['apikey'] = key.hash()
|
||||||
#
|
#
|
||||||
# Test valid POST
|
# Test valid POST
|
||||||
|
@ -313,7 +313,7 @@ class CustomApiTests(TestCase):
|
||||||
#
|
#
|
||||||
# Check record
|
# Check record
|
||||||
obj = MeetingRegistration.objects.get(email=reg['email'], meeting__number=reg['meeting'])
|
obj = MeetingRegistration.objects.get(email=reg['email'], meeting__number=reg['meeting'])
|
||||||
for key in [ 'affiliation', 'country_code', 'first_name', 'last_name', 'person', 'reg_type', 'ticket_type', ]:
|
for key in ['affiliation', 'country_code', 'first_name', 'last_name', 'person', 'reg_type', 'ticket_type']:
|
||||||
self.assertEqual(getattr(obj, key), reg.get(key), "Bad data for field '%s'" % key)
|
self.assertEqual(getattr(obj, key), reg.get(key), "Bad data for field '%s'" % key)
|
||||||
#
|
#
|
||||||
# Test with existing user
|
# Test with existing user
|
||||||
|
@ -328,15 +328,15 @@ class CustomApiTests(TestCase):
|
||||||
# There should be no new outgoing mail
|
# There should be no new outgoing mail
|
||||||
self.assertEqual(len(outbox), old_len + 1)
|
self.assertEqual(len(outbox), old_len + 1)
|
||||||
#
|
#
|
||||||
# Test combination of reg types
|
# Test multiple reg types
|
||||||
reg['reg_type'] = 'remote'
|
reg['reg_type'] = 'remote'
|
||||||
reg['ticket_type'] = 'full_week_pass'
|
reg['ticket_type'] = 'full_week_pass'
|
||||||
r = self.client.post(url, reg)
|
r = self.client.post(url, reg)
|
||||||
self.assertContains(r, "Accepted, Updated registration", status_code=202)
|
self.assertContains(r, "Accepted, New registration", status_code=202)
|
||||||
obj = MeetingRegistration.objects.get(email=reg['email'], meeting__number=reg['meeting'])
|
objs = MeetingRegistration.objects.filter(email=reg['email'], meeting__number=reg['meeting'])
|
||||||
self.assertIn('hackathon', set(obj.reg_type.split()))
|
self.assertEqual(len(objs), 2)
|
||||||
self.assertIn('remote', set(obj.reg_type.split()))
|
self.assertEqual(objs.filter(reg_type='hackathon').count(), 1)
|
||||||
self.assertIn('full_week_pass', set(obj.ticket_type.split()))
|
self.assertEqual(objs.filter(reg_type='remote', ticket_type='full_week_pass').count(), 1)
|
||||||
self.assertEqual(len(outbox), old_len + 1)
|
self.assertEqual(len(outbox), old_len + 1)
|
||||||
#
|
#
|
||||||
# Test incomplete POST
|
# Test incomplete POST
|
||||||
|
@ -346,7 +346,7 @@ class CustomApiTests(TestCase):
|
||||||
r = self.client.post(url, reg)
|
r = self.client.post(url, reg)
|
||||||
self.assertContains(r, 'Missing parameters:', status_code=400)
|
self.assertContains(r, 'Missing parameters:', status_code=400)
|
||||||
err, fields = r.content.decode().split(':', 1)
|
err, fields = r.content.decode().split(':', 1)
|
||||||
missing_fields = [ f.strip() for f in fields.split(',') ]
|
missing_fields = [f.strip() for f in fields.split(',')]
|
||||||
self.assertEqual(set(missing_fields), set(drop_fields))
|
self.assertEqual(set(missing_fields), set(drop_fields))
|
||||||
|
|
||||||
def test_api_version(self):
|
def test_api_version(self):
|
||||||
|
@ -422,4 +422,4 @@ class TastypieApiTestCase(ResourceTestCaseMixin, TestCase):
|
||||||
if not model._meta.model_name in list(app_resources.keys()):
|
if not model._meta.model_name in list(app_resources.keys()):
|
||||||
#print("There doesn't seem to be any resource for model %s.models.%s"%(app.__name__,model.__name__,))
|
#print("There doesn't seem to be any resource for model %s.models.%s"%(app.__name__,model.__name__,))
|
||||||
self.assertIn(model._meta.model_name, list(app_resources.keys()),
|
self.assertIn(model._meta.model_name, list(app_resources.keys()),
|
||||||
"There doesn't seem to be any API resource for model %s.models.%s"%(app.__name__,model.__name__,))
|
"There doesn't seem to be any API resource for model %s.models.%s"%(app.__name__,model.__name__,))
|
||||||
|
|
|
@ -162,30 +162,28 @@ def api_new_meeting_registration(request):
|
||||||
meeting = Meeting.objects.get(number=number)
|
meeting = Meeting.objects.get(number=number)
|
||||||
except Meeting.DoesNotExist:
|
except Meeting.DoesNotExist:
|
||||||
return err(400, "Invalid meeting value: '%s'" % (number, ))
|
return err(400, "Invalid meeting value: '%s'" % (number, ))
|
||||||
|
reg_type = data['reg_type']
|
||||||
email = data['email']
|
email = data['email']
|
||||||
try:
|
try:
|
||||||
validate_email(email)
|
validate_email(email)
|
||||||
except ValidationError:
|
except ValidationError:
|
||||||
return err(400, "Invalid email value: '%s'" % (email, ))
|
return err(400, "Invalid email value: '%s'" % (email, ))
|
||||||
if request.POST.get('cancelled', 'false') == 'true':
|
if request.POST.get('cancelled', 'false') == 'true':
|
||||||
MeetingRegistration.objects.filter(meeting_id=meeting.pk, email=email).delete()
|
MeetingRegistration.objects.filter(
|
||||||
|
meeting_id=meeting.pk,
|
||||||
|
email=email,
|
||||||
|
reg_type=reg_type).delete()
|
||||||
return HttpResponse('OK', status=200, content_type='text/plain')
|
return HttpResponse('OK', status=200, content_type='text/plain')
|
||||||
else:
|
else:
|
||||||
object, created = MeetingRegistration.objects.get_or_create(meeting_id=meeting.pk, email=email)
|
object, created = MeetingRegistration.objects.get_or_create(
|
||||||
|
meeting_id=meeting.pk,
|
||||||
|
email=email,
|
||||||
|
reg_type=reg_type)
|
||||||
try:
|
try:
|
||||||
# Set attributes not already in the object
|
# Update attributes
|
||||||
for key in set(data.keys())-set(['attended', 'apikey', 'meeting', 'email',]):
|
for key in set(data.keys())-set(['attended', 'apikey', 'meeting', 'email']):
|
||||||
new = data.get(key)
|
new = data.get(key)
|
||||||
cur = getattr(object, key, None)
|
setattr(object, key, new)
|
||||||
if key in ['reg_type', 'ticket_type', ] and new:
|
|
||||||
# Special handling for multiple reg types
|
|
||||||
if cur:
|
|
||||||
if not new in cur:
|
|
||||||
setattr(object, key, cur+' '+new)
|
|
||||||
else:
|
|
||||||
setattr(object, key, new)
|
|
||||||
else:
|
|
||||||
setattr(object, key, new)
|
|
||||||
person = Person.objects.filter(email__address=email)
|
person = Person.objects.filter(email__address=email)
|
||||||
if person.exists():
|
if person.exists():
|
||||||
object.person = person.first()
|
object.person = person.first()
|
||||||
|
|
|
@ -291,15 +291,13 @@ class OidcExtraScopeClaims(oidc_provider.lib.claims.ScopeClaims):
|
||||||
ticket_types = set([])
|
ticket_types = set([])
|
||||||
reg_types = set([])
|
reg_types = set([])
|
||||||
for reg in regs:
|
for reg in regs:
|
||||||
for t in reg.ticket_type.split():
|
ticket_types.add(reg.ticket_type)
|
||||||
ticket_types.add(t)
|
reg_types.add(reg.reg_type)
|
||||||
for r in reg.reg_type.split():
|
|
||||||
reg_types.add(r)
|
|
||||||
info = {
|
info = {
|
||||||
'meeting': meeting.number,
|
'meeting': meeting.number,
|
||||||
# full_week, one_day, student:
|
# full_week, one_day, student:
|
||||||
'ticket_type': ' '.join(ticket_types),
|
'ticket_type': ' '.join(ticket_types),
|
||||||
# in_person, onliine, hackathon:
|
# onsite, remote, hackathon_onsite, hackathon_remote:
|
||||||
'reg_type': ' '.join(reg_types),
|
'reg_type': ' '.join(reg_types),
|
||||||
'affiliation': ([ reg.affiliation for reg in regs if reg.affiliation ] or [''])[0],
|
'affiliation': ([ reg.affiliation for reg in regs if reg.affiliation ] or [''])[0],
|
||||||
}
|
}
|
||||||
|
|
33
ietf/stats/migrations/0004_split_records.py
Normal file
33
ietf/stats/migrations/0004_split_records.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# Generated by Django 2.2.26 on 2022-01-19 16:36
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def forward(apps, schema_editor):
|
||||||
|
'''Split records that have 2 reg_types into two separate records'''
|
||||||
|
MeetingRegistration = apps.get_model('stats', 'MeetingRegistration')
|
||||||
|
meetings = [108, 109, 110, 111, 112, 113, 114]
|
||||||
|
for reg in MeetingRegistration.objects.filter(meeting__number__in=meetings):
|
||||||
|
reg_types = reg.reg_type.split()
|
||||||
|
if len(reg_types) == 2:
|
||||||
|
reg.reg_type = reg_types[0]
|
||||||
|
reg.save()
|
||||||
|
# create copy
|
||||||
|
reg.pk = None
|
||||||
|
reg.reg_type = reg_types[1]
|
||||||
|
reg.save()
|
||||||
|
|
||||||
|
|
||||||
|
def reverse(apps, schema_editor):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('stats', '0003_meetingregistration_attended'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(forward, reverse)
|
||||||
|
]
|
|
@ -13,7 +13,6 @@ from requests import Response
|
||||||
import debug # pyflakes:ignore
|
import debug # pyflakes:ignore
|
||||||
|
|
||||||
from django.urls import reverse as urlreverse
|
from django.urls import reverse as urlreverse
|
||||||
from django.contrib.auth.models import User
|
|
||||||
|
|
||||||
from ietf.utils.test_utils import login_testing_unauthorized, TestCase
|
from ietf.utils.test_utils import login_testing_unauthorized, TestCase
|
||||||
import ietf.stats.views
|
import ietf.stats.views
|
||||||
|
@ -231,33 +230,38 @@ class StatisticsTests(TestCase):
|
||||||
@patch('requests.get')
|
@patch('requests.get')
|
||||||
def test_get_meeting_registration_data(self, mock_get):
|
def test_get_meeting_registration_data(self, mock_get):
|
||||||
'''Test function to get reg data. Confirm leading/trailing spaces stripped'''
|
'''Test function to get reg data. Confirm leading/trailing spaces stripped'''
|
||||||
response = Response()
|
person = PersonFactory()
|
||||||
response.status_code = 200
|
data = {
|
||||||
response._content = b'[{"LastName":"Smith ","FirstName":" John","Company":"ABC","Country":"US","Email":"john.doe@example.us"}]'
|
'LastName': person.last_name() + ' ',
|
||||||
mock_get.return_value = response
|
'FirstName': person.first_name(),
|
||||||
meeting = MeetingFactory(type_id='ietf', date=datetime.date(2016,7,14), number="96")
|
'Company': 'ABC',
|
||||||
|
'Country': 'US',
|
||||||
|
'Email': person.email().address,
|
||||||
|
'RegType': 'onsite'
|
||||||
|
}
|
||||||
|
data2 = data.copy()
|
||||||
|
data2['RegType'] = 'hackathon'
|
||||||
|
response_a = Response()
|
||||||
|
response_a.status_code = 200
|
||||||
|
response_a._content = json.dumps([data, data2]).encode('utf8')
|
||||||
|
# second response one less record, it's been deleted
|
||||||
|
response_b = Response()
|
||||||
|
response_b.status_code = 200
|
||||||
|
response_b._content = json.dumps([data]).encode('utf8')
|
||||||
|
# mock_get.return_value = response
|
||||||
|
mock_get.side_effect = [response_a, response_b]
|
||||||
|
meeting = MeetingFactory(type_id='ietf', date=datetime.date(2016, 7, 14), number="96")
|
||||||
get_meeting_registration_data(meeting)
|
get_meeting_registration_data(meeting)
|
||||||
query = MeetingRegistration.objects.filter(first_name='John',last_name='Smith',country_code='US')
|
query = MeetingRegistration.objects.filter(
|
||||||
self.assertTrue(query.count(), 1)
|
first_name=person.first_name(),
|
||||||
self.assertTrue(isinstance(query[0].person,Person))
|
last_name=person.last_name(),
|
||||||
|
country_code='US')
|
||||||
@patch('requests.get')
|
self.assertEqual(query.count(), 2)
|
||||||
def test_get_meeting_registration_data_user_exists(self, mock_get):
|
self.assertEqual(query.filter(reg_type='onsite').count(), 1)
|
||||||
response = Response()
|
self.assertEqual(query.filter(reg_type='hackathon').count(), 1)
|
||||||
response.status_code = 200
|
# call a second time to test delete
|
||||||
response._content = b'[{"LastName":"Smith","FirstName":"John","Company":"ABC","Country":"US","Email":"john.doe@example.us"}]'
|
|
||||||
email = "john.doe@example.us"
|
|
||||||
user = User.objects.create(username=email)
|
|
||||||
user.save()
|
|
||||||
|
|
||||||
mock_get.return_value = response
|
|
||||||
meeting = MeetingFactory(type_id='ietf', date=datetime.date(2016,7,14), number="96")
|
|
||||||
get_meeting_registration_data(meeting)
|
get_meeting_registration_data(meeting)
|
||||||
query = MeetingRegistration.objects.filter(first_name='John',last_name='Smith',country_code='US')
|
query = MeetingRegistration.objects.filter(meeting=meeting, email=person.email())
|
||||||
emails = Email.objects.filter(address=email)
|
self.assertEqual(query.count(), 1)
|
||||||
self.assertTrue(query.count(), 1)
|
self.assertEqual(query.filter(reg_type='onsite').count(), 1)
|
||||||
self.assertTrue(isinstance(query[0].person, Person))
|
self.assertEqual(query.filter(reg_type='hackathon').count(), 0)
|
||||||
self.assertTrue(len(emails)>=1)
|
|
||||||
self.assertEqual(query[0].person, emails[0].person)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,16 +7,17 @@ import requests
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
|
||||||
|
|
||||||
import debug # pyflakes:ignore
|
import debug # pyflakes:ignore
|
||||||
|
|
||||||
from ietf.stats.models import AffiliationAlias, AffiliationIgnoredEnding, CountryAlias, MeetingRegistration
|
from ietf.stats.models import AffiliationAlias, AffiliationIgnoredEnding, CountryAlias, MeetingRegistration
|
||||||
from ietf.name.models import CountryName
|
from ietf.name.models import CountryName
|
||||||
from ietf.person.models import Person, Email, Alias
|
from ietf.person.models import Person, Email
|
||||||
from ietf.person.name import unidecode_name
|
|
||||||
from ietf.utils.log import log
|
from ietf.utils.log import log
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger('django')
|
||||||
|
|
||||||
|
|
||||||
def compile_affiliation_ending_stripping_regexp():
|
def compile_affiliation_ending_stripping_regexp():
|
||||||
parts = []
|
parts = []
|
||||||
|
@ -250,7 +251,7 @@ def get_meeting_registration_data(meeting):
|
||||||
raise RuntimeError("Could not decode response from registrations API: '%s...'" % (response.content[:64], ))
|
raise RuntimeError("Could not decode response from registrations API: '%s...'" % (response.content[:64], ))
|
||||||
|
|
||||||
records = MeetingRegistration.objects.filter(meeting_id=meeting.pk).select_related('person')
|
records = MeetingRegistration.objects.filter(meeting_id=meeting.pk).select_related('person')
|
||||||
meeting_registrations = {r.email:r for r in records}
|
meeting_registrations = {(r.email, r.reg_type):r for r in records}
|
||||||
for registration in decoded:
|
for registration in decoded:
|
||||||
person = None
|
person = None
|
||||||
# capture the stripped registration values for later use
|
# capture the stripped registration values for later use
|
||||||
|
@ -259,11 +260,15 @@ def get_meeting_registration_data(meeting):
|
||||||
affiliation = registration['Company'].strip()
|
affiliation = registration['Company'].strip()
|
||||||
country_code = registration['Country'].strip()
|
country_code = registration['Country'].strip()
|
||||||
address = registration['Email'].strip()
|
address = registration['Email'].strip()
|
||||||
if address in meeting_registrations:
|
reg_type = registration['RegType'].strip()
|
||||||
object = meeting_registrations[address]
|
if (address, reg_type) in meeting_registrations:
|
||||||
|
object = meeting_registrations.pop((address, reg_type))
|
||||||
created = False
|
created = False
|
||||||
else:
|
else:
|
||||||
object = MeetingRegistration.objects.create(meeting_id=meeting.pk, email=address)
|
object = MeetingRegistration.objects.create(
|
||||||
|
meeting_id=meeting.pk,
|
||||||
|
email=address,
|
||||||
|
reg_type=reg_type)
|
||||||
created = True
|
created = True
|
||||||
|
|
||||||
if (object.first_name != first_name[:200] or
|
if (object.first_name != first_name[:200] or
|
||||||
|
@ -286,65 +291,7 @@ def get_meeting_registration_data(meeting):
|
||||||
person = emails.first().person
|
person = emails.first().person
|
||||||
# Create a new Person object
|
# Create a new Person object
|
||||||
else:
|
else:
|
||||||
try:
|
logger.error("No Person record for registration. email={}".format(address))
|
||||||
# Normalize all-caps or all-lower entries. Don't touch
|
|
||||||
# others, there might be names properly spelled with
|
|
||||||
# internal uppercase letters.
|
|
||||||
if ( ( first_name == first_name.upper() or first_name == first_name.lower() )
|
|
||||||
and ( last_name == last_name.upper() or last_name == last_name.lower() ) ):
|
|
||||||
first_name = first_name.capitalize()
|
|
||||||
last_name = last_name.capitalize()
|
|
||||||
regname = "%s %s" % (first_name, last_name)
|
|
||||||
# if there are any unicode characters decode the string to ascii
|
|
||||||
ascii_name = unidecode_name(regname)
|
|
||||||
|
|
||||||
# Create a new user object if it does not exist already
|
|
||||||
# if the user already exists do not try to create a new one
|
|
||||||
users = User.objects.filter(username=address)
|
|
||||||
if users.exists():
|
|
||||||
user = users.first()
|
|
||||||
else:
|
|
||||||
# Create a new user.
|
|
||||||
user = User.objects.create(
|
|
||||||
first_name=first_name[:30],
|
|
||||||
last_name=last_name[:30],
|
|
||||||
username=address,
|
|
||||||
email=address,
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
person = user.person
|
|
||||||
except Person.DoesNotExist:
|
|
||||||
aliases = Alias.objects.filter(name=regname)
|
|
||||||
if aliases.exists():
|
|
||||||
person = aliases.first().person
|
|
||||||
else:
|
|
||||||
# Create the new Person object.
|
|
||||||
person = Person.objects.create(
|
|
||||||
name=regname,
|
|
||||||
ascii=ascii_name,
|
|
||||||
user=user,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Create an associated Email address for this Person
|
|
||||||
try:
|
|
||||||
email = Email.objects.get(person=person, address=address[:64])
|
|
||||||
except Email.DoesNotExist:
|
|
||||||
email = Email.objects.create(person=person, address=address[:64], origin='registration: ietf-%s'%meeting.number)
|
|
||||||
|
|
||||||
# If this is the only email address, set primary to true.
|
|
||||||
# If the person already existed (found through Alias) and
|
|
||||||
# had email addresses, we don't do this.
|
|
||||||
if Email.objects.filter(person=person).count() == 1:
|
|
||||||
email.primary = True
|
|
||||||
email.save()
|
|
||||||
except:
|
|
||||||
debug.show('first_name')
|
|
||||||
debug.show('last_name')
|
|
||||||
debug.show('regname')
|
|
||||||
debug.show('user')
|
|
||||||
debug.show('aliases')
|
|
||||||
raise
|
|
||||||
# update the person object to an actual value
|
# update the person object to an actual value
|
||||||
object.person = person
|
object.person = person
|
||||||
object.save()
|
object.save()
|
||||||
|
@ -352,9 +299,23 @@ def get_meeting_registration_data(meeting):
|
||||||
if created:
|
if created:
|
||||||
num_created += 1
|
num_created += 1
|
||||||
num_processed += 1
|
num_processed += 1
|
||||||
|
|
||||||
|
# handle deleted registrations, if count is reasonable
|
||||||
|
# any registrations left in meeting_registrations no longer exist in reg
|
||||||
|
# so must have been deleted
|
||||||
|
if 0 < len(meeting_registrations) < 5:
|
||||||
|
for r in meeting_registrations:
|
||||||
|
try:
|
||||||
|
MeetingRegistration.objects.get(meeting=meeting,email=r[0],reg_type=r[1]).delete()
|
||||||
|
logger.info('Removing deleted registration. email={}, reg_type={}'.format(r[0], r[1]))
|
||||||
|
except MeetingRegistration.DoesNotExist:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("Bad response from registrations API: %s, '%s'" % (response.status_code, response.content))
|
raise RuntimeError("Bad response from registrations API: %s, '%s'" % (response.status_code, response.content))
|
||||||
num_total = MeetingRegistration.objects.filter(meeting_id=meeting.pk).count()
|
num_total = MeetingRegistration.objects.filter(
|
||||||
|
meeting_id=meeting.pk,
|
||||||
|
attended=True,
|
||||||
|
reg_type__in=['onsite', 'remote']).count()
|
||||||
if meeting.attendees is None or num_total > meeting.attendees:
|
if meeting.attendees is None or num_total > meeting.attendees:
|
||||||
meeting.attendees = num_total
|
meeting.attendees = num_total
|
||||||
meeting.save()
|
meeting.save()
|
||||||
|
|
|
@ -811,7 +811,10 @@ def meeting_stats(request, num=None, stats_type=None):
|
||||||
return email.utils.formataddr(((r.first_name + " " + r.last_name).strip(), r.email))
|
return email.utils.formataddr(((r.first_name + " " + r.last_name).strip(), r.email))
|
||||||
|
|
||||||
if meeting and any(stats_type == t[0] for t in possible_stats_types):
|
if meeting and any(stats_type == t[0] for t in possible_stats_types):
|
||||||
attendees = MeetingRegistration.objects.filter(meeting=meeting, attended=True)
|
attendees = MeetingRegistration.objects.filter(
|
||||||
|
meeting=meeting,
|
||||||
|
attended=True,
|
||||||
|
reg_type__in=['onsite', 'remote'])
|
||||||
|
|
||||||
if stats_type == "country":
|
if stats_type == "country":
|
||||||
stats_title = "Number of attendees for {} {} per country".format(meeting.type.name, meeting.number)
|
stats_title = "Number of attendees for {} {} per country".format(meeting.type.name, meeting.number)
|
||||||
|
@ -883,7 +886,10 @@ def meeting_stats(request, num=None, stats_type=None):
|
||||||
elif not meeting and any(stats_type == t[0] for t in possible_stats_types):
|
elif not meeting and any(stats_type == t[0] for t in possible_stats_types):
|
||||||
template_name = "overview"
|
template_name = "overview"
|
||||||
|
|
||||||
attendees = MeetingRegistration.objects.filter(meeting__type="ietf", attended=True).select_related('meeting')
|
attendees = MeetingRegistration.objects.filter(
|
||||||
|
meeting__type="ietf",
|
||||||
|
attended=True,
|
||||||
|
reg_type__in=['onsite', 'remote']).select_related('meeting')
|
||||||
|
|
||||||
if stats_type == "overview":
|
if stats_type == "overview":
|
||||||
stats_title = "Number of attendees per meeting"
|
stats_title = "Number of attendees per meeting"
|
||||||
|
|
Loading…
Reference in a new issue