diff options
-rwxr-xr-x | export.py | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/export.py b/export.py new file mode 100755 index 0000000..97ad669 --- /dev/null +++ b/export.py | |||
@@ -0,0 +1,162 @@ | |||
1 | #!venv/bin/python | ||
2 | |||
3 | from flask import Flask, render_template, jsonify, request | ||
4 | from flask_sqlalchemy import SQLAlchemy | ||
5 | from lxml import etree | ||
6 | from argparse import ArgumentParser | ||
7 | import requests | ||
8 | import json | ||
9 | import sys | ||
10 | |||
11 | dryrun=False | ||
12 | |||
13 | parser = ArgumentParser(description="C3 rating helper") | ||
14 | 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") | ||
15 | args = parser.parse_args() | ||
16 | |||
17 | if args.dryrun: | ||
18 | dryrun=True | ||
19 | |||
20 | with open(args.config, mode="r", encoding="utf-8") as json_file: | ||
21 | cfg = json.load(json_file) | ||
22 | |||
23 | app = Flask(__name__) | ||
24 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + cfg['frab-conference'] + '-' + cfg['track'] + '.db' | ||
25 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False | ||
26 | app.config['SECRET_KEY'] = 'Silence is golden. Gerd Eist.' | ||
27 | app.jinja_env.trim_blocks = True | ||
28 | app.jinja_env.lstrip_blocks = True | ||
29 | |||
30 | db = SQLAlchemy(app) | ||
31 | |||
32 | config['rt-rest-url'] = cfg['rt-url'] + 'REST/1.0/' | ||
33 | config['frab-conf-url'] = config['frab-url'] + config['frab-conference'] | ||
34 | |||
35 | class Event(db.Model): | ||
36 | """An event as dumped from frab""" | ||
37 | frab_id = db.Column(db.Integer, primary_key=True) | ||
38 | title = db.Column(db.String(1024)) | ||
39 | subtitle = db.Column(db.String(1024)) | ||
40 | abstract = db.Column(db.Text()) | ||
41 | description = db.Column(db.Text()) | ||
42 | state = db.Column(db.String(64)) | ||
43 | event_type = db.Column(db.String(64)) | ||
44 | speakers = db.Column(db.String(1024)) | ||
45 | coordinator = db.Column(db.String(1024)) | ||
46 | notes = db.Column(db.Text()) | ||
47 | |||
48 | class EventRating(db.Model): | ||
49 | """A rating as given by a logged in user""" | ||
50 | id = db.Column(db.Integer, primary_key=True) | ||
51 | submitter = db.Column(db.String(1024)) | ||
52 | event_id = db.Column(db.Integer, db.ForeignKey('event.frab_id')) | ||
53 | event = db.relationship('Event', backref=db.backref('ratings', lazy='dynamic')) | ||
54 | comment = db.Column(db.Text()) | ||
55 | rating_dict = db.Column(db.String(1024), server_default="{}") | ||
56 | |||
57 | def add_coordinator(sess, person, event): | ||
58 | edit_person_page = sess.get(config['frab-conf-url'] + '/events/' + event + '/edit_people') | ||
59 | tree = etree.HTML(edit_person_page.text) | ||
60 | for option in tree.xpath('//option[@selected]'): | ||
61 | if option.text.lower() == 'coordinator': | ||
62 | print ('Not patching: ' + person + ' on event ' + event + ', coordinator found') | ||
63 | return | ||
64 | # print (option.text) | ||
65 | print ('Patching: ' + person + ' on event ' + event) | ||
66 | |||
67 | if dryrun: | ||
68 | return | ||
69 | |||
70 | add_person_data = dict() | ||
71 | add_person_data['utf8'] = '✓' | ||
72 | add_person_data['commit'] = 'Update event' | ||
73 | add_person_data['_method'] = 'PATCH' | ||
74 | add_person_data['filter'] = '' | ||
75 | add_person_data['authenticity_token'] = tree.xpath("//meta[@name='csrf-token']")[0].get("content") | ||
76 | add_person_data['event[event_people_attributes][1510531378][person_id]'] = person | ||
77 | add_person_data['event[event_people_attributes][1510531378][event_role]'] = 'coordinator' | ||
78 | add_person_data['event[event_people_attributes][1510531378][role_state]'] = '' | ||
79 | add_person_data['event[event_people_attributes][1510531378][_destroy]'] = 'false' | ||
80 | response = sess.post(config['frab-conf-url'] + '/events/' + event, add_person_data) | ||
81 | # print (response.text) | ||
82 | |||
83 | def add_ticket(sess, rt_sess, event, person): | ||
84 | event_page = sess.get(config['frab-conf-url'] + '/events/' + event) | ||
85 | tree = etree.HTML(event_page.text) | ||
86 | click = '' | ||
87 | for button in tree.xpath('//a[@class="btn primary"]/@href'): | ||
88 | if '/tickets/' in button: | ||
89 | click = button | ||
90 | break | ||
91 | if not click: | ||
92 | print ('No add ticket link found, event already has a ticket?') | ||
93 | return | ||
94 | print ('Using add ticket link: ' + click) | ||
95 | |||
96 | if dryrun: | ||
97 | return | ||
98 | |||
99 | ticket_data = dict() | ||
100 | ticket_data['authenticity_token'] = tree.xpath("//meta[@name='csrf-token']")[0].get("content") | ||
101 | ticket_data['_method'] = 'POST' | ||
102 | response = sess.post(cfg['frab-url'] + click, ticket_data) | ||
103 | tree = etree.HTML(response.text) | ||
104 | ticket_id = '' | ||
105 | for link in tree.xpath('//a/@href'): | ||
106 | if '/Ticket/Display.html?' in link: | ||
107 | ticket_id = link.split('=')[1] | ||
108 | print ('Found new ticket, #'+ticket_id) | ||
109 | 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']}) | ||
110 | print (response.text) | ||
111 | if not ticket_id: | ||
112 | print ('Failed to add ticket to event ' + event) | ||
113 | |||
114 | def set_state(sess, event, state): | ||
115 | event_page = sess.get(config['frab-conf-url'] + '/events/' + event) | ||
116 | tree = etree.HTML(event_page.text) | ||
117 | click = '' | ||
118 | for button in tree.xpath('//a[starts-with(@class,"btn")]/@href'): | ||
119 | if '?transition=' + state in button: | ||
120 | click = button | ||
121 | break | ||
122 | if not click: | ||
123 | print ('No ' + state + ' button found. State already set?') | ||
124 | return | ||
125 | print ('Using set state link: ' + click) | ||
126 | if dryrun: | ||
127 | return | ||
128 | set_state_data = dict() | ||
129 | set_state_data['authenticity_token'] = tree.xpath("//meta[@name='csrf-token']")[0].get("content") | ||
130 | set_state_data['_method'] = 'PUT' | ||
131 | response = sess.post(cgf['frab-url'] + click, set_state_data) | ||
132 | |||
133 | def put_talks(): | ||
134 | sess = requests.Session() | ||
135 | new_session_page = sess.get(cfg['frab-url']) | ||
136 | tree = etree.HTML(new_session_page.text) | ||
137 | login_data = dict() | ||
138 | login_data['user[email]'] = cfg['frab-user'] | ||
139 | login_data['user[password]'] = cfg['frab-password'] | ||
140 | login_data['user[remember_me]'] = 1 | ||
141 | login_data['authenticity_token'] = tree.xpath("//meta[@name='csrf-token']")[0].get("content") | ||
142 | sess.post(cfg['frab-url'] + 'users/sign_in?conference_acronym=' + cfg['frab-conference'] + '&locale=en', login_data) | ||
143 | |||
144 | rt_sess = requests.Session() | ||
145 | rt_sess.post(cfg['rt-url']+'index.html', {"user": cfg['rt-user'], "pass": cfg['rt-password'] }) | ||
146 | |||
147 | for event in Event.query.all(): | ||
148 | if event.coordinator: | ||
149 | if event.coordinator in person_map: | ||
150 | print (str(event.frab_id) + ': ' + event.coordinator + '(' + cfg.get('frab-person-map')[event.coordinator]+')') | ||
151 | add_coordinator(sess, cfg.get('frab-person-map')[event.coordinator], str(event.frab_id)) | ||
152 | add_ticket(sess, rt_sess, str(event.frab_id), cfg.get('rt-person-map')[event.coordinator]) | ||
153 | else: | ||
154 | print ('Unknown coordinator '+event.coordinator+' for event '+str(event.frab_id)) | ||
155 | if event.state in [ 'accepted', 'rejected' ]: | ||
156 | set_state(sess, str(event.frab_id), 'accept' if event.state == 'accepted' else 'reject') | ||
157 | else: | ||
158 | print ('Unknown state ' + event.state + ' for event ' + str(event.frab_id)) | ||
159 | |||
160 | if __name__ == "__main__": | ||
161 | db.create_all() | ||
162 | put_talks() | ||