def make_root(): root = etree.Element('schedule') _add_sub_with_text(root, 'version', '1.0-public') conference = etree.SubElement(root, 'conference') _add_sub_with_text(conference, 'title', 'Electromagnetic Field {}'.format(event_start().year)) _add_sub_with_text(conference, 'acronym', 'emf{}'.format(event_start().year)) _add_sub_with_text(conference, 'start', event_start().strftime('%Y-%m-%d')) _add_sub_with_text(conference, 'end', event_end().strftime('%Y-%m-%d')) _add_sub_with_text(conference, 'days', '3') _add_sub_with_text(conference, 'timeslot_duration', '00:10') return root
def add_event(room, event): url = external_url('schedule.line_up_proposal', proposal_id=event['id'], slug=event['slug']) event_node = etree.SubElement(room, 'event', id=str(event['id']), guid=str(uuid5(NAMESPACE_URL, url))) _add_sub_with_text(event_node, 'room', room.attrib['name']) _add_sub_with_text(event_node, 'title', event['title']) _add_sub_with_text(event_node, 'type', event.get('type', 'talk')) _add_sub_with_text(event_node, 'date', event['start_date'].isoformat()) # Start time _add_sub_with_text(event_node, 'start', event['start_date'].strftime('%H:%M')) duration = get_duration(event['start_date'], event['end_date']) _add_sub_with_text(event_node, 'duration', duration) _add_sub_with_text(event_node, 'abstract', event['description']) _add_sub_with_text(event_node, 'description', event['description']) _add_sub_with_text( event_node, 'slug', 'emf%s-%s-%s' % (event_start().year, event['id'], event['slug'])) _add_sub_with_text(event_node, 'subtitle', '') _add_sub_with_text(event_node, 'track', '') add_persons(event_node, event) add_recording(event_node, event)
def get_auth_tag(): tag = "emf-{}-".format(event_start().year) msg = b"bar-training-" + tag.encode("utf-8") tag += hmac.new(app.config["SECRET_KEY"].encode("utf-8"), msg, digestmod=sha256).hexdigest() return tag
def favourites_ical(): code = request.args.get('token', None) user = None if code: user = User.get_by_api_token(app.config.get('SECRET_KEY'), str(code)) if not current_user.is_anonymous: user = current_user if not user: abort(404) schedule = _get_scheduled_proposals(request.args, override_user=user) title = 'EMF {} Favourites for {}'.format(event_start().year, user.name) cal = Calendar() cal.add('summary', title) cal.add('X-WR-CALNAME', title) cal.add('X-WR-CALDESC', title) cal.add('version', '2.0') for event in schedule: if not event['is_fave']: continue cal_event = Event() cal_event.add('uid', event['id']) cal_event.add('summary', event['title']) cal_event.add('description', event['description']) cal_event.add('location', event['venue']) cal_event.add('dtstart', event['start_date']) cal_event.add('dtend', event['end_date']) cal.add_component(cal_event) return Response(cal.to_ical(), mimetype='text/calendar')
def add_event(room, event): url = external_url('schedule.line_up_proposal', proposal_id=event['id'], slug=event['slug']) event_node = etree.SubElement(room, 'event', id=str(event['id']), guid=str(uuid5(NAMESPACE_URL, url))) _add_sub_with_text(event_node, 'room', room.attrib['name']) _add_sub_with_text(event_node, 'title', event['title']) _add_sub_with_text(event_node, 'type', event.get('type', 'talk')) _add_sub_with_text(event_node, 'date', event['start_date'].isoformat()) # Start time _add_sub_with_text(event_node, 'start', event['start_date'].strftime('%H:%M')) duration = get_duration(event['start_date'], event['end_date']) _add_sub_with_text(event_node, 'duration', duration) _add_sub_with_text(event_node, 'abstract', event['description']) _add_sub_with_text(event_node, 'description', '') _add_sub_with_text(event_node, 'slug', 'emf%s-%s-%s' % (event_start().year, event['id'], event['slug'])) _add_sub_with_text(event_node, 'subtitle', '') _add_sub_with_text(event_node, 'track', '') add_persons(event_node, event) add_recording(event_node, event)
def favourites_ical(): code = request.args.get('token', None) user = None if code: user = User.get_by_checkin_code(app.config.get('FEED_SECRET_KEY'), str(code)) if not current_user.is_anonymous: user = current_user if not user: abort(404) schedule = _get_scheduled_proposals(request.args, override_user=user) title = 'EMF {} Favourites for {}'.format(event_start().year, user.name) cal = Calendar() cal.add('summary', title) cal.add('X-WR-CALNAME', title) cal.add('X-WR-CALDESC', title) cal.add('version', '2.0') for event in schedule: if not event['is_fave']: continue cal_event = Event() cal_event.add('uid', event['id']) cal_event.add('summary', event['title']) cal_event.add('description', event['description']) cal_event.add('location', event['venue']) cal_event.add('dtstart', event['start_date']) cal_event.add('dtend', event['end_date']) cal.add_component(cal_event) return Response(cal.to_ical(), mimetype='text/calendar')
def favourites_ical(): code = request.args.get("token", None) user = None if code: user = User.get_by_api_token(app.config.get("SECRET_KEY"), str(code)) if not current_user.is_anonymous: user = current_user if not user: abort(404) schedule = _get_scheduled_proposals(request.args, override_user=user) title = "EMF {} Favourites for {}".format(event_start().year, user.name) cal = Calendar() cal.add("summary", title) cal.add("X-WR-CALNAME", title) cal.add("X-WR-CALDESC", title) cal.add("version", "2.0") for event in schedule: if not event["is_fave"]: continue cal_event = Event() cal_event.add("uid", event["id"]) cal_event.add("summary", event["title"]) cal_event.add("description", _format_event_description(event)) cal_event.add("location", event["venue"]) cal_event.add("dtstart", event["start_date"]) cal_event.add("dtend", event["end_date"]) cal.add_component(cal_event) return Response(cal.to_ical(), mimetype="text/calendar")
def event_date_processor(): def suffix(d): return ("th" if 11 <= d <= 13 else { 1: "st", 2: "nd", 3: "rd" }.get(d % 10, "th")) s = event_start() e = event_end() assert s.year == e.year if s.month == e.month: fancy_dates = f"""{s.strftime('%B')}<span style="white-space: nowrap"> {s.day}<sup>{suffix(s.day)}</sup>–{e.day}<sup>{suffix(e.day)}</sup> {s.year} </span>""" simple_dates = f"{s.day}–{e.day} {s.strftime('%B')}" else: fancy_dates = f"""{s.strftime("%B")} {s.day}<sup>{suffix(s.day)}</sup>–{e.strftime("%B")} {e.day}<sup>{suffix(e.day)}</sup>""" simple_dates = ( f"""{s.day} {s.strftime("%B")}–{e.day} {e.strftime("%B")}""" ) return { "fancy_dates": Markup(fancy_dates), "simple_dates": Markup(simple_dates), "event_start": s, "event_end": e, "event_year": s.year, }
def attach_tickets(msg, user): # Attach tickets to a mail Message page = render_receipt(user, pdf=True) url = external_url('tickets.receipt', user_id=user.id) pdf = render_pdf(url, page) msg.attach('EMF{}.pdf'.format(event_start().year), 'application/pdf', pdf.read())
def get_auth_tag(): tag = 'emf-{}-'.format(event_start().year) msg = b'bar-training-' + tag.encode('utf-8') tag += hmac.new(app.config['SECRET_KEY'].encode('utf-8'), msg, digestmod=sha256).hexdigest() return tag
def make_root(): root = etree.Element("schedule") _add_sub_with_text(root, "version", "1.0-public") conference = etree.SubElement(root, "conference") _add_sub_with_text(conference, "title", "Electromagnetic Field {}".format(event_start().year)) _add_sub_with_text(conference, "acronym", "emf{}".format(event_start().year)) _add_sub_with_text(conference, "start", event_start().strftime("%Y-%m-%d")) _add_sub_with_text(conference, "end", event_end().strftime("%Y-%m-%d")) _add_sub_with_text(conference, "days", "3") _add_sub_with_text(conference, "timeslot_duration", "00:10") return root
def event_date_processor(): def suffix(d): return 'th' if 11 <= d <= 13 else {1: 'st', 2: 'nd', 3: 'rd'}.get(d % 10, 'th') s = event_start() e = event_end() assert s.year == e.year if s.month == e.month: fancy_dates = '{s_month} ' \ '<span style="white-space: nowrap">' \ '{s.day}<sup>{s_suff}</sup>—' \ '{e.day}<sup>{e_suff}</sup>' \ '</span>' \ .format(s=s, s_suff=suffix(s.day), s_month=s.strftime('%B'), e=e, e_suff=suffix(e.day)) simple_dates = '{s.day}—' \ '{e.day} ' \ '{s_month}' \ .format(s=s, s_month=s.strftime('%B'), e=e) else: fancy_dates = '{s_month} ' \ '{s.day}<sup>{s_suff}</sup>–' \ '{e_month} ' \ '{e.day}<sup>{e_suff}</sup>' \ .format(s=s, s_suff=suffix(s.day), s_month=s.strftime('%B'), e=e, e_suff=suffix(e.day), e_month=e.strftime('%B')) simple_dates = '{s.day} ' \ '{s_month}–' \ '{e.day} ' \ '{e_month}' \ .format(s=s, s_month=s.strftime('%B'), e=e, e_month=e.strftime('%B')) return { 'fancy_dates': Markup(fancy_dates), 'simple_dates': Markup(simple_dates), 'event_start': s, 'event_end': e, 'event_year': s.year, }
def attach_tickets(msg, user): # Attach tickets to a mail Message page = render_receipt(user, pdf=True) url = external_url('tickets.receipt', user_id=user.id) pdf = render_pdf(url, page) msg.attach('EMF{}.pdf'.format(event_start().year), 'application/pdf', pdf.read()) purchases = user.owned_purchases.filter_by(is_paid_for=True, state='paid') \ .join(PriceTier, Product, ProductGroup) \ .filter(ProductGroup.type.in_(RECEIPT_TYPES)) \ .with_entities(Purchase) \ .group_by(Purchase) \ .order_by(Purchase.id) for p in purchases: p.set_state('receipt-emailed')
def schedule_ical(): schedule = _get_scheduled_proposals(request.args) title = 'EMF {}'.format(event_start().year) cal = Calendar() cal.add('summary', title) cal.add('X-WR-CALNAME', title) cal.add('X-WR-CALDESC', title) cal.add('version', '2.0') for event in schedule: cal_event = Event() cal_event.add('uid', event['id']) cal_event.add('summary', event['title']) cal_event.add('description', event['description']) cal_event.add('location', event['venue']) cal_event.add('dtstart', event['start_date']) cal_event.add('dtend', event['end_date']) cal.add_component(cal_event) return Response(cal.to_ical(), mimetype='text/calendar')
def add_event(room, event): url = external_url("schedule.item", year=event_year(), proposal_id=event["id"], slug=event["slug"]) event_node = etree.SubElement(room, "event", id=str(event["id"]), guid=str(uuid5(NAMESPACE_URL, url))) _add_sub_with_text(event_node, "room", room.attrib["name"]) _add_sub_with_text(event_node, "title", event["title"]) _add_sub_with_text(event_node, "type", event.get("type", "talk")) _add_sub_with_text(event_node, "date", event["start_date"].isoformat()) # Start time _add_sub_with_text(event_node, "start", event["start_date"].strftime("%H:%M")) duration = get_duration(event["start_date"], event["end_date"]) _add_sub_with_text(event_node, "duration", duration) _add_sub_with_text(event_node, "abstract", event["description"]) _add_sub_with_text(event_node, "description", "") _add_sub_with_text( event_node, "slug", "emf%s-%s-%s" % (event_start().year, event["id"], event["slug"]), ) _add_sub_with_text(event_node, "subtitle", "") _add_sub_with_text(event_node, "track", "") add_persons(event_node, event) add_recording(event_node, event)
def schedule_ical(year): if year != event_year(): return feed_historic(year, "ics") schedule = _get_scheduled_proposals(request.args) title = "EMF {}".format(event_start().year) cal = Calendar() cal.add("summary", title) cal.add("X-WR-CALNAME", title) cal.add("X-WR-CALDESC", title) cal.add("version", "2.0") for event in schedule: cal_event = Event() cal_event.add("uid", event["id"]) cal_event.add("summary", event["title"]) cal_event.add("description", _format_event_description(event)) cal_event.add("location", event["venue"]) cal_event.add("dtstart", event["start_date"]) cal_event.add("dtend", event["end_date"]) cal.add_component(cal_event) return Response(cal.to_ical(), mimetype="text/calendar")
def charge_stripe(payment): logger.info("Charging Stripe payment %s, token %s", payment.id, payment.token) # If we fail to go from charging to charged, we won't have the charge ID, # so can't process the webhook. The payment will need to be manually resolved. # Test this with 4000000000000341. payment.state = 'charging' db.session.commit() payment = get_user_payment_or_abort( payment.id, 'stripe', valid_states=['charging'], ) # Stripe say it's max 15 chars, appended to company name, # but it looks like only 10 chars is reliable. description = 'EMF {}'.format(event_start().year) try: try: charge = stripe.Charge.create( amount=payment.amount_int, currency=payment.currency.lower(), card=payment.token, description=payment.description, statement_description=description, ) except stripe.CardError as e: error = e.json_body['error'] logger.warn('Card payment failed with exception "%s"', e) flash('Unfortunately your card payment failed with the error: %s' % (error['message'])) raise except Exception as e: logger.warn("Exception %r confirming payment", e) flash('An error occurred with your payment, please try again') raise except Exception: # Allow trying again payment.state = 'captured' db.session.commit() return redirect(url_for('.stripe_tryagain', payment_id=payment.id)) payment.chargeid = charge.id if charge.paid: payment.paid() else: payment.state = 'charged' payment.expires = datetime.utcnow() + timedelta(days=app.config['EXPIRY_DAYS_STRIPE']) db.session.commit() logger.info('Payment %s completed OK (state %s)', payment.id, payment.state) # FIXME: determine whether these are tickets or generic products msg = Message("Your EMF ticket purchase", sender=app.config.get('TICKETS_EMAIL'), recipients=[payment.user.email]) already_emailed = set_tickets_emailed(payment.user) msg.body = render_template("emails/tickets-purchased-email-stripe.txt", user=payment.user, payment=payment, already_emailed=already_emailed) if feature_enabled('ISSUE_TICKETS') and charge.paid: attach_tickets(msg, payment.user) mail.send(msg) db.session.commit() return redirect(url_for('.stripe_waiting', payment_id=payment.id))
def charge_stripe(payment): logger.info("Charging Stripe payment %s, token %s", payment.id, payment.token) # If we fail to go from charging to charged, we won't have the charge ID, # so can't process the webhook. The payment will need to be manually resolved. # Test this with 4000000000000341. payment.state = 'charging' db.session.commit() payment = get_user_payment_or_abort( payment.id, 'stripe', valid_states=['charging'], ) # max 15 chars, appended to company name description = 'Tickets {}'.format(event_start().year) try: try: charge = stripe.Charge.create( amount=payment.amount_int, currency=payment.currency.lower(), card=payment.token, description=payment.description, statement_description=description, ) except stripe.CardError as e: error = e.json_body['error'] logger.warn('Card payment failed with exception "%s"', e) flash('Unfortunately your card payment failed with the error: %s' % (error['message'])) raise except Exception as e: logger.warn("Exception %r confirming payment", e) flash('An error occurred with your payment, please try again') raise except Exception: # Allow trying again payment.state = 'captured' db.session.commit() return redirect(url_for('.stripe_tryagain', payment_id=payment.id)) payment.chargeid = charge.id if charge.paid: payment.paid() else: payment.state = 'charged' payment.expires = datetime.utcnow() + timedelta( days=app.config['EXPIRY_DAYS_STRIPE']) db.session.commit() logger.info('Payment %s completed OK (state %s)', payment.id, payment.state) # FIXME: determine whether these are tickets or generic products msg = Message("Your EMF ticket purchase", sender=app.config.get('TICKETS_EMAIL'), recipients=[payment.user.email]) msg.body = render_template("emails/tickets-purchased-email-stripe.txt", user=payment.user, payment=payment) if feature_enabled('ISSUE_TICKETS') and charge.paid: attach_tickets(msg, payment.user) mail.send(msg) db.session.commit() return redirect(url_for('.stripe_waiting', payment_id=payment.id))
def refresh(self): request = requests.get(self.url) cal = Calendar.from_ical(request.text) if self.name is None: self.name = cal.get("X-WR-CALNAME") for event in self.events: event.displayed = False local_tz = pendulum.timezone("Europe/London") alerts = [] uids_seen = set() out_of_range_event = False for component in cal.walk(): if component.name == "VEVENT": summary = component.get("Summary") # postgres converts to UTC if given an aware datetime, so strip it up front start_dt = pendulum.instance(component.get("dtstart").dt) start_dt = local_tz.convert(start_dt).naive() end_dt = pendulum.instance(component.get("dtend").dt) end_dt = local_tz.convert(end_dt).naive() name = summary if summary and start_dt: name = "'{}' at {}".format(summary, start_dt) elif summary: name = "'{}'".format(summary) elif start_dt: name = "Event at {}".format(start_dt) else: name = len(self.events) + 1 if not component.get("uid"): alerts.append(("danger", "{} has no UID".format(name))) continue uid = str(component["uid"]) if uid in uids_seen: alerts.append( ("danger", "{} has duplicate UID {}".format(name, uid))) continue uids_seen.add(uid) if "rrule" in component: alerts.append( ("warning", "{} has rrule, which is not processed".format(uid))) # Allow a bit of slop for build-up events if (start_dt < event_start() - pendulum.duration(days=2) and not out_of_range_event): alerts.append(( "warning", "At least one event ({}) is before the start of the event" .format(uid), )) out_of_range_event = True if (end_dt > event_end() + pendulum.duration(days=1) and not out_of_range_event): alerts.append(( "warning", "At least one event ({}) is after the end of the event" .format(uid), )) out_of_range_event = True if start_dt > end_dt: alerts.append(( "danger", "Start time for {} is after its end time".format(uid), )) out_of_range_event = True try: event = CalendarEvent.query.filter_by(source_id=self.id, uid=uid).one() except NoResultFound: event = CalendarEvent(uid=uid) self.events.append(event) if len(self.events) > 1000: raise Exception("Too many events in feed") event.start_dt = start_dt event.end_dt = end_dt event.summary = component.get("summary") event.description = component.get("description") event.location = component.get("location") event.displayed = True self.refreshed_at = pendulum.now() return alerts
def description(self): return 'EMF {} purchase'.format(event_start().year)
def refresh(self): request = requests.get(self.url) cal = Calendar.from_ical(request.text) if self.name is None: self.name = cal.get('X-WR-CALNAME') for event in self.events: event.displayed = False local_tz = pendulum.timezone('Europe/London') alerts = [] uids_seen = set() out_of_range_event = False for component in cal.walk(): if component.name == 'VEVENT': summary = component.get('Summary') # postgres converts to UTC if given an aware datetime, so strip it up front start_dt = pendulum.instance(component.get('dtstart').dt) start_dt = local_tz.convert(start_dt).naive() end_dt = pendulum.instance(component.get('dtend').dt) end_dt = local_tz.convert(end_dt).naive() name = summary if summary and start_dt: name = "'{}' at {}".format(summary, start_dt) elif summary: name = "'{}'".format(summary) elif start_dt: name = 'Event at {}'.format(start_dt) else: name = len(self.events) + 1 if not component.get('uid'): alerts.append(('danger', "{} has no UID".format(name))) continue uid = str(component['uid']) if uid in uids_seen: alerts.append(('danger', "{} has duplicate UID {}".format(name, uid))) continue uids_seen.add(uid) if 'rrule' in component: alerts.append(('warning', "{} has rrule, which is not processed".format(uid))) # Allow a bit of slop for build-up events if start_dt < event_start() - pendulum.duration(days=2) and not out_of_range_event: alerts.append(('warning', "At least one event ({}) is before the start of the event".format(uid))) out_of_range_event = True if end_dt > event_end() + pendulum.duration(days=1) and not out_of_range_event: alerts.append(('warning', "At least one event ({}) is after the end of the event".format(uid))) out_of_range_event = True if start_dt > end_dt: alerts.append(('danger', "Start time for {} is after its end time".format(uid))) out_of_range_event = True try: event = CalendarEvent.query.filter_by(source_id=self.id, uid=uid).one() except NoResultFound: event = CalendarEvent(uid=uid) self.events.append(event) if len(self.events) > 1000: raise Exception("Too many events in feed") event.start_dt = start_dt event.end_dt = end_dt event.summary = component.get('summary') event.description = component.get('description') event.location = component.get('location') event.displayed = True self.refreshed_at = pendulum.now() return alerts