From c55e7e8e2089a60e67a5b1f3ee4d59afa41bb2e9 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lemburg Date: Sun, 23 Jun 2019 22:26:11 +0200 Subject: [PATCH 1/5] Fix a couple more management commands. --- p3/management/commands/accepted_talks.py | 19 +++------- .../commands/attendify_schedule_xlsx.py | 38 +++++++++---------- .../commands/attendify_speakers_xlsx.py | 32 +++++++--------- p3/management/commands/create_coupon_batch.py | 26 ++++++++++--- 4 files changed, 59 insertions(+), 56 deletions(-) diff --git a/p3/management/commands/accepted_talks.py b/p3/management/commands/accepted_talks.py index 331c8ac73..eb31fde5a 100644 --- a/p3/management/commands/accepted_talks.py +++ b/p3/management/commands/accepted_talks.py @@ -42,23 +42,16 @@ def speaker_listing(talk): ### class Command(BaseCommand): - option_list = BaseCommand.option_list + ( - # make_option('--option', - # action='store', - # dest='option_attr', - # default=0, - # type='int', - # help='Help text', - # ), - ) args = '' + def add_arguments(self, parser): + + # Positional arguments + parser.add_argument('conference') + def handle(self, *args, **options): - try: - conference = args[0] - except IndexError: - raise CommandError('conference not specified') + conference = options['conference'] talks = (models.Talk.objects .filter(conference=conference, diff --git a/p3/management/commands/attendify_schedule_xlsx.py b/p3/management/commands/attendify_schedule_xlsx.py index 872c3e9b3..e1c188b5a 100644 --- a/p3/management/commands/attendify_schedule_xlsx.py +++ b/p3/management/commands/attendify_schedule_xlsx.py @@ -38,7 +38,16 @@ _debug = 0 # These must match the talk .type or .admin_type -from .accepted_talks import TYPE_NAMES +TYPE_NAMES = ( + ('k', 'Keynotes', ''), + ('t', 'Talks', ''), + ('r', 'Training sessions', ''), + ('p', 'Poster sessions', ''), + ('i', 'Interactive sessions', ''), + ('n', 'Panels', ''), + ('h', 'Help desks', 'Help desks provide slots for attendees to discuss their problems one-on-one with experts from the projects.'), + ('m', 'EuroPython sessions', 'The EuroPython sessions are intended for anyone interested in helping with the EuroPython organization in the coming years.'), + ) # Special handling of poster sessions if 0: @@ -53,7 +62,7 @@ # Plenary sessions will have 2-3 tracks assigned. We use the # plenary room in this case. -PLENARY_ROOM = 'Smarkets' +PLENARY_ROOM = 'MongoDB' # Breaks have more than 3 tracks assigned. Since this changes between # the days, we don't set the room name. @@ -274,27 +283,18 @@ def update_schedule(schedule_xlsx, new_data, updated_xlsx=None): ### class Command(BaseCommand): - option_list = BaseCommand.option_list + ( - # make_option('--option', - # action='store', - # dest='option_attr', - # default=0, - # type='int', - # help='Help text', - # ), - ) args = ' ' + def add_arguments(self, parser): + + # Positional arguments + parser.add_argument('conference') + parser.add_argument('xlsx') + def handle(self, *args, **options): - try: - conference = args[0] - except IndexError: - raise CommandError('conference not specified') - try: - schedule_xlsx = args[1] - except IndexError: - raise CommandError('XLSX file not specified') + conference = options['conference'] + schedule_xlsx = options['xlsx'] talks = (models.Talk.objects .filter(conference=conference, diff --git a/p3/management/commands/attendify_speakers_xlsx.py b/p3/management/commands/attendify_speakers_xlsx.py index b9958cece..23eaf3acc 100644 --- a/p3/management/commands/attendify_speakers_xlsx.py +++ b/p3/management/commands/attendify_speakers_xlsx.py @@ -40,6 +40,9 @@ # Debug output ? _debug = 1 +# URL prefix to use in URLs +URL_PREFIX = settings.DEFAULT_URL_PREFIX or 'https://ep2019.europython.eu' + ### Helpers def profile_url(user): @@ -88,7 +91,7 @@ def add_speaker(data, speaker): company = profile.company position = profile.job_title profile_text = ('Profile on EuroPython Website' % - (settings.DEFAULT_URL_PREFIX, profile_url(user))) + (URL_PREFIX, profile_url(user))) twitter = p3profile.twitter if twitter.startswith(('https://twitter.com/', 'http://twitter.com/')): twitter = twitter.split('/')[-1] @@ -143,7 +146,7 @@ def update_speakers(speakers_xlsx, new_data, updated_xlsx=None): print('first line: %r' % ws_data[:1]) print('last line: %r' % ws_data[-1:]) - # Reconcile UIDs / talks + # Reconcile UIDs / speakers uids = {} for line in ws_data: uid = line[SPEAKERS_UID_COLUMN] @@ -196,27 +199,18 @@ def update_speakers(speakers_xlsx, new_data, updated_xlsx=None): ### class Command(BaseCommand): - option_list = BaseCommand.option_list + ( - # make_option('--option', - # action='store', - # dest='option_attr', - # default=0, - # type='int', - # help='Help text', - # ), - ) args = ' ' + def add_arguments(self, parser): + + # Positional arguments + parser.add_argument('conference') + parser.add_argument('xlsx') + def handle(self, *args, **options): - try: - conference = args[0] - except IndexError: - raise CommandError('conference not specified') - try: - speakers_xlsx = args[1] - except IndexError: - raise CommandError('XLSX file not specified') + conference = options['conference'] + speakers_xlsx = options['xlsx'] # Get speaker records speakers = set() diff --git a/p3/management/commands/create_coupon_batch.py b/p3/management/commands/create_coupon_batch.py index 2448f9856..e21c3142b 100644 --- a/p3/management/commands/create_coupon_batch.py +++ b/p3/management/commands/create_coupon_batch.py @@ -50,33 +50,49 @@ class Command(BaseCommand): # Dry run ? dry_run = False + def add_arguments(self, parser): + + # Positional arguments + parser.add_argument('conference') + parser.add_argument('ticket_code') + parser.add_argument('count') + parser.add_argument('amount') + parser.add_argument('coupon_code') + + # Named (optional) arguments + parser.add_argument('--dry-run', + action='store_true', + dest='dry_run', + default=False, + help='Do everything except create the coupons') + @transaction.atomic def handle(self, *args, **options): self.dry_run = options.get('dry_run', False) try: - conference = cmodels.Conference.objects.get(code=args[0]) + conference = cmodels.Conference.objects.get(code=options['conference']) except IndexError: raise CommandError('conference missing') try: - ticket_code = str(args[1]) + ticket_code = str(options['ticket_code']) except IndexError: raise CommandError('ticket code missing') try: - number_of_coupons = int(args[2]) + number_of_coupons = int(options['count']) except IndexError: raise CommandError('coupon count missing') try: amount_per_coupon = str(args[3]) except IndexError: - raise CommandError('coupon discount amount missing') + raise CommandError(options['amount']) try: - coupon_prefix = str(args[4]) + coupon_prefix = str(options['coupon_code']) except IndexError: coupon_prefix = DEFAULT_PREFIX From 50c323b57a4321cad2b265fdf4b47f30fbacd177 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lemburg Date: Fri, 5 Jul 2019 21:48:25 +0200 Subject: [PATCH 2/5] Fix video_schedule_xlsx.py --- p3/management/commands/video_schedule_xlsx.py | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/p3/management/commands/video_schedule_xlsx.py b/p3/management/commands/video_schedule_xlsx.py index b7559555b..333f6c59c 100644 --- a/p3/management/commands/video_schedule_xlsx.py +++ b/p3/management/commands/video_schedule_xlsx.py @@ -309,27 +309,18 @@ def update_schedule(schedule_xlsx, new_data, updated_xlsx=None): ### class Command(BaseCommand): - option_list = BaseCommand.option_list + ( - # make_option('--option', - # action='store', - # dest='option_attr', - # default=0, - # type='int', - # help='Help text', - # ), - ) args = ' ' + def add_arguments(self, parser): + + # Positional arguments + parser.add_argument('conference') + parser.add_argument('xlsx') + def handle(self, *args, **options): - try: - conference = args[0] - except IndexError: - raise CommandError('conference not specified') - try: - schedule_xlsx = args[1] - except IndexError: - raise CommandError('XLSX file not specified') + conference = options['conference'] + schedule_xlsx = options['xlsx'] talks = (models.Talk.objects .filter(conference=conference, From e5bb9678aa8e0117e6f3766570953ee056909549 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lemburg Date: Fri, 5 Jul 2019 22:16:17 +0200 Subject: [PATCH 3/5] Fix ticket search apps. --- .../build_social_ticket_search_app.py | 28 ++++++------------- .../commands/build_ticket_search_app.py | 28 ++++++------------- 2 files changed, 18 insertions(+), 38 deletions(-) diff --git a/p3/management/commands/build_social_ticket_search_app.py b/p3/management/commands/build_social_ticket_search_app.py index 2d3a50534..c40e8d68e 100644 --- a/p3/management/commands/build_social_ticket_search_app.py +++ b/p3/management/commands/build_social_ticket_search_app.py @@ -186,28 +186,18 @@ def create_app_file(conference, output_file): ### class Command(BaseCommand): - option_list = BaseCommand.option_list + ( - # make_option('--output', - # action='store', - # dest='talk_status', - # default='accepted', - # choices=['accepted', 'proposed'], - # help='The status of the talks to be put in the report. ' - # 'Choices: accepted, proposed', - # ), - ) args = ' []' + def add_arguments(self, parser): + + # Positional arguments + parser.add_argument('conference') + parser.add_argument('output_file', nargs='?', + default='ep-social-ticket-search-app/index.html') + def handle(self, *args, **options): - - try: - conference = args[0] - except IndexError: - raise CommandError('conference not specified') - try: - output_file = args[1] - except IndexError: - output_file = 'ep-social-ticket-search-app/index.html' + conference = options['conference'] + output_file = options['output_file'] create_app_file(conference, output_file) diff --git a/p3/management/commands/build_ticket_search_app.py b/p3/management/commands/build_ticket_search_app.py index 4f5553e16..84a81c63a 100644 --- a/p3/management/commands/build_ticket_search_app.py +++ b/p3/management/commands/build_ticket_search_app.py @@ -229,28 +229,18 @@ def create_app_file(conference, output_file): ### class Command(BaseCommand): - option_list = BaseCommand.option_list + ( - # make_option('--output', - # action='store', - # dest='talk_status', - # default='accepted', - # choices=['accepted', 'proposed'], - # help='The status of the talks to be put in the report. ' - # 'Choices: accepted, proposed', - # ), - ) args = ' []' + def add_arguments(self, parser): + + # Positional arguments + parser.add_argument('conference') + parser.add_argument('output_file', nargs='?', + default='ep-ticket-search-app/index.html') + def handle(self, *args, **options): - - try: - conference = args[0] - except IndexError: - raise CommandError('conference not specified') - try: - output_file = args[1] - except IndexError: - output_file = 'ep-ticket-search-app/index.html' + conference = options['conference'] + output_file = options['output_file'] create_app_file(conference, output_file) From 17588d207fe5e77f096ee05e329cbcb5dee6a732 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lemburg Date: Fri, 5 Jul 2019 22:59:05 +0200 Subject: [PATCH 4/5] Fix ticket search app for EP2019. --- .../build_social_ticket_search_app.py | 24 +++++++------ .../commands/build_ticket_search_app.py | 35 ++++++++++++------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/p3/management/commands/build_social_ticket_search_app.py b/p3/management/commands/build_social_ticket_search_app.py index c40e8d68e..a96514add 100644 --- a/p3/management/commands/build_social_ticket_search_app.py +++ b/p3/management/commands/build_social_ticket_search_app.py @@ -94,25 +94,29 @@ def profile_url(user): def attendee_name(ticket, profile=None): + # XXX For EP2019, we have to use the ticket.name, since the profile + # will be referring to the buyer's profile in many cases due + # to a bug in the system. + # See https://github.com/EuroPython/epcon/issues/1055 + + # Use ticket name if not set in profile + name = ticket.name.strip() + # Determine user name from profile, if available - if profile is not None: + if not name and profile is not None: name = '%s %s' % ( profile.user.first_name, profile.user.last_name) - else: - name = '' + name = name.strip() - # Remove whitespace - name = name.strip() - - # Use ticket name if not set in profile - if not name: - name = ticket.name.strip() - # Convert to title case, if not an email address if '@' not in name: name = name.title() + # Use email address if no ticket name set + if not name: + name = ticket.p3_conference.assigned_to.strip() + return name def attendee_list_key(entry): diff --git a/p3/management/commands/build_ticket_search_app.py b/p3/management/commands/build_ticket_search_app.py index 84a81c63a..60029e48f 100644 --- a/p3/management/commands/build_ticket_search_app.py +++ b/p3/management/commands/build_ticket_search_app.py @@ -47,6 +47,9 @@ .training { color: blue; } +.combined { + color: green; +} .conference { color: red; } @@ -105,21 +108,21 @@ def profile_url(user): def attendee_name(ticket, profile=None): + # XXX For EP2019, we have to use the ticket.name, since the profile + # will be referring to the buyer's profile in many cases due + # to a bug in the system. + # See https://github.com/EuroPython/epcon/issues/1055 + + # Use ticket name if not set in profile + name = ticket.name.strip() + # Determine user name from profile, if available - if profile is not None: + if not name and profile is not None: name = '%s %s' % ( profile.user.first_name, profile.user.last_name) - else: - name = '' + name = name.strip() - # Remove whitespace - name = name.strip() - - # Use ticket name if not set in profile - if not name: - name = ticket.name.strip() - # Convert to title case, if not an email address if '@' not in name: name = name.title() @@ -175,6 +178,10 @@ def create_app_file(conference, output_file): sys.stderr.write('multiple users accounts for %r\n' % ticket.p3_conference.assigned_to) profile = None + if ticket.id in attendee_dict: + # Duplicate ticket.id; should not happen + sys.stderr.write('duplicate ticket.id %r for %r\n' % + (ticket.id, ticket.p3_conference.assigned_to)) name = attendee_name(ticket, profile) attendee_dict[ticket.id] = ( ticket, @@ -202,6 +209,8 @@ def create_app_file(conference, output_file): code = ticket.fare.code if ticket.fare.code.startswith('TRT'): ticket_class = 'training' + elif ticket.fare.code.startswith('TRC'): + ticket_class = 'combined' else: ticket_class = 'conference' l.append(('' @@ -218,8 +227,10 @@ def create_app_file(conference, output_file): l.extend(['', '', '

%i tickets in total. ' - 'Color coding: TID = Training Pass. ' - 'TID = Conference Ticket.

' % len(attendee_list), + 'Color coding: TID = Training Ticket. ' + 'TID = Combined Ticket. ' + 'TID = Conference Ticket.

' % + len(attendee_list), ]) output.write((TEMPLATE % { 'listing': '\n'.join(l), From 913cd7f9c3a55e50b44987ce24ec885b1cfe8fc1 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lemburg Date: Tue, 16 Jul 2019 17:07:40 +0200 Subject: [PATCH 5/5] Adjust speaker release form URL. --- p3/management/commands/video_schedule_xlsx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p3/management/commands/video_schedule_xlsx.py b/p3/management/commands/video_schedule_xlsx.py index 333f6c59c..a07d308f1 100644 --- a/p3/management/commands/video_schedule_xlsx.py +++ b/p3/management/commands/video_schedule_xlsx.py @@ -39,7 +39,7 @@ LICENSE = """ License: This video is licensed under the CC BY-NC-SA 3.0 license: https://creativecommons.org/licenses/by-nc-sa/3.0/ -Please see our speaker release agreement for details: https://ep2018.europython.eu/en/speaker-release-agreement/ +Please see our speaker release agreement for details: https://ep2019.europython.eu/events/speaker-release-agreement/ """ # Special handling of poster sessions