def code_summary():
    '''Admin only - return a summary of all codes for input into RegOnline.'''

    data = {
        'eventID' : app.config['SPONSOR_EVENT'],
        'api_key' : app.config['APP_KEY']
    }
    discount_codes = requests.post( "%s/data/discounts/" % ( app.config['APP_SERVER'] ), json.dumps( data ) ).json()['discount_codes']

    codes_by_type = {}
    last_updated_by_type = {}

    def get_date( date_string ):
        return datetime.datetime.strptime( date_string, "%a, %d %b %Y %H:%M:%S %Z" )

    badge_types = get_badge_types( app.config['SPONSOR_EVENT'] )

    discount_codes.sort( key=lambda x:['created_date'] )

    for discount_code in discount_codes:
        if discount_code['badge_type'] in codes_by_type:
            codes_by_type[discount_code['badge_type']] += ",%s=%s(%d)" % ( discount_code['discount_code'], discount_code['regonline_str'], discount_code['quantity'] )

            if get_date( last_updated_by_type[discount_code['badge_type']] ) < get_date( discount_code['created_date'] ):
                last_updated_by_type[discount_code['badge_type']] = discount_code['created_date']
        else:
            codes_by_type[discount_code['badge_type']] = "%s=%s(%d)" % ( discount_code['discount_code'], discount_code['regonline_str'], discount_code['quantity'] )
            last_updated_by_type[discount_code['badge_type']] = discount_code['created_date']

    code_summary = sorted( [ { "label" : v['name'], "regonline_code_string" : codes_by_type.get( k, '' ), "last_updated" : last_updated_by_type.get( k, 'N/A' ) } for k, v in badge_types.items() ] )
       
    return render_template( "code_summary.html", code_summary=code_summary )
Example #2
0
def code_summary():
    '''Admin only - return a summary of all codes for input into RegOnline.'''

    data = {
        'eventID': app.config['SPONSOR_EVENT'],
        'api_key': app.config['APP_KEY']
    }
    discount_codes = requests.post(
        "%s/data/discounts/" % (app.config['APP_SERVER']),
        json.dumps(data)).json()['discount_codes']

    codes_by_type = {}
    last_updated_by_type = {}

    def get_date(date_string):
        return datetime.datetime.strptime(date_string,
                                          "%a, %d %b %Y %H:%M:%S %Z")

    badge_types = get_badge_types(app.config['SPONSOR_EVENT'])

    discount_codes.sort(key=lambda x: ['created_date'])

    for discount_code in discount_codes:
        if discount_code['badge_type'] in codes_by_type:
            codes_by_type[discount_code['badge_type']] += ",%s=%s(%d)" % (
                discount_code['discount_code'], discount_code['regonline_str'],
                discount_code['quantity'])

            if get_date(last_updated_by_type[discount_code['badge_type']]
                        ) < get_date(discount_code['created_date']):
                last_updated_by_type[discount_code[
                    'badge_type']] = discount_code['created_date']
        else:
            codes_by_type[discount_code['badge_type']] = "%s=%s(%d)" % (
                discount_code['discount_code'], discount_code['regonline_str'],
                discount_code['quantity'])
            last_updated_by_type[
                discount_code['badge_type']] = discount_code['created_date']

    code_summary = sorted([{
        "label": v['name'],
        "regonline_code_string": codes_by_type.get(k, ''),
        "last_updated": last_updated_by_type.get(k, 'N/A')
    } for k, v in badge_types.items()])

    return render_template("code_summary.html", code_summary=code_summary)
Example #3
0
def sponsor_summary():
    '''Will eventually be limited to particular sponsors, right now admin only.'''

    sponsor_email = None
    if 'sponsor_email' in request.values:
        if validate_email(request.values['sponsor_email'].strip().lower()):
            sponsor_email = request.values['sponsor_email'].strip().lower()

    # DEBUG - hard code this for now.
    #sponsor_email = "*****@*****.**"

    data = {
        'eventID': app.config['SPONSOR_EVENT'],
        'api_key': app.config['APP_KEY']
    }
    sponsors = [
        x for x in requests.post(
            "%s/data/sponsors/" %
            (app.config['APP_SERVER']), json.dumps(data)).json()['sponsors']
        if (x['Email'].strip().lower() == sponsor_email
            or x['CCEmail'].strip().lower() == sponsor_email)
    ]
    sponsors_by_id = {x['ID']: x for x in sponsors}

    discount_codes = [
        x for x in requests.post(
            "%s/data/discounts/" % (app.config['APP_SERVER']), json.dumps(
                data)).json()['discount_codes']
        if x['SponsorID'] in sponsors_by_id
    ]
    discounts_by_code = {x['discount_code']: x for x in discount_codes}

    include_company_data = False

    if include_company_data:
        # Try to build up a list of alternate company names that
        # registrants might have entered in the company box that all mean
        # the same thing.
        company_suffixes = [
            'co', 'co.', 'corp', 'corp.', 'corporation', 'inc', 'inc.',
            'incorporated', 'llc', 'llc.'
        ]
        companies = {}
        for sponsor in sponsors:
            if len(sponsor['Company'].strip()):
                company = sponsor['Company'].lower().strip()
                company = ' '.join(company.split())
                companies[company] = True

                # Remove any suffixes and trailing commas.
                words = company.split()
                if len(words) > 1:
                    if words[-1] in company_suffixes:
                        if words[-2][-1] == ',':
                            words[-2] = words[-2][:-1]
                        words = [w for w in words if len(w)]
                    companies[' '.join(words)] = True

    data = {
        'eventID': app.config['REGISTRANT_EVENT'],
        'api_key': app.config['APP_KEY']
    }
    all_registrants = requests.post(
        "%s/data/registrants/" % (app.config['APP_SERVER']),
        json.dumps(data)).json()['registrants']

    # Boil down our registrants to those who used one of our codes, or
    # those who come from our company.
    registrants = [
        x for x in all_registrants if x['discount_code'] in discounts_by_code
    ]

    if include_company_data:
        other_registrants = [
            x for x in all_registrants
            if x['discount_code'] not in discounts_by_code
        ]
        registrants += [
            x for x in other_registrants if x['Company'] in companies
        ]

    badge_types = get_badge_types(app.config['SPONSOR_EVENT'])

    # ==========================================================================
    # Handle the case where we are sending an reminder discount code email.

    if "send_email" in request.values:
        sponsorID = int(request.values['sponsorID'])

        email_recipients = []
        try:
            email_recipients_raw = request.values['email_recipients'].replace(
                ';', ',')
            email_recipients = [
                x.strip() for x in email_recipients_raw.split(',')
            ]
        except Exception as e:
            flash(
                "Invalid email address argument! Please enter a list of email addresses separated by commas."
            )

        sponsor = [x for x in sponsors if x['ID'] == sponsorID][0]

        sponsor_discount_codes = [
            x for x in discount_codes if x['SponsorID'] == sponsor['ID']
        ]

        mail_sent = False
        try:
            # DEBUG, leave this variable alone here and actually send to the recipients in production.
            email_recipients = app.config['ADMIN_MAIL_RECIPIENTS']

            mail_message = Message(
                "Grace Hopper Celebration 2015 Discount Codes",
                sender=app.config['SEND_AS'],
                recipients=email_recipients,
                bcc=app.config['ADMIN_MAIL_RECIPIENTS'])

            for discount_code in sorted(sponsor_discount_codes,
                                        key=lambda x: x['discount_code']):

                discount_code['badge_type_name'] = badge_types[
                    discount_code['badge_type']]['name']
                discount_code['regonline_url'] = badge_types[
                    discount_code['badge_type']]['regonline_url']

                discount_code['discount_search_url'] = "%s/%s?code=%s" % (
                    app.config['EXTERNAL_SERVER_BASE_URL'],
                    url_for('discount_code'), discount_code['discount_code'])

            mail_message.html = render_template(
                "email_discount_code_summary.html",
                data={
                    'sponsor': sponsor,
                    'discount_codes': sponsor_discount_codes
                })

            mail.init_app(app)
            mail.send(mail_message)
            mail_sent = True
        except Exception as e:
            flash("ERROR! Failed to send discount code summary to: %s. %s" %
                  (email_recipients + app.config['ADMIN_MAIL_RECIPIENTS'], e))

        if mail_sent:
            # DEBUG - remove the admin mail recipients from the flash notification.
            success_message = "Discount summary email sent to: %s" % (
                email_recipients + app.config['ADMIN_MAIL_RECIPIENTS'])
            flash(success_message)

    # ==========================================================================
    # Now display all the summary data.

    # Attendees who did not redeem a code.
    nonsponsored = 0
    # Attendees who did use a code.
    redeemed = 0
    # Code entitlements not accounted for.
    quantity = 0

    sponsors_by_id = {x['ID']: x for x in sponsors}

    # Used to aggregate some reports about Academic, Corporate, etc. sponsors and their registrants.
    sponsor_reporting_groups = get_sponsor_reporting_groups(
        app.config['SPONSOR_EVENT'])
    group_attendee_stats = {
        'Other Sponsored': {
            'redeemed': 0,
            'quantity': 0
        },
    }

    # Used to compute redeemed / available for each particular sponsor
    # code.
    codes_by_sponsor = {}

    for discount_code in discount_codes:
        if discount_code['SponsorID'] in codes_by_sponsor:
            codes_by_sponsor[discount_code['SponsorID']].append(discount_code)
        else:
            codes_by_sponsor[discount_code['SponsorID']] = [discount_code]

        if discount_code['badge_type'] in badge_types:
            if badge_types[discount_code['badge_type']]['reserve_spot']:
                quantity += int(discount_code['quantity'])

                sponsor_reporting_group = sponsor_reporting_groups.get(
                    sponsors_by_id[discount_code['SponsorID']]
                    ['RegistrationType'], 'Other Sponsored')
                if sponsor_reporting_group in group_attendee_stats:
                    group_attendee_stats[sponsor_reporting_group][
                        'quantity'] += discount_code['quantity']
                else:
                    group_attendee_stats[sponsor_reporting_group] = {
                        'quantity': discount_code['quantity'],
                        'redeemed': 0
                    }
        else:
            logging.warning(
                json.dumps({
                    'message':
                    'Unknown badge_type: %s found in discount codes.' %
                    (discount_code['badge_type'])
                }))

    redemptions_by_code = {}

    for registrant in registrants:
        if registrant['discount_code']:
            discount_code = discounts_by_code.get(registrant['discount_code'],
                                                  {})

            if discount_code.get('badge_type', None) in badge_types:
                if badge_types[discount_code['badge_type']]['reserve_spot']:

                    sponsor_reporting_group = 'Other Sponsored'
                    if 'SponsorID' in discount_code:
                        if discount_code['SponsorID'] in sponsors_by_id:
                            if sponsors_by_id[discount_code['SponsorID']][
                                    'RegistrationType'] in sponsor_reporting_groups:
                                sponsor_reporting_group = sponsor_reporting_groups[
                                    sponsors_by_id[discount_code['SponsorID']]
                                    ['RegistrationType']]

                    redeemed += 1
                    group_attendee_stats[sponsor_reporting_group][
                        'redeemed'] += 1

                if registrant['discount_code'] in redemptions_by_code:
                    redemptions_by_code[registrant['discount_code']] += 1
                else:
                    redemptions_by_code[registrant['discount_code']] = 1
            else:
                nonsponsored += 1
        else:
            nonsponsored += 1

    for group_name, group_stats in group_attendee_stats.items():
        group_stats[
            'reserved'] = group_stats['quantity'] - group_stats['redeemed']

    for sponsor, codes in codes_by_sponsor.items():
        for code in codes:
            if code['discount_code'] in redemptions_by_code:
                code['redeemed'] = redemptions_by_code[code['discount_code']]
                code['available'] = code['quantity'] - redemptions_by_code[
                    code['discount_code']]

    sponsors.sort(key=lambda x: x['Company'])

    for sponsor in sponsors:
        sponsor['discount_codes'] = codes_by_sponsor.get(sponsor['ID'], [])

    # Private function that strips down a registrant data to what we
    # can give out publicly to someone with the code.
    def get_fields(registrant):
        return {
            "name":
            "%s %s" % (registrant['FirstName'], registrant['LastName']),
            "company": registrant['Company'],
            "title": registrant['Title'],
            "status": registrant['StatusDescription'],
            "registration_type": registrant['RegistrationType'],
            "registration_date": registrant['AddDate']
        }

    public_registrants = [get_fields(x) for x in registrants]

    # ==========================================================================
    # Return the appropriate stuff for the whole HTML page, or a
    # particular CSV table.
    # ==========================================================================

    sponsor_summary = {
        "sponsor_email":
        sponsor_email,
        "sponsors":
        sponsors,
        "reserved":
        quantity - redeemed,
        "redeemed":
        redeemed,
        "nonsponsored":
        nonsponsored,
        "registered":
        nonsponsored + redeemed,
        "group_attendee_stats": [{
            "name": k,
            "data": group_attendee_stats[k]
        } for k in sorted(group_attendee_stats.keys())],
        "badge_type_names": [{
            "value": k,
            "name": badge_types[k]['name']
        } for k in sorted(badge_types.keys())],
        "registrants":
        public_registrants
    }

    if "download_csv" in request.values:
        download_content = request.values['download_content']

        if download_content == 'sponsor_summary_sponsors':

            csv_rows = [[
                'Company Name', 'Contact Email', 'Sponsorship',
                'Discount code', 'Discount code Report URL',
                'Discount code Redemption URL', 'Total', 'Redeemed', 'Unusued'
            ]]

            for sponsor in sponsor_summary['sponsors']:
                for discount_code in sponsor['discount_codes']:
                    csv_rows.append([
                        sponsor.get('Company', ''),
                        sponsor.get('Email', ''),
                        sponsor.get('RegistrationType', ''),
                        discount_code.get('discount_code', ''),
                        "%s/%s?code=%s" %
                        (app.config['EXTERNAL_SERVER_BASE_URL'],
                         url_for('discount_code'),
                         discount_code.get('discount_code', '')), badge_types[
                             discount_code['badge_type']]['regonline_url'],
                        discount_code.get('quantity', 0),
                        discount_code.get('redeemed', 0),
                        discount_code.get('available', 0)
                    ])

            download_filename = "sponsor-summary-%s.csv" % (
                datetime.datetime.now().strftime("%Y-%m-%d-%H%M%S"))

        elif download_content == 'sponsor_summary_registrants':
            csv_rows = [[
                'Name', 'Company', 'Title', 'Registration Type', 'Status',
                'Registration Date'
            ]]

            for attendee in sponsor_summary['registrants']:
                csv_rows.append([
                    attendee.get('name', ''),
                    attendee.get('company', ''),
                    attendee.get('title', ''),
                    attendee.get('registration_type', ''),
                    attendee.get('status', ''),
                    attendee.get('registration_date', '')
                ])

            download_filename = "sponsor-attendees-%s.csv" % (
                datetime.datetime.now().strftime("%Y-%m-%d-%H%M%S"))

        else:
            raise Exception("Unknown download_content type: %s requested." %
                            (download_content))

        si = StringIO.StringIO()
        writer = csv.writer(si)
        for row in csv_rows:
            writer.writerow(row)
        output = make_response(si.getvalue())
        output.headers["Content-Disposition"] = "attachment; filename=%s" % (
            download_filename)
        output.headers["Content-type"] = "text/csv"
        return output

    else:
        if 'sponsor_email' in request.values:
            if len(sponsors) == 0:
                flash("No data found for sponsor email: %s" %
                      (request.values['sponsor_email'].strip()))
            else:
                flash('Showing data for sponsor email: %s' %
                      (request.values['sponsor_email'].strip()))

        return render_template("sponsor_summary.html",
                               sponsor_summary=sponsor_summary)
Example #4
0
def registration_summary():
    '''Admin only, summaries all registration counts and sponsor data.'''

    data = {
        'eventID': app.config['SPONSOR_EVENT'],
        'api_key': app.config['APP_KEY']
    }

    discount_codes = requests.post(
        "%s/data/discounts/" % (app.config['APP_SERVER']),
        json.dumps(data)).json()['discount_codes']

    sponsors = requests.post("%s/data/sponsors/" % (app.config['APP_SERVER']),
                             json.dumps(data)).json()['sponsors']

    data = {
        'eventID': app.config['REGISTRANT_EVENT'],
        'api_key': app.config['APP_KEY']
    }
    registrants = requests.post(
        "%s/data/registrants/" % (app.config['APP_SERVER']),
        json.dumps(data)).json()['registrants']

    badge_types = get_badge_types(app.config['SPONSOR_EVENT'])

    # ==========================================================================
    # Handle the case where we are creating a new discount code for this sponsor.
    # ==========================================================================

    if "add_discount_code" in request.values:
        badge_type = request.values['badge_type']
        quantity = int(request.values['quantity'].strip())
        sponsorID = int(request.values['sponsorID'])

        sponsor = [x for x in sponsors if x['ID'] == sponsorID][0]

        discount_code = generate_discount_code(app.config['SPONSOR_EVENT'],
                                               sponsor, badge_type, quantity,
                                               discount_codes)

        def get_date_string(date):
            return date.strftime("%a, %d %b %Y %H:%M:%S %Z")

        discount_code['created_date'] = get_date_string(
            discount_code['created_date'])

        result = requests.post(
            "%s/data/discount_code/add/" % (app.config['APP_SERVER']),
            json.dumps({
                "eventID": app.config['SPONSOR_EVENT'],
                "discount_code_data": discount_code,
                "api_key": app.config['APP_KEY']
            }))

        mail_sent = False
        try:
            # DEBUG, add in mail recipients.
            mail_recipients = app.config['ADMIN_MAIL_RECIPIENTS']

            # We want this mail to go out at 4 pm on the next business
            # day.
            today = datetime.date.today()
            today_4pm = datetime.datetime(today.year,
                                          today.month,
                                          today.day,
                                          16,
                                          tzinfo=pytz.timezone('PST8PDT'))
            tomorrow_4pm = today_4pm + relativedelta(bdays=+1)
            when = tomorrow_4pm.astimezone(pytz.utc)

            extra_headers = {'X-MC-SendAt': when.strftime("%Y-%m-%d %H:%M:%S")}

            mail_message = Message(
                "Grace Hopper Celebration 2015 Discount Codes",
                sender=app.config['SEND_AS'],
                recipients=mail_recipients,
                bcc=app.config['ADMIN_MAIL_RECIPIENTS'],
                extra_headers=extra_headers)

            discount_search_url = "%s/%s?code=%s" % (
                app.config['EXTERNAL_SERVER_BASE_URL'],
                url_for('discount_code'), discount_code['discount_code'])

            mail_message.html = render_template(
                "email_add_discount_code.html",
                data={
                    'sponsor': sponsor,
                    'quantity': quantity,
                    'discount_code': discount_code,
                    'badge_type_name': badge_types[badge_type]['name'],
                    'regonline_url': badge_types[badge_type]['regonline_url'],
                    'discount_search_url': discount_search_url
                })

            mail.init_app(app)
            mail.send(mail_message)
            mail_sent = True
        except Exception as e:
            flash(
                "ERROR! Failed to send email notification of discount code creation to: %s."
                % (mail_recipients))

        if result.json()['success']:
            discount_codes.append(discount_code)

            success_message = 'Added %d %s badges to sponsor %s with discount code: %s.' % (
                quantity, badge_types[badge_type]['name'], sponsor['Company'],
                discount_code['discount_code'])

            if mail_sent:
                success_message += " Notification email sent to: %s" % (
                    mail_recipients)

            flash(success_message)
        else:
            raise Exception("Failed to add code!")

    # ==========================================================================

    # ==========================================================================
    # Handle the case where we are sending an reminder discount code email.

    if "send_email" in request.values:
        sponsorID = int(request.values['sponsorID'])

        email_recipients = []
        try:
            email_recipients_raw = request.values['email_recipients'].replace(
                ';', ',')
            email_recipients = [
                x.strip() for x in email_recipients_raw.split(',')
            ]
        except Exception as e:
            flash(
                "Invalid email address argument! Please enter a list of email addresses separated by commas."
            )

        sponsor = [x for x in sponsors if x['ID'] == sponsorID][0]

        sponsor_discount_codes = [
            x for x in discount_codes if x['SponsorID'] == sponsor['ID']
        ]

        mail_sent = False
        try:
            # DEBUG, leave this variable alone here and actually send to the recipients in production.
            email_recipients = app.config['ADMIN_MAIL_RECIPIENTS']

            mail_message = Message(
                "Grace Hopper Celebration 2015 Discount Codes",
                sender=app.config['SEND_AS'],
                recipients=email_recipients,
                bcc=app.config['ADMIN_MAIL_RECIPIENTS'])

            for discount_code in sorted(sponsor_discount_codes,
                                        key=lambda x: x['discount_code']):
                discount_code['badge_type_name'] = badge_types[
                    discount_code['badge_type']]['name']
                discount_code['regonline_url'] = badge_types[
                    discount_code['badge_type']]['regonline_url']

                discount_code['discount_search_url'] = "%s/%s?code=%s" % (
                    app.config['EXTERNAL_SERVER_BASE_URL'],
                    url_for('discount_code'), discount_code['discount_code'])

            mail_message.html = render_template(
                "email_discount_code_summary.html",
                data={
                    'sponsor': sponsor,
                    'discount_codes': sponsor_discount_codes
                })

            mail.init_app(app)
            mail.send(mail_message)
            mail_sent = True
        except Exception as e:
            flash("ERROR! Failed to send discount code summary to: %s." %
                  (email_recipients + app.config['ADMIN_MAIL_RECIPIENTS']))

        if mail_sent:
            success_message = "Discount summary email sent to: %s" % (
                email_recipients + app.config['ADMIN_MAIL_RECIPIENTS'])
            flash(success_message)

    # ==========================================================================
    # Now display all the summary data.

    # Attendees who did not redeem a code.
    nonsponsored = 0
    # Attendees who did use a code.
    redeemed = 0
    # Code entitlements not accounted for.
    quantity = 0

    sponsors_by_id = {x['ID']: x for x in sponsors}
    discounts_by_code = {}

    # Used to aggregate some reports about Academic, Corporate, etc. sponsors and their registrants.
    sponsor_reporting_groups = get_sponsor_reporting_groups(
        app.config['SPONSOR_EVENT'])
    group_attendee_stats = {
        'Other Sponsored': {
            'redeemed': 0,
            'quantity': 0
        },
    }

    # Used to compute redeemed / available for each particular sponsor
    # code.
    codes_by_sponsor = {}

    for discount_code in discount_codes:
        discounts_by_code[discount_code['discount_code']] = discount_code

        if discount_code['SponsorID'] in codes_by_sponsor:
            codes_by_sponsor[discount_code['SponsorID']].append(discount_code)
        else:
            codes_by_sponsor[discount_code['SponsorID']] = [discount_code]

        if discount_code['badge_type'] in badge_types:
            if badge_types[discount_code['badge_type']]['reserve_spot']:
                quantity += int(discount_code['quantity'])

                sponsor_reporting_group = sponsor_reporting_groups.get(
                    sponsors_by_id[discount_code['SponsorID']]
                    ['RegistrationType'], 'Other Sponsored')
                if sponsor_reporting_group in group_attendee_stats:
                    group_attendee_stats[sponsor_reporting_group][
                        'quantity'] += discount_code['quantity']
                else:
                    group_attendee_stats[sponsor_reporting_group] = {
                        'quantity': discount_code['quantity'],
                        'redeemed': 0
                    }
        else:
            logging.warning(
                json.dumps({
                    'message':
                    'Unknown badge_type: %s found in discount codes.' %
                    (discount_code['badge_type'])
                }))

    redemptions_by_code = {}

    for registrant in registrants:
        if registrant['discount_code']:
            discount_code = discounts_by_code.get(registrant['discount_code'],
                                                  {})

            if discount_code.get('badge_type', None) in badge_types:
                if badge_types[discount_code['badge_type']]['reserve_spot']:

                    sponsor_reporting_group = 'Other Sponsored'
                    if 'SponsorID' in discount_code:
                        if discount_code['SponsorID'] in sponsors_by_id:
                            if sponsors_by_id[discount_code['SponsorID']][
                                    'RegistrationType'] in sponsor_reporting_groups:
                                sponsor_reporting_group = sponsor_reporting_groups[
                                    sponsors_by_id[discount_code['SponsorID']]
                                    ['RegistrationType']]

                    redeemed += 1
                    group_attendee_stats[sponsor_reporting_group][
                        'redeemed'] += 1

                if registrant['discount_code'] in redemptions_by_code:
                    redemptions_by_code[registrant['discount_code']] += 1
                else:
                    redemptions_by_code[registrant['discount_code']] = 1
            else:
                nonsponsored += 1
        else:
            nonsponsored += 1

    for group_name, group_stats in group_attendee_stats.items():
        group_stats[
            'reserved'] = group_stats['quantity'] - group_stats['redeemed']

    for sponsor, codes in codes_by_sponsor.items():
        for code in codes:
            if code['discount_code'] in redemptions_by_code:
                code['redeemed'] = redemptions_by_code[code['discount_code']]
                code['available'] = code['quantity'] - redemptions_by_code[
                    code['discount_code']]

    sponsors.sort(key=lambda x: x['Company'])

    for sponsor in sponsors:
        sponsor['discount_codes'] = codes_by_sponsor.get(sponsor['ID'], [])

    registration_summary = {
        "sponsors":
        sponsors,
        "reserved":
        quantity - redeemed,
        "redeemed":
        redeemed,
        "nonsponsored":
        nonsponsored,
        "registered":
        nonsponsored + redeemed,
        "group_attendee_stats": [{
            "name": k,
            "data": group_attendee_stats[k]
        } for k in sorted(group_attendee_stats.keys())],
        "badge_type_names": [{
            "value": k,
            "name": badge_types[k]['name']
        } for k in sorted(badge_types.keys())]
    }

    # ==========================================================================
    # Return the appropriate stuff for the whole HTML page, or a
    # particular CSV table.
    # ==========================================================================

    if "download_csv" in request.values:
        download_content = request.values['download_content']

        if download_content == 'registration_summary':

            csv_rows = [[
                'Company Name', 'Contact Email', 'Sponsorship',
                'Discount code', 'Discount code Report URL',
                'Discount code Redemption URL', 'Total', 'Redeemed', 'Unusued'
            ]]

            for sponsor in registration_summary['sponsors']:
                for discount_code in sponsor['discount_codes']:
                    csv_rows.append([
                        sponsor.get('Company', ''),
                        sponsor.get('Email', ''),
                        sponsor.get('RegistrationType', ''),
                        discount_code.get('discount_code', ''),
                        "%s/%s?code=%s" %
                        (app.config['EXTERNAL_SERVER_BASE_URL'],
                         url_for('discount_code'),
                         discount_code.get('discount_code', '')), badge_types[
                             discount_code['badge_type']]['regonline_url'],
                        discount_code.get('quantity', 0),
                        discount_code.get('redeemed', 0),
                        discount_code.get('available', 0)
                    ])

            download_filename = "registration-summary-%s.csv" % (
                datetime.datetime.now().strftime("%Y-%m-%d-%H%M%S"))

        else:
            raise Exception("Unknown download_content type: %s requested." %
                            (download_content))

        si = StringIO.StringIO()
        writer = csv.writer(si)
        for row in csv_rows:
            writer.writerow(row)
        output = make_response(si.getvalue())
        output.headers["Content-Disposition"] = "attachment; filename=%s" % (
            download_filename)
        output.headers["Content-type"] = "text/csv"
        return output
    else:
        return render_template("registration_summary.html",
                               registration_summary=registration_summary)
Example #5
0
def bulk_purchases():
    '''Admin only, lists all Enterprise Pack and Bulk Purchases.'''

    data = {
        'eventID': app.config['SPONSOR_EVENT'],
        'api_key': app.config['APP_KEY']
    }

    discount_codes = requests.post(
        "%s/data/discounts/" % (app.config['APP_SERVER']),
        json.dumps(data)).json()['discount_codes']

    sponsors = requests.post("%s/data/sponsors/" % (app.config['APP_SERVER']),
                             json.dumps(data)).json()['sponsors']

    badge_types = get_badge_types(app.config['SPONSOR_EVENT'])

    sponsor_reporting_groups = get_sponsor_reporting_groups(
        app.config['SPONSOR_EVENT'])

    sponsors_by_id = {x['ID']: x for x in sponsors}
    discounts_by_code = {}

    enterprise_packs_by_sponsor = {}
    bulk_purchases_by_sponsor = {}

    total_enterprise_packs = 0
    total_bulk_purchases = 0

    enterprise_group_purchase_stats = {}
    bulk_group_purchase_stats = {}

    for discount_code in discount_codes:
        entitlement = (discount_code['SponsorID'],
                       sponsors_by_id[discount_code['SponsorID']]['Company'],
                       discount_code['code_source'],
                       badge_types[discount_code['badge_type']]['name'],
                       discount_code['RegistrationType'])
        quantity = discount_code['quantity'] / 10
        if discount_code['code_source'] == 'Enterprise Pack':
            total_enterprise_packs += quantity
            if entitlement in enterprise_packs_by_sponsor:
                enterprise_packs_by_sponsor[entitlement] += quantity
            else:
                enterprise_packs_by_sponsor[entitlement] = quantity
        elif discount_code['code_source'].startswith('Bulk Purchase'):
            total_bulk_purchases += quantity
            if entitlement in bulk_purchases_by_sponsor:
                bulk_purchases_by_sponsor[entitlement] += quantity
            else:
                bulk_purchases_by_sponsor[entitlement] = quantity
        else:
            # Don't tally up group based stats if we're not working
            # with certain sponsor types.
            continue

        sponsor_reporting_group = sponsor_reporting_groups.get(
            sponsors_by_id[discount_code['SponsorID']]['RegistrationType'],
            'Other Sponsored')
        if discount_code['code_source'] == 'Enterprise Pack':
            if sponsor_reporting_group in enterprise_group_purchase_stats:
                enterprise_group_purchase_stats[
                    sponsor_reporting_group] += quantity
            else:
                enterprise_group_purchase_stats[
                    sponsor_reporting_group] = quantity
        else:
            if sponsor_reporting_group in bulk_group_purchase_stats:
                bulk_group_purchase_stats[sponsor_reporting_group] += quantity
            else:
                bulk_group_purchase_stats[sponsor_reporting_group] = quantity

    bulk_purchases = {
        "total_enterprise_packs":
        total_enterprise_packs,
        "total_bulk_purchases":
        total_bulk_purchases,
        "enterprise_group_purchase_stats":
        enterprise_group_purchase_stats,
        "bulk_group_purchase_stats":
        bulk_group_purchase_stats,
        "enterprise_packs_by_sponsor": [{
            "SponsorID": key[0],
            "Company": key[1],
            "code_source": key[2],
            "badge_type": key[3],
            "RegistrationType": key[4],
            "quantity": value
        } for key, value in sorted(enterprise_packs_by_sponsor.items(),
                                   key=lambda k: k[0][1])],
        "bulk_purchases_by_sponsor": [{
            "SponsorID": key[0],
            "Company": key[1],
            "code_source": key[2],
            "badge_type": key[3],
            "RegistrationType": key[4],
            "quantity": value
        } for key, value in sorted(bulk_purchases_by_sponsor.items(),
                                   key=lambda k: k[0][1])]
    }

    return render_template("bulk_purchases.html",
                           bulk_purchases=bulk_purchases)
Example #6
0
def discount_code():
    '''Search request for input discount code.'''

    redeemed_codes = None
    if 'code' in request.values:
        data = {
            'discount_eventID': app.config['SPONSOR_EVENT'],
            'registrant_eventID': app.config['REGISTRANT_EVENT'],
            'discount_code': request.values['code'].lower().strip()
        }
        redeemed_codes = requests.post(
            "%s/data/discount_code/" % (app.config['APP_SERVER']),
            json.dumps(data)).json()

        if redeemed_codes['discount_code_data'] == {}:
            flash("No data found for code: %s" %
                  (request.values['code'].strip()))
        else:
            redeemed_codes['redemptions'].sort(
                key=lambda x: x['name'].split()[-1])
            badge_types = get_badge_types(app.config['SPONSOR_EVENT'])
            badge_type_name = redeemed_codes['discount_code_data'][
                'badge_type']
            if redeemed_codes['discount_code_data'][
                    'badge_type'] in badge_types:
                badge_type_name = badge_types[
                    redeemed_codes['discount_code_data']['badge_type']]['name']

            redeemed_codes['badge_type_name'] = badge_type_name

    # ==========================================================================
    # Return the appropriate stuff for the whole HTML page, or a
    # particular CSV table.
    # ==========================================================================
    if "download_csv" in request.values:
        download_content = request.values['download_content']

        if download_content == 'discount_code_search':

            csv_rows = [[
                'Name', 'Company', 'Title', 'Registration Type', 'Status',
                'Registration Date'
            ]]

            for attendee in redeemed_codes['redemptions']:
                csv_rows.append([
                    attendee.get('name', ''),
                    attendee.get('company', ''),
                    attendee.get('title', ''),
                    attendee.get('registration_type', ''),
                    attendee.get('status', ''),
                    attendee.get('registration_date', '')
                ])

            download_filename = "sponsor-attendees-%s.csv" % (
                datetime.datetime.now().strftime("%Y-%m-%d-%H%M%S"))

            si = StringIO.StringIO()
            writer = csv.writer(si)
            for row in csv_rows:
                writer.writerow(row)
            output = make_response(si.getvalue())
            output.headers[
                "Content-Disposition"] = "attachment; filename=%s" % (
                    download_filename)
            output.headers["Content-type"] = "text/csv"
            return output

        else:
            raise Exception("Unknown download_content type: %s requested." %
                            (download_content))

    else:
        if 'code' in request.values:
            flash('Showing data for discount code: %s' %
                  (request.values['code'].strip()))

        return render_template("discount_code.html",
                               redeemed_codes=redeemed_codes)
def sponsor_summary():
    '''Will eventually be limited to particular sponsors, right now admin only.'''

    sponsor_email = None
    if 'sponsor_email' in request.values:
        if validate_email( request.values['sponsor_email'].strip().lower() ):
            sponsor_email = request.values['sponsor_email'].strip().lower()

    # DEBUG - hard code this for now.
    #sponsor_email = "*****@*****.**"

    data = {
        'eventID' : app.config['SPONSOR_EVENT'],
        'api_key' : app.config['APP_KEY']
    }
    sponsors = [ x for x in requests.post( "%s/data/sponsors/" % ( app.config['APP_SERVER'] ), json.dumps( data ) ).json()['sponsors'] if ( x['Email'].strip().lower() == sponsor_email or x['CCEmail'].strip().lower() == sponsor_email ) ]
    sponsors_by_id = { x['ID']:x for x in sponsors }

    discount_codes = [ x for x in requests.post( "%s/data/discounts/" % ( app.config['APP_SERVER'] ), json.dumps( data ) ).json()['discount_codes'] if x['SponsorID'] in sponsors_by_id ]
    discounts_by_code = { x['discount_code']:x for x in discount_codes }

    include_company_data = False

    if include_company_data:
        # Try to build up a list of alternate company names that
        # registrants might have entered in the company box that all mean
        # the same thing.
        company_suffixes = [ 'co', 'co.', 'corp', 'corp.', 'corporation', 'inc', 'inc.', 'incorporated', 'llc', 'llc.' ]
        companies = {}
        for sponsor in sponsors:
            if len( sponsor['Company'].strip() ):
                company = sponsor['Company'].lower().strip()
                company = ' '.join( company.split() )
                companies[company] = True
        
                # Remove any suffixes and trailing commas.
                words = company.split()
                if len( words ) > 1:
                    if words[-1] in company_suffixes:
                        if words[-2][-1] == ',':
                            words[-2] = words[-2][:-1]
                        words = [ w for w in words if len( w ) ]
                    companies[' '.join( words )] = True

    data = {
        'eventID' : app.config['REGISTRANT_EVENT'],
        'api_key' : app.config['APP_KEY']
    }
    all_registrants = requests.post( "%s/data/registrants/" % ( app.config['APP_SERVER'] ), json.dumps( data ) ).json()['registrants']

    # Boil down our registrants to those who used one of our codes, or
    # those who come from our company.
    registrants = [ x for x in all_registrants if x['discount_code'] in discounts_by_code ]

    if include_company_data:
        other_registrants = [ x for x in all_registrants if x['discount_code'] not in discounts_by_code ]
        registrants += [ x for x in other_registrants if x['Company'] in companies ]

    badge_types = get_badge_types( app.config['SPONSOR_EVENT'] )

    # ==========================================================================
    # Handle the case where we are sending an reminder discount code email.

    if "send_email" in request.values:
        sponsorID = int( request.values['sponsorID'] )

        email_recipients = []
        try:
            email_recipients_raw = request.values['email_recipients'].replace( ';', ',' )
            email_recipients = [ x.strip() for x in email_recipients_raw.split( ',' ) ]
        except Exception as e:
            flash( "Invalid email address argument! Please enter a list of email addresses separated by commas." )

        sponsor = [ x for x in sponsors if x['ID'] == sponsorID ][0]

        sponsor_discount_codes = [ x for x in discount_codes if x['SponsorID'] == sponsor['ID'] ]

        mail_sent = False
        try:
            # DEBUG, leave this variable alone here and actually send to the recipients in production.
            email_recipients = app.config['ADMIN_MAIL_RECIPIENTS']

            mail_message = Message( "Grace Hopper Celebration 2015 Discount Codes",
                                    sender = app.config['SEND_AS'],
                                    recipients = email_recipients,
                                    bcc = app.config['ADMIN_MAIL_RECIPIENTS'])
            
            for discount_code in sorted( sponsor_discount_codes, 
                                         key = lambda x: x['discount_code'] ):

                discount_code['badge_type_name'] = badge_types[discount_code['badge_type']]['name']
                discount_code['regonline_url'] = badge_types[discount_code['badge_type']]['regonline_url']
                
                discount_code['discount_search_url'] = "%s/%s?code=%s" % ( app.config['EXTERNAL_SERVER_BASE_URL'], url_for( 'discount_code' ), discount_code['discount_code'] )

            mail_message.html = render_template( "email_discount_code_summary.html", data={
                'sponsor'        : sponsor,
                'discount_codes' : sponsor_discount_codes } )

            mail.init_app( app )
            mail.send( mail_message )
            mail_sent = True
        except Exception as e:
            flash( "ERROR! Failed to send discount code summary to: %s. %s" % ( email_recipients + app.config['ADMIN_MAIL_RECIPIENTS'], e ) )

        if mail_sent:
            # DEBUG - remove the admin mail recipients from the flash notification.
            success_message = "Discount summary email sent to: %s" % ( email_recipients + app.config['ADMIN_MAIL_RECIPIENTS'] )
            flash( success_message )

    # ==========================================================================
    # Now display all the summary data.

    # Attendees who did not redeem a code.
    nonsponsored = 0
    # Attendees who did use a code.
    redeemed = 0
    # Code entitlements not accounted for.
    quantity = 0

    sponsors_by_id = { x['ID']:x for x in sponsors }

    # Used to aggregate some reports about Academic, Corporate, etc. sponsors and their registrants.
    sponsor_reporting_groups = get_sponsor_reporting_groups( app.config['SPONSOR_EVENT'] )    
    group_attendee_stats = { 
        'Other Sponsored' : { 'redeemed' : 0,
                              'quantity' : 0 },
    }

    # Used to compute redeemed / available for each particular sponsor
    # code.
    codes_by_sponsor = {}

    for discount_code in discount_codes:
        if discount_code['SponsorID'] in codes_by_sponsor:
            codes_by_sponsor[discount_code['SponsorID']].append( discount_code )
        else:
            codes_by_sponsor[discount_code['SponsorID']] = [ discount_code ]

        if discount_code['badge_type'] in badge_types:
            if badge_types[discount_code['badge_type']]['reserve_spot']:
                quantity += int( discount_code['quantity'] )

                sponsor_reporting_group = sponsor_reporting_groups.get( sponsors_by_id[discount_code['SponsorID']]['RegistrationType'], 'Other Sponsored' )
                if sponsor_reporting_group in group_attendee_stats:
                    group_attendee_stats[sponsor_reporting_group]['quantity'] += discount_code['quantity']
                else:
                    group_attendee_stats[sponsor_reporting_group] = { 'quantity' : discount_code['quantity'],
                                                                      'redeemed' : 0 }
        else:
            logging.warning( json.dumps( { 'message' : 'Unknown badge_type: %s found in discount codes.' % ( discount_code['badge_type'] ) } ) )

    redemptions_by_code = {}

    for registrant in registrants:
        if registrant['discount_code']:
            discount_code = discounts_by_code.get( registrant['discount_code'], {} )

            if discount_code.get( 'badge_type', None ) in badge_types:
                if badge_types[discount_code['badge_type']]['reserve_spot']:

                    sponsor_reporting_group = 'Other Sponsored'
                    if 'SponsorID' in discount_code:
                        if discount_code['SponsorID'] in sponsors_by_id:
                            if sponsors_by_id[discount_code['SponsorID']]['RegistrationType'] in sponsor_reporting_groups:
                                sponsor_reporting_group = sponsor_reporting_groups[sponsors_by_id[discount_code['SponsorID']]['RegistrationType']]
          
                    redeemed += 1
                    group_attendee_stats[sponsor_reporting_group]['redeemed'] += 1

                if registrant['discount_code'] in redemptions_by_code:
                    redemptions_by_code[registrant['discount_code']] += 1
                else:
                    redemptions_by_code[registrant['discount_code']] = 1
            else:
                nonsponsored += 1
        else:
            nonsponsored += 1

    for group_name, group_stats in group_attendee_stats.items():
        group_stats['reserved'] = group_stats['quantity'] - group_stats['redeemed']
            
    for sponsor, codes in codes_by_sponsor.items():
        for code in codes:
            if code['discount_code'] in redemptions_by_code:
                code['redeemed'] = redemptions_by_code[code['discount_code']]
                code['available'] = code['quantity'] - redemptions_by_code[code['discount_code']]

    sponsors.sort( key=lambda x: x['Company'] )

    for sponsor in sponsors:
        sponsor['discount_codes'] = codes_by_sponsor.get( sponsor['ID'], [] )

    # Private function that strips down a registrant data to what we
    # can give out publicly to someone with the code.
    def get_fields( registrant ):
        return {
            "name" : "%s %s" % ( registrant['FirstName'], registrant['LastName'] ),
            "company" : registrant['Company'],
            "title" : registrant['Title'],
            "status" : registrant['StatusDescription'],
            "registration_type" : registrant['RegistrationType'],
            "registration_date" : registrant['AddDate']
        }
    
    public_registrants = [ get_fields( x ) for x in registrants ]

    # ==========================================================================
    # Return the appropriate stuff for the whole HTML page, or a
    # particular CSV table.
    # ==========================================================================

    sponsor_summary = {
        "sponsor_email"        : sponsor_email,
        "sponsors"             : sponsors,
        "reserved"             : quantity - redeemed,
        "redeemed"             : redeemed,
        "nonsponsored"         : nonsponsored,
        "registered"           : nonsponsored + redeemed,
        "group_attendee_stats" : [ { "name" : k, "data" : group_attendee_stats[k] } for k in sorted( group_attendee_stats.keys() ) ],
        "badge_type_names"     : [ { "value" : k, "name" : badge_types[k]['name'] } for k in sorted( badge_types.keys() ) ],
        "registrants"          : public_registrants
    }

    if "download_csv" in request.values:
        download_content = request.values['download_content']

        if download_content == 'sponsor_summary_sponsors':

            csv_rows = [ [ 'Company Name', 'Contact Email', 'Sponsorship', 'Discount code', 'Discount code Report URL', 'Discount code Redemption URL', 'Total', 'Redeemed', 'Unusued' ] ]

            for sponsor in sponsor_summary['sponsors']:
                for discount_code in sponsor['discount_codes']:
                    csv_rows.append( [
                        sponsor.get( 'Company', '' ),
                        sponsor.get( 'Email', '' ),
                        sponsor.get( 'RegistrationType', '' ),
                        discount_code.get( 'discount_code', '' ),
                        "%s/%s?code=%s" % ( app.config['EXTERNAL_SERVER_BASE_URL'], url_for( 'discount_code' ), discount_code.get( 'discount_code', '' ) ),
                        badge_types[discount_code['badge_type']]['regonline_url'],
                        discount_code.get( 'quantity', 0 ),
                        discount_code.get( 'redeemed', 0 ),
                        discount_code.get( 'available', 0 ) ] )
            
            download_filename = "sponsor-summary-%s.csv" % ( datetime.datetime.now().strftime( "%Y-%m-%d-%H%M%S"  ) )

        elif download_content == 'sponsor_summary_registrants':
            csv_rows = [ [ 'Name', 'Company', 'Title', 'Registration Type', 'Status', 'Registration Date' ] ]

            for attendee in sponsor_summary['registrants']:
                csv_rows.append( [
                    attendee.get( 'name', '' ),
                    attendee.get( 'company', '' ),
                    attendee.get( 'title', '' ),
                    attendee.get( 'registration_type', '' ),
                    attendee.get( 'status', '' ),
                    attendee.get( 'registration_date', '' ) ] )

            download_filename = "sponsor-attendees-%s.csv" % ( datetime.datetime.now().strftime( "%Y-%m-%d-%H%M%S"  ) )

        else:
            raise Exception( "Unknown download_content type: %s requested." % ( download_content ) )
                             
        si = StringIO.StringIO()
        writer = csv.writer( si )
        for row in csv_rows:
            writer.writerow( row )
        output = make_response( si.getvalue() )
        output.headers["Content-Disposition"] = "attachment; filename=%s" % ( download_filename )
        output.headers["Content-type"] = "text/csv"
        return output

    else:
        if 'sponsor_email' in request.values:
            if len( sponsors ) == 0:
                flash( "No data found for sponsor email: %s" % ( request.values['sponsor_email'].strip() ) )
            else:
                flash( 'Showing data for sponsor email: %s' % ( request.values['sponsor_email'].strip() ) )

        return render_template( "sponsor_summary.html", sponsor_summary=sponsor_summary )
def registration_summary():
    '''Admin only, summaries all registration counts and sponsor data.'''

    data = {
        'eventID' : app.config['SPONSOR_EVENT'],
        'api_key' : app.config['APP_KEY']
    }
    
    discount_codes = requests.post( "%s/data/discounts/" % ( app.config['APP_SERVER'] ), json.dumps( data ) ).json()['discount_codes']

    sponsors = requests.post( "%s/data/sponsors/" % ( app.config['APP_SERVER'] ), json.dumps( data ) ).json()['sponsors']

    data = {
        'eventID' : app.config['REGISTRANT_EVENT'],
        'api_key' : app.config['APP_KEY']
    }
    registrants = requests.post( "%s/data/registrants/" % ( app.config['APP_SERVER'] ), json.dumps( data ) ).json()['registrants']

    badge_types = get_badge_types( app.config['SPONSOR_EVENT'] )

    # ==========================================================================
    # Handle the case where we are creating a new discount code for this sponsor.
    # ==========================================================================

    if "add_discount_code" in request.values:
        badge_type = request.values['badge_type']
        quantity = int( request.values['quantity'].strip() )
        sponsorID = int( request.values['sponsorID'] )

        sponsor = [ x for x in sponsors if x['ID'] == sponsorID ][0]
    
        discount_code = generate_discount_code( app.config['SPONSOR_EVENT'], sponsor, badge_type, quantity, discount_codes )

        def get_date_string( date ):
            return date.strftime( "%a, %d %b %Y %H:%M:%S %Z" )

        discount_code['created_date'] = get_date_string( discount_code['created_date'] )

        result = requests.post( "%s/data/discount_code/add/" % ( app.config['APP_SERVER'] ), json.dumps( { "eventID" : app.config['SPONSOR_EVENT'], "discount_code_data" : discount_code, "api_key" : app.config['APP_KEY']  } ) )

        mail_sent = False
        try:
            # DEBUG, add in mail recipients.
            mail_recipients = app.config['ADMIN_MAIL_RECIPIENTS']

            # We want this mail to go out at 4 pm on the next business
            # day.
            today = datetime.date.today()
            today_4pm = datetime.datetime( today.year, today.month, today.day, 16, tzinfo=pytz.timezone( 'PST8PDT' ) )
            tomorrow_4pm = today_4pm + relativedelta( bdays = +1 )
            when = tomorrow_4pm.astimezone( pytz.utc )
            
            extra_headers = {
                'X-MC-SendAt' : when.strftime( "%Y-%m-%d %H:%M:%S" )
            }

            mail_message = Message( "Grace Hopper Celebration 2015 Discount Codes",
                                    sender = app.config['SEND_AS'],
                                    recipients = mail_recipients,
                                    bcc = app.config['ADMIN_MAIL_RECIPIENTS'],
                                    extra_headers = extra_headers )

            discount_search_url = "%s/%s?code=%s" % ( app.config['EXTERNAL_SERVER_BASE_URL'], url_for( 'discount_code' ), discount_code['discount_code'] )

            mail_message.html = render_template( "email_add_discount_code.html", data={
                'sponsor'             : sponsor,
                'quantity'            : quantity,
                'discount_code'       : discount_code,
                'badge_type_name'     : badge_types[badge_type]['name'],
                'regonline_url'       : badge_types[badge_type]['regonline_url'],
                'discount_search_url' : discount_search_url } )

            mail.init_app( app )
            mail.send( mail_message )
            mail_sent = True
        except Exception as e:
            flash( "ERROR! Failed to send email notification of discount code creation to: %s." % ( mail_recipients ) )

        if result.json()['success']:
            discount_codes.append( discount_code )

            success_message = 'Added %d %s badges to sponsor %s with discount code: %s.' % ( quantity, badge_types[badge_type]['name'], sponsor['Company'], discount_code['discount_code'] )

            if mail_sent:
                success_message += " Notification email sent to: %s" % ( mail_recipients )

            flash( success_message )
        else:
            raise Exception( "Failed to add code!" )

    # ==========================================================================


    # ==========================================================================
    # Handle the case where we are sending an reminder discount code email.

    if "send_email" in request.values:
        sponsorID = int( request.values['sponsorID'] )

        email_recipients = []
        try:
            email_recipients_raw = request.values['email_recipients'].replace( ';', ',' )
            email_recipients = [ x.strip() for x in email_recipients_raw.split( ',' ) ]
        except Exception as e:
            flash( "Invalid email address argument! Please enter a list of email addresses separated by commas." )

        sponsor = [ x for x in sponsors if x['ID'] == sponsorID ][0]

        sponsor_discount_codes = [ x for x in discount_codes if x['SponsorID'] == sponsor['ID'] ]

        mail_sent = False
        try:
            # DEBUG, leave this variable alone here and actually send to the recipients in production.
            email_recipients = app.config['ADMIN_MAIL_RECIPIENTS']

            mail_message = Message( "Grace Hopper Celebration 2015 Discount Codes",
                                    sender = app.config['SEND_AS'],
                                    recipients = email_recipients,
                                    bcc = app.config['ADMIN_MAIL_RECIPIENTS'])

            for discount_code in sorted( sponsor_discount_codes, 
                                         key = lambda x: x['discount_code'] ):
                discount_code['badge_type_name'] = badge_types[discount_code['badge_type']]['name']
                discount_code['regonline_url'] = badge_types[discount_code['badge_type']]['regonline_url']
                
                discount_code['discount_search_url'] = "%s/%s?code=%s" % ( app.config['EXTERNAL_SERVER_BASE_URL'], url_for( 'discount_code' ), discount_code['discount_code'] )
        
            mail_message.html = render_template( "email_discount_code_summary.html", data={
                'sponsor'        : sponsor,
                'discount_codes' : sponsor_discount_codes } )

            mail.init_app( app )
            mail.send( mail_message )
            mail_sent = True
        except Exception as e:
            flash( "ERROR! Failed to send discount code summary to: %s." % ( email_recipients + app.config['ADMIN_MAIL_RECIPIENTS'] ) )

        if mail_sent:
            success_message = "Discount summary email sent to: %s" % ( email_recipients + app.config['ADMIN_MAIL_RECIPIENTS'] )
            flash( success_message )

    # ==========================================================================
    # Now display all the summary data.

    # Attendees who did not redeem a code.
    nonsponsored = 0
    # Attendees who did use a code.
    redeemed = 0
    # Code entitlements not accounted for.
    quantity = 0

    sponsors_by_id = { x['ID']:x for x in sponsors }
    discounts_by_code = {}

    # Used to aggregate some reports about Academic, Corporate, etc. sponsors and their registrants.
    sponsor_reporting_groups = get_sponsor_reporting_groups( app.config['SPONSOR_EVENT'] )    
    group_attendee_stats = { 
        'Other Sponsored' : { 'redeemed' : 0,
                              'quantity' : 0 },
    }

    # Used to compute redeemed / available for each particular sponsor
    # code.
    codes_by_sponsor = {}

    for discount_code in discount_codes:
        discounts_by_code[discount_code['discount_code']] = discount_code

        if discount_code['SponsorID'] in codes_by_sponsor:
            codes_by_sponsor[discount_code['SponsorID']].append( discount_code )
        else:
            codes_by_sponsor[discount_code['SponsorID']] = [ discount_code ]

        if discount_code['badge_type'] in badge_types:
            if badge_types[discount_code['badge_type']]['reserve_spot']:
                quantity += int( discount_code['quantity'] )

                sponsor_reporting_group = sponsor_reporting_groups.get( sponsors_by_id[discount_code['SponsorID']]['RegistrationType'], 'Other Sponsored' )
                if sponsor_reporting_group in group_attendee_stats:
                    group_attendee_stats[sponsor_reporting_group]['quantity'] += discount_code['quantity']
                else:
                    group_attendee_stats[sponsor_reporting_group] = { 'quantity' : discount_code['quantity'],
                                                                      'redeemed' : 0 }
        else:
            logging.warning( json.dumps( { 'message' : 'Unknown badge_type: %s found in discount codes.' % ( discount_code['badge_type'] ) } ) )

    redemptions_by_code = {}

    for registrant in registrants:
        if registrant['discount_code']:
            discount_code = discounts_by_code.get( registrant['discount_code'], {} )

            if discount_code.get( 'badge_type', None ) in badge_types:
                if badge_types[discount_code['badge_type']]['reserve_spot']:

                    sponsor_reporting_group = 'Other Sponsored'
                    if 'SponsorID' in discount_code:
                        if discount_code['SponsorID'] in sponsors_by_id:
                            if sponsors_by_id[discount_code['SponsorID']]['RegistrationType'] in sponsor_reporting_groups:
                                sponsor_reporting_group = sponsor_reporting_groups[sponsors_by_id[discount_code['SponsorID']]['RegistrationType']]
          
                    redeemed += 1
                    group_attendee_stats[sponsor_reporting_group]['redeemed'] += 1

                if registrant['discount_code'] in redemptions_by_code:
                    redemptions_by_code[registrant['discount_code']] += 1
                else:
                    redemptions_by_code[registrant['discount_code']] = 1
            else:
                nonsponsored += 1
        else:
            nonsponsored += 1

    for group_name, group_stats in group_attendee_stats.items():
        group_stats['reserved'] = group_stats['quantity'] - group_stats['redeemed']
            
    for sponsor, codes in codes_by_sponsor.items():
        for code in codes:
            if code['discount_code'] in redemptions_by_code:
                code['redeemed'] = redemptions_by_code[code['discount_code']]
                code['available'] = code['quantity'] - redemptions_by_code[code['discount_code']]

    sponsors.sort( key=lambda x: x['Company'] )

    for sponsor in sponsors:
        sponsor['discount_codes'] = codes_by_sponsor.get( sponsor['ID'], [] )

    registration_summary = {
        "sponsors" : sponsors,
        "reserved" : quantity - redeemed,
        "redeemed" : redeemed,
        "nonsponsored" : nonsponsored,
        "registered" : nonsponsored + redeemed,
        "group_attendee_stats" : [ { "name" : k, "data" : group_attendee_stats[k] } for k in sorted( group_attendee_stats.keys() ) ],
        "badge_type_names" : [ { "value" : k, "name" : badge_types[k]['name'] } for k in sorted( badge_types.keys() ) ]
    }

    # ==========================================================================
    # Return the appropriate stuff for the whole HTML page, or a
    # particular CSV table.
    # ==========================================================================

    if "download_csv" in request.values:
        download_content = request.values['download_content']

        if download_content == 'registration_summary':

            csv_rows = [ [ 'Company Name', 'Contact Email', 'Sponsorship', 'Discount code', 'Discount code Report URL', 'Discount code Redemption URL', 'Total', 'Redeemed', 'Unusued' ] ]

            for sponsor in registration_summary['sponsors']:
                for discount_code in sponsor['discount_codes']:
                    csv_rows.append( [
                        sponsor.get( 'Company', '' ),
                        sponsor.get( 'Email', '' ),
                        sponsor.get( 'RegistrationType', '' ),
                        discount_code.get( 'discount_code', '' ),
                        "%s/%s?code=%s" % ( app.config['EXTERNAL_SERVER_BASE_URL'], url_for( 'discount_code' ), discount_code.get( 'discount_code', '' ) ),
                        badge_types[discount_code['badge_type']]['regonline_url'],
                        discount_code.get( 'quantity', 0 ),
                        discount_code.get( 'redeemed', 0 ),
                        discount_code.get( 'available', 0 ) ] )
            
            download_filename = "registration-summary-%s.csv" % ( datetime.datetime.now().strftime( "%Y-%m-%d-%H%M%S"  ) )

        else:
            raise Exception( "Unknown download_content type: %s requested." % ( download_content ) )
                             
        si = StringIO.StringIO()
        writer = csv.writer( si )
        for row in csv_rows:
            writer.writerow( row )
        output = make_response( si.getvalue() )
        output.headers["Content-Disposition"] = "attachment; filename=%s" % ( download_filename )
        output.headers["Content-type"] = "text/csv"
        return output
    else:
        return render_template( "registration_summary.html", registration_summary=registration_summary )
def bulk_purchases():
    '''Admin only, lists all Enterprise Pack and Bulk Purchases.'''

    data = {
        'eventID' : app.config['SPONSOR_EVENT'],
        'api_key' : app.config['APP_KEY']
    }
    
    discount_codes = requests.post( "%s/data/discounts/" % ( app.config['APP_SERVER'] ), json.dumps( data ) ).json()['discount_codes']

    sponsors = requests.post( "%s/data/sponsors/" % ( app.config['APP_SERVER'] ), json.dumps( data ) ).json()['sponsors']

    badge_types = get_badge_types( app.config['SPONSOR_EVENT'] )

    sponsor_reporting_groups = get_sponsor_reporting_groups( app.config['SPONSOR_EVENT'] )    

    sponsors_by_id = { x['ID']:x for x in sponsors }
    discounts_by_code = {}

    enterprise_packs_by_sponsor = {}
    bulk_purchases_by_sponsor = {}

    total_enterprise_packs = 0
    total_bulk_purchases = 0

    enterprise_group_purchase_stats = {}
    bulk_group_purchase_stats = {}

    for discount_code in discount_codes:
        entitlement = ( discount_code['SponsorID'], 
                        sponsors_by_id[discount_code['SponsorID']]['Company'], 
                        discount_code['code_source'], 
                        badge_types[discount_code['badge_type']]['name'], 
                        discount_code['RegistrationType'] )
        quantity = discount_code['quantity'] / 10
        if discount_code['code_source'] == 'Enterprise Pack':
            total_enterprise_packs += quantity
            if entitlement in enterprise_packs_by_sponsor:
                enterprise_packs_by_sponsor[entitlement] += quantity
            else:
                enterprise_packs_by_sponsor[entitlement] = quantity
        elif discount_code['code_source'].startswith( 'Bulk Purchase' ):
            total_bulk_purchases += quantity
            if entitlement in bulk_purchases_by_sponsor:
                bulk_purchases_by_sponsor[entitlement] += quantity
            else:
                bulk_purchases_by_sponsor[entitlement] = quantity
        else:
            # Don't tally up group based stats if we're not working
            # with certain sponsor types.
            continue

        sponsor_reporting_group = sponsor_reporting_groups.get( sponsors_by_id[discount_code['SponsorID']]['RegistrationType'], 'Other Sponsored' )
        if discount_code['code_source'] == 'Enterprise Pack':
            if sponsor_reporting_group in enterprise_group_purchase_stats:
                enterprise_group_purchase_stats[sponsor_reporting_group] += quantity
            else:
                enterprise_group_purchase_stats[sponsor_reporting_group] = quantity
        else:
            if sponsor_reporting_group in bulk_group_purchase_stats:
                bulk_group_purchase_stats[sponsor_reporting_group] += quantity
            else:
                bulk_group_purchase_stats[sponsor_reporting_group] = quantity

    bulk_purchases = {
        "total_enterprise_packs" : total_enterprise_packs,
        "total_bulk_purchases"   : total_bulk_purchases,
        "enterprise_group_purchase_stats" : enterprise_group_purchase_stats,
        "bulk_group_purchase_stats" : bulk_group_purchase_stats,
        "enterprise_packs_by_sponsor" : [ { "SponsorID"         : key[0],
                                            "Company"           : key[1],
                                            "code_source"       : key[2],
                                            "badge_type"        : key[3],
                                            "RegistrationType"  : key[4],
                                            "quantity"          : value }
                                          for key, value in sorted( enterprise_packs_by_sponsor.items(), key=lambda k: k[0][1] ) ],
        "bulk_purchases_by_sponsor" : [ { "SponsorID"         : key[0],
                                          "Company"           : key[1],
                                          "code_source"       : key[2],
                                          "badge_type"        : key[3],
                                          "RegistrationType"  : key[4],
                                          "quantity"          : value }
                                        for key, value in sorted( bulk_purchases_by_sponsor.items(), key=lambda k: k[0][1] ) ]
    }

    return render_template( "bulk_purchases.html", bulk_purchases=bulk_purchases )
def discount_code():
    '''Search request for input discount code.'''

    redeemed_codes = None
    if 'code' in request.values:
        data = {
            'discount_eventID' : app.config['SPONSOR_EVENT'],
            'registrant_eventID' : app.config['REGISTRANT_EVENT'],
            'discount_code' : request.values['code'].lower().strip()
        }
        redeemed_codes = requests.post( "%s/data/discount_code/" % ( app.config['APP_SERVER'] ), json.dumps( data ) ).json()

        if redeemed_codes['discount_code_data'] == {}:
            flash( "No data found for code: %s" % ( request.values['code'].strip() ) )
        else:
            redeemed_codes['redemptions'].sort( key=lambda x: x['name'].split()[-1] )
            badge_types = get_badge_types( app.config['SPONSOR_EVENT'] )
            badge_type_name = redeemed_codes['discount_code_data']['badge_type']
            if redeemed_codes['discount_code_data']['badge_type'] in badge_types:
                badge_type_name = badge_types[redeemed_codes['discount_code_data']['badge_type']]['name']

            redeemed_codes['badge_type_name'] = badge_type_name

    # ==========================================================================
    # Return the appropriate stuff for the whole HTML page, or a
    # particular CSV table.
    # ==========================================================================
    if "download_csv" in request.values:
        download_content = request.values['download_content']

        if download_content == 'discount_code_search':

            csv_rows = [ [ 'Name', 'Company', 'Title', 'Registration Type', 'Status', 'Registration Date' ] ]

            for attendee in redeemed_codes['redemptions']:
                csv_rows.append( [
                    attendee.get( 'name', '' ),
                    attendee.get( 'company', '' ),
                    attendee.get( 'title', '' ),
                    attendee.get( 'registration_type', '' ),
                    attendee.get( 'status', '' ),
                    attendee.get( 'registration_date', '' ) ] )

            download_filename = "sponsor-attendees-%s.csv" % ( datetime.datetime.now().strftime( "%Y-%m-%d-%H%M%S"  ) )

            si = StringIO.StringIO()
            writer = csv.writer( si )
            for row in csv_rows:
                writer.writerow( row )
            output = make_response( si.getvalue() )
            output.headers["Content-Disposition"] = "attachment; filename=%s" % ( download_filename )
            output.headers["Content-type"] = "text/csv"
            return output

        else:
            raise Exception( "Unknown download_content type: %s requested." % ( download_content ) )

    else:
        if 'code' in request.values:
            flash( 'Showing data for discount code: %s' % ( request.values['code'].strip() ) )

        return render_template( "discount_code.html", redeemed_codes=redeemed_codes )