diff --git a/export.py b/export.py
new file mode 100755
index 0000000000000000000000000000000000000000..97ad6699da5898c7a3df108cdb482da3528a5aa2
--- /dev/null
+++ b/export.py
@@ -0,0 +1,162 @@
+#!venv/bin/python
+
+from flask import Flask, render_template, jsonify, request
+from flask_sqlalchemy import SQLAlchemy
+from lxml import etree
+from argparse import ArgumentParser
+import requests
+import json
+import sys
+
+dryrun=False
+
+parser = ArgumentParser(description="C3 rating helper")
+parser.add_argument("-d", action="store_true", dest="dryrun", default=False, help="Don't actually execute anything on frab or rt, just show what would have been done")
+args = parser.parse_args()
+
+if args.dryrun:
+    dryrun=True
+
+with open(args.config, mode="r", encoding="utf-8") as json_file:
+    cfg = json.load(json_file)
+
+app = Flask(__name__)
+app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + cfg['frab-conference'] + '-' + cfg['track'] + '.db'
+app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
+app.config['SECRET_KEY'] = 'Silence is golden. Gerd Eist.'
+app.jinja_env.trim_blocks = True
+app.jinja_env.lstrip_blocks = True
+
+db = SQLAlchemy(app)
+
+config['rt-rest-url'] = cfg['rt-url'] + 'REST/1.0/'
+config['frab-conf-url'] = config['frab-url'] + config['frab-conference']
+
+class Event(db.Model):
+    """An event as dumped from frab"""
+    frab_id = db.Column(db.Integer, primary_key=True)
+    title = db.Column(db.String(1024))
+    subtitle = db.Column(db.String(1024))
+    abstract = db.Column(db.Text())
+    description = db.Column(db.Text())
+    state = db.Column(db.String(64))
+    event_type = db.Column(db.String(64))
+    speakers = db.Column(db.String(1024))
+    coordinator = db.Column(db.String(1024))
+    notes = db.Column(db.Text())
+
+class EventRating(db.Model):
+    """A rating as given by a logged in user"""
+    id = db.Column(db.Integer, primary_key=True)
+    submitter = db.Column(db.String(1024))
+    event_id = db.Column(db.Integer, db.ForeignKey('event.frab_id'))
+    event = db.relationship('Event', backref=db.backref('ratings', lazy='dynamic'))
+    comment = db.Column(db.Text())
+    rating_dict = db.Column(db.String(1024), server_default="{}")
+
+def add_coordinator(sess, person, event):
+    edit_person_page = sess.get(config['frab-conf-url'] + '/events/' + event + '/edit_people')
+    tree = etree.HTML(edit_person_page.text)
+    for option in tree.xpath('//option[@selected]'):
+        if option.text.lower() == 'coordinator':
+            print ('Not patching: ' + person + ' on event ' + event + ', coordinator found')
+            return
+        # print (option.text)
+    print ('Patching: ' + person + ' on event ' + event)
+
+    if dryrun:
+        return
+
+    add_person_data = dict()
+    add_person_data['utf8'] = '✓'
+    add_person_data['commit'] = 'Update event'
+    add_person_data['_method'] = 'PATCH'
+    add_person_data['filter'] = ''
+    add_person_data['authenticity_token'] = tree.xpath("//meta[@name='csrf-token']")[0].get("content")
+    add_person_data['event[event_people_attributes][1510531378][person_id]'] = person
+    add_person_data['event[event_people_attributes][1510531378][event_role]'] = 'coordinator'
+    add_person_data['event[event_people_attributes][1510531378][role_state]'] = ''
+    add_person_data['event[event_people_attributes][1510531378][_destroy]'] = 'false'
+    response = sess.post(config['frab-conf-url'] + '/events/' + event, add_person_data)
+    # print (response.text)
+
+def add_ticket(sess, rt_sess, event, person):
+    event_page = sess.get(config['frab-conf-url'] + '/events/' + event)
+    tree = etree.HTML(event_page.text)
+    click = ''
+    for button in tree.xpath('//a[@class="btn primary"]/@href'):
+        if '/tickets/' in button:
+            click = button
+            break
+    if not click:
+        print ('No add ticket link found, event already has a ticket?')
+        return
+    print ('Using add ticket link: ' + click)
+
+    if dryrun:
+        return
+
+    ticket_data = dict()
+    ticket_data['authenticity_token'] = tree.xpath("//meta[@name='csrf-token']")[0].get("content")
+    ticket_data['_method'] = 'POST'
+    response = sess.post(cfg['frab-url'] + click, ticket_data)
+    tree = etree.HTML(response.text)
+    ticket_id = ''
+    for link in tree.xpath('//a/@href'):
+        if '/Ticket/Display.html?' in link:
+            ticket_id = link.split('=')[1]
+            print ('Found new ticket, #'+ticket_id)
+            response = rt_sess.post(config['rt-rest-url'] + 'ticket/' + ticket_id + '/edit', { "content": "id: ticket/"+ticket_id + '\nOwner: ' + person + '\n' }, headers={ 'referer': config['rt-rest-url']})
+            print (response.text)
+    if not ticket_id:
+        print ('Failed to add ticket to event ' + event)
+
+def set_state(sess, event, state):
+    event_page = sess.get(config['frab-conf-url'] + '/events/' + event)
+    tree = etree.HTML(event_page.text)
+    click = ''
+    for button in tree.xpath('//a[starts-with(@class,"btn")]/@href'):
+        if '?transition=' + state in button:
+            click = button
+            break
+    if not click:
+        print ('No ' + state + ' button found. State already set?')
+        return
+    print ('Using set state link: ' + click)
+    if dryrun:
+        return
+    set_state_data = dict()
+    set_state_data['authenticity_token'] = tree.xpath("//meta[@name='csrf-token']")[0].get("content")
+    set_state_data['_method'] = 'PUT'
+    response = sess.post(cgf['frab-url'] + click, set_state_data)
+
+def put_talks():
+    sess = requests.Session()
+    new_session_page = sess.get(cfg['frab-url'])
+    tree = etree.HTML(new_session_page.text)
+    login_data = dict()
+    login_data['user[email]'] = cfg['frab-user']
+    login_data['user[password]'] = cfg['frab-password']
+    login_data['user[remember_me]'] = 1
+    login_data['authenticity_token'] = tree.xpath("//meta[@name='csrf-token']")[0].get("content")
+    sess.post(cfg['frab-url'] + 'users/sign_in?conference_acronym=' + cfg['frab-conference'] + '&locale=en', login_data)
+
+    rt_sess = requests.Session()
+    rt_sess.post(cfg['rt-url']+'index.html', {"user": cfg['rt-user'], "pass": cfg['rt-password'] })
+
+    for event in Event.query.all():
+        if event.coordinator:
+            if event.coordinator in person_map:
+                print (str(event.frab_id) + ': ' + event.coordinator + '(' + cfg.get('frab-person-map')[event.coordinator]+')')
+                add_coordinator(sess, cfg.get('frab-person-map')[event.coordinator], str(event.frab_id))
+                add_ticket(sess, rt_sess, str(event.frab_id), cfg.get('rt-person-map')[event.coordinator])
+            else:
+                print ('Unknown coordinator '+event.coordinator+' for event '+str(event.frab_id))
+        if event.state in [ 'accepted', 'rejected' ]:
+            set_state(sess, str(event.frab_id), 'accept' if event.state == 'accepted' else 'reject')
+        else:
+            print ('Unknown state ' + event.state + ' for event ' + str(event.frab_id))
+
+if __name__ == "__main__":
+    db.create_all()
+    put_talks()