-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.wsgi
356 lines (278 loc) · 10.4 KB
/
index.wsgi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
import os, sys, time
import string
from collections import OrderedDict
from configparser import ConfigParser
# =============================================================================
# 1) CONFIG FILE
# =============================================================================
import sqlite3
from flask import Response
from flask import json
from flask.templating import render_template
from flask import request
class DefCP(ConfigParser):
def get(self, option, *args, **kwargs):
return super().get('DEFAULT', option, **kwargs)
c = DefCP()
mydir = os.path.dirname(__file__)
c.read(os.path.join(mydir, 'config.ini'))
# -------------------------------------------
# Add required dependencies to the path.
# -------------------------------------------
paths = c.get('pythonpath', fallback='').split(':')
for path in paths:
sys.path.insert(0, path)
# -------------------------------------------
# Import other modules.
# -------------------------------------------
from freki.serialize import FrekiDoc, FrekiLine
# -------------------------------------------
# Do other environment setup.
# -------------------------------------------
freki_root = c.get('freki_root')
# =============================================================================
# URLs
# =============================================================================
from flask import Flask
app = Flask(__name__)
application = app
app.debug = True
def tagsort(tag):
tagorder = ['L','G','T','M','O']
if tag in tagorder:
return tagorder.index(tag)
else:
return len(tagorder)
config = {k:c.get(k) for k in c.defaults().keys()}
config['tags'] = sorted(c.get('tags').split(','), key=tagsort)
config['flags'] = sorted(c.get('flags').split(','))
@app.route('/')
def home():
return render_template('home.html', **config)
@app.route('/valid/<dir>')
def isvalid(dir):
full_dir_path = os.path.join(freki_root, os.path.basename(dir))
if os.path.exists(full_dir_path):
return json.dumps({'dir':dir, 'exists':True})
else:
return json.dumps({'dir':dir, 'exists':False})
@app.route('/dir/<dir>')
def dir_list(dir):
full_dir_path = os.path.join(freki_root, os.path.basename(dir))
try:
contents = sorted(os.listdir(full_dir_path), key=lambda x: int(x.split('.')[0]))
except:
contents = sorted(os.listdir(full_dir_path))
saves = modified_files(dir)
return render_template('browser.html',
contents=contents,
dir=dir,
saves=saves,
**config)
def get_span_type(fd, lineno):
"""
:type fd: FrekiDoc
:type lineno: int
"""
# -------------------------------------------
# Determine whether the current span is new
# or continuing
# -------------------------------------------
line = fd.linemap[lineno]
assert isinstance(line, FrekiLine)
prev_span_id = fd.linemap[lineno - 1].span_id if lineno > 1 else None
span_parts = line.span_id.split('-') if line.span_id else 0
# It's a "new span" if transitioning from
# no span_id or if the previous line's span_id
# doesn't match this one.
new_span = (line.span_id is not None and
(prev_span_id == None or
line.span_id != prev_span_id))
# It's a "continuing" span if this span_id
# has a letter suffix that's not "a"
span_cont = (line.span_id is not None and
len(span_parts) > 1 and
span_parts[1] in string.ascii_lowercase and
span_parts[1] != 'a')
span_type = 'prev'
if new_span and not span_cont:
span_type = 'new'
elif new_span and span_cont:
span_type = 'cont'
# -------------------------------------------
return span_type
def is_new_block(fd, lineno):
"""
:type fd: FrekiDoc
:type lineno: int
"""
if lineno == 1:
return False
else:
line = fd.linemap[lineno]
prev_line = fd.linemap[lineno-1]
return line.block.block_id != prev_line.block.block_id
def read_frekidoc(fd):
"""
Return a dict of span types for each line: prev, new, or cont(inuing)
:type fd: FrekiDoc
:rtype: dict
"""
meta = OrderedDict()
text = OrderedDict()
for lineno in fd.linemap:
line = fd.linemap[lineno] # Get the current FrekiLine
assert isinstance(line, FrekiLine)
lang_name = line.attrs.get('lang_name')
lang_code = line.attrs.get('lang_code')
cur_line_dict = {'tag':line.tag}
if line.tag != 'O':
cur_line_dict['span_type'] = get_span_type(fd, lineno)
if lang_name is not None:
cur_line_dict['lang_name'] = lang_name
if lang_code is not None:
cur_line_dict['lang_code'] = lang_name
if is_new_block(fd, lineno):
cur_line_dict['new_block'] = True
meta[lineno] = cur_line_dict
text[lineno] = str(line)
return meta, text
def get_frekitext(dir, doc_id):
# Get the range to display:
start_line = int(request.args.get('start', 1))
num_lines = int(request.args.get('range', 100))
# Read in the freki doc...
fd = FrekiDoc.read(os.path.join(os.path.join(freki_root, dir), doc_id))
max_lineno = max(fd.linemap)
end_line = min(max_lineno, start_line + num_lines - 1)
fd_meta, fd_text = read_frekidoc(fd)
# Now, filter down the amount of text we want to display.
lines = OrderedDict()
for lineno in fd_text:
if start_line <= lineno <= end_line:
lines[lineno] = fd_text[lineno]
html = render_template('doc.html',
lines=lines,
line_meta=fd_meta,
doc_id=doc_id,
**config)
data = {'start_line': start_line,
'end_line': end_line,
'max_line': max_lineno,
'doc_id': doc_id,
'html':html,
'meta':fd_meta}
return data
@app.route('/text/<dir>/<doc_id>', methods=['GET'])
def load_text(dir, doc_id):
data = get_frekitext(dir, doc_id)
del data['meta']
return json.dumps(data)
@app.route('/load/<dir>/<doc_id>', methods=['GET'])
def load_dir(dir, doc_id):
data = get_frekitext(dir, doc_id)
return json.dumps(data)
def assign_spans(line_data):
"""
From the dict of line data,
create a span_id for each line.
"""
span_dict = {}
line_numbers = sorted([int(i) for i in line_data.keys()])
span_count = 0
def get_span_type(lineno): return line_data[str(lineno)].get('span_type')
def name_lettered_span(span_num, num_prev_spans): return 's{}-{}'.format(span_num, string.ascii_lowercase[num_prev_spans])
cur_span = None
for lineno in line_numbers:
span_type = get_span_type(lineno)
if span_type == 'new':
span_count += 1
cur_span = 's{}'.format(span_count)
span_dict[lineno] = cur_span
elif span_type == 'cont':
# If the current span is "continuing,"
# walk backward until a "new" span is
# seen. This will make this span
# n+1
num_prev_spans = 0
prev_line_counter = lineno-1
while prev_line_counter > 0:
prev_span_type = get_span_type(prev_line_counter)
if prev_span_type == 'new':
num_prev_spans += 1
span_dict[prev_line_counter] = name_lettered_span(span_count, 0)
break
elif prev_span_type == 'cont':
num_prev_spans += 1
prev_line_counter -=1
span_dict[lineno] = name_lettered_span(span_count, num_prev_spans)
# One more pass for the "prev_spans"
cur_span = None
for lineno in line_numbers:
span_type = get_span_type(lineno)
if span_type in ['new', 'cont']:
cur_span = span_dict[lineno]
elif span_type == 'prev':
span_dict[lineno] = cur_span
return span_dict
@app.route('/save/<dir>/<doc_id>', methods=['POST'])
def save(dir, doc_id):
"""
Save the data from the editor to file.
"""
data = request.get_json()
line_numbers = sorted([int(i) for i in data.keys()])
span_ids = assign_spans(data)
path = os.path.join(os.path.join(freki_root, dir), doc_id)
fd = FrekiDoc.read(path)
for lineno in line_numbers:
line = fd.get_line(lineno)
new_tag = data[str(lineno)]['tag']
if new_tag.split('+')[0] == 'O':
line.tag = None
else:
line.tag = new_tag
line.span_id = span_ids.get(lineno)
with open(path, 'w') as f:
f.write(str(fd))
save_to_db(dir, doc_id)
return json.jsonify({'success':True})
@app.route('/finish/<dir>/<doc_id>', methods=['POST'])
def finish(dir, doc_id):
save(dir, doc_id)
flag_complete(dir, doc_id)
return Response(response='OK', status=200)
# Just write that a file was saved, and the timestamp
# so that we can log modifications.
db_path = os.path.join(os.path.dirname(__file__), 'saves.db')
def save_to_db(dir, file):
db = sqlite3.connect(db_path)
db.execute("""CREATE TABLE IF NOT EXISTS saves
(dir TEXT,
file TEXT,
modified REAL,
complete INTEGER)""")
db.execute("""INSERT OR REPLACE INTO saves VALUES ('{}', '{}', {}, 0)""".format(dir, file, time.time()))
db.commit()
db.close()
def flag_complete(dir, file):
"""
Flag this file as "complete" in the database.
:param dir: The directory that the file is contained in
:param file: The filename
"""
db = sqlite3.connect(db_path)
c = db.execute("SELECT * FROM saves WHERE dir = '{}' AND file='{}'".format(dir, file))
if not (c.fetchall()):
db.execute("INSERT INTO saves VALUES ('{}', '{}', {}, 1)".format(dir, file, time.time()))
else:
db.execute("UPDATE saves SET complete = 1 WHERE dir = '{}' and file = '{}'".format(dir, file))
db.commit()
db.close()
def modified_files(dir):
if not os.path.exists(db_path):
return {}
else:
db = sqlite3.connect(db_path)
c = db.execute("""SELECT * FROM saves WHERE dir = '{}'""".format(dir))
return {file:bool(complete) for dir, file, timestamp, complete in c.fetchall()}