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
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
|
#! /usr/bin/python3
# -*- coding: utf-8 -*-
###########################################################################
# Copyright 2019 (C) Hui Lan <hui.lan@cantab.net>
# Written permission must be obtained from the author for commercial uses.
###########################################################################
from WordFreq import WordFreq
from wordfreqCMD import youdao_link, sort_in_descending_order
from UseSqlite import InsertQuery, RecordQuery
import pickle_idea, pickle_idea2
import os
import random, glob
from datetime import datetime
from flask import Flask, request, redirect, render_template, url_for, session, abort, flash
from difficulty import get_difficulty_level, text_difficulty_level, user_difficulty_level
app = Flask(__name__)
app.secret_key = 'lunch.time!'
path_prefix = '/var/www/wordfreq/wordfreq/'
path_prefix = './' # comment this line in deployment
def get_random_image(path):
img_path = random.choice(glob.glob(os.path.join(path, '*.jpg')))
return img_path[img_path.rfind('/static'):]
def get_random_ads():
ads = random.choice(['个性化分析精准提升', '你的专有单词本', '智能捕捉阅读弱点,针对性提高你的阅读水平'])
return ads + '。 <a href="/signup">试试</a>吧!'
def load_freq_history(path):
d = {}
if os.path.exists(path):
d = pickle_idea.load_record(path)
return d
def verify_user(username, password):
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
rq.instructions("SELECT * FROM user WHERE name='%s' AND password='%s'" % (username, password))
rq.do()
result = rq.get_results()
return result != []
def add_user(username, password):
start_date = datetime.now().strftime('%Y%m%d')
expiry_date = start_date
rq = InsertQuery(path_prefix + 'static/wordfreqapp.db')
rq.instructions("INSERT INTO user Values ('%s', '%s', '%s', '%s')" % (username, password, start_date, expiry_date))
rq.do()
def check_username_availability(username):
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
rq.instructions("SELECT * FROM user WHERE name='%s'" % (username))
rq.do()
result = rq.get_results()
return result == []
def get_expiry_date(username):
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
rq.instructions("SELECT expiry_date FROM user WHERE name='%s'" % (username))
rq.do()
result = rq.get_results()
if len(result) > 0:
return result[0]['expiry_date']
else:
return '20191024'
def within_range(x, y, r):
return x > y and abs(x - y) <= r
def get_today_article(user_word_list, articleID):
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
if articleID == None:
rq.instructions("SELECT * FROM article")
else:
rq.instructions('SELECT * FROM article WHERE article_id=%d' % (articleID))
rq.do()
result = rq.get_results()
# Choose article according to reader's level
d1 = load_freq_history(path_prefix + 'static/frequency/frequency.p')
d2 = load_freq_history(path_prefix + 'static/words_and_tests.p')
d3 = get_difficulty_level(d1, d2)
d = {}
d_user = load_freq_history(user_word_list)
user_level = user_difficulty_level(d_user, d3) # more consideration as user's behaviour is dynamic. Time factor should be considered.
random.shuffle(result) # shuffle list
d = random.choice(result)
text_level = text_difficulty_level(d['text'], d3)
if articleID == None:
for reading in result:
text_level = text_difficulty_level(reading['text'], d3)
#print('TEXT_LEVEL %4.2f' % (text_level))
if within_range(text_level, user_level, 0.5):
d = reading
break
s = '<p><i>According to your word list, your level is <b>%4.2f</b> and we have chosen an article with a difficulty level of <b>%4.2f</b> for you.</i></p>' % (user_level, text_level)
s += '<p><b>%s</b></p>' % (d['date'])
s += '<p><font size=+2>%s</font></p>' % (d['text'])
s += '<p><i>%s</i></p>' % (d['source'])
s += '<p><b>%s</b></p>' % (get_question_part(d['question']))
s = s.replace('\n', '<br/>')
s += '%s' % (get_answer_part(d['question']))
session['articleID'] = d['article_id']
return s
def appears_in_test(word, d):
if not word in d:
return ''
else:
return ','.join(d[word])
def get_time():
return datetime.now().strftime('%Y%m%d%H%M') # upper to minutes
def get_question_part(s):
s = s.strip()
result = []
flag = 0
for line in s.split('\n'):
line = line.strip()
if line == 'QUESTION':
result.append(line)
flag = 1
elif line == 'ANSWER':
flag = 0
elif flag == 1:
result.append(line)
return '\n'.join(result)
def get_answer_part(s):
s = s.strip()
result = []
flag = 0
for line in s.split('\n'):
line = line.strip()
if line == 'ANSWER':
flag = 1
elif flag == 1:
result.append(line)
# https://css-tricks.com/snippets/javascript/showhide-element/
js = '''
<script type="text/javascript">
function toggle_visibility(id) {
var e = document.getElementById(id);
if(e.style.display == 'block')
e.style.display = 'none';
else
e.style.display = 'block';
}
</script>
'''
html_code = js
html_code += '\n'
html_code += '<button onclick="toggle_visibility(\'answer\');">ANSWER</button>\n'
html_code += '<div id="answer" style="display:none;">%s</div>\n' % ('\n'.join(result))
return html_code
@app.route("/<username>/reset", methods=['GET', 'POST'])
def user_reset(username):
if request.method == 'GET':
session['articleID'] = None
return redirect(url_for('userpage', username=username))
else:
return 'Under construction'
@app.route("/mark", methods=['GET', 'POST'])
def mark_word():
if request.method == 'POST':
d = load_freq_history(path_prefix + 'static/frequency/frequency.p')
lst_history = pickle_idea.dict2lst(d)
lst = []
for word in request.form.getlist('marked'):
lst.append((word, 1))
d = pickle_idea.merge_frequency(lst, lst_history)
pickle_idea.save_frequency_to_pickle(d, path_prefix + 'static/frequency/frequency.p')
return redirect(url_for('mainpage'))
else:
return 'Under construction'
@app.route("/", methods=['GET', 'POST'])
def mainpage():
if request.method == 'POST': # when we submit a form
content = request.form['content']
f = WordFreq(content)
lst = f.get_freq()
page = '<form method="post" action="/mark">\n'
count = 1
for x in lst:
page += '<p><font color="grey">%d</font>: <a href="%s">%s</a> (%d) <input type="checkbox" name="marked" value="%s"></p>\n' % (count, youdao_link(x[0]), x[0], x[1], x[0])
count += 1
page += ' <input type="submit" value="确定并返回"/>\n'
page += '</form>\n'
# save history
d = load_freq_history(path_prefix + 'static/frequency/frequency.p')
lst_history = pickle_idea.dict2lst(d)
d = pickle_idea.merge_frequency(lst, lst_history)
pickle_idea.save_frequency_to_pickle(d, path_prefix + 'static/frequency/frequency.p')
return page
elif request.method == 'GET': # when we load a html page
page = '''
<html lang="zh">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />
<title>EnglishPal 英文单词高效记</title>
</head>
<body>
'''
page += '<p><b><font size="+3" color="red">English Pal - Learn English in a smart way!</font></b></p>'
if session.get('logged_in'):
page += ' <a href="%s">%s</a></p>\n' % (session['username'], session['username'])
else:
page += '<p><a href="/login">登录</a> <a href="/signup">成为会员</a> <a href="/static/usr/instructions.html">使用说明</a></p>\n'
#page += '<p><img src="%s" width="400px" alt="advertisement"/></p>' % (get_random_image(path_prefix + 'static/img/'))
page += '<p><b>%s</b></p>' % (get_random_ads())
page += '<p>粘帖1篇文章 (English only)</p>'
page += '<form method="post" action="/">'
page += ' <textarea name="content" rows="10" cols="120"></textarea><br/>'
page += ' <input type="submit" value="get文章中的词频"/>'
page += ' <input type="reset" value="清除"/>'
page += '</form>'
d = load_freq_history(path_prefix + 'static/frequency/frequency.p')
if len(d) > 0:
page += '<p><b>最常见的词</b></p>'
for x in sort_in_descending_order(pickle_idea.dict2lst(d)):
if x[1] <= 99:
break
page += '<a href="%s">%s</a> %d\n' % (youdao_link(x[0]), x[0], x[1])
page += '</body></html>'
return page
@app.route("/<username>/mark", methods=['GET', 'POST'])
def user_mark_word(username):
username = session[username]
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
if request.method == 'POST':
d = load_freq_history(user_freq_record)
lst_history = pickle_idea2.dict2lst(d)
lst = []
for word in request.form.getlist('marked'):
lst.append((word, [get_time()]))
d = pickle_idea2.merge_frequency(lst, lst_history)
pickle_idea2.save_frequency_to_pickle(d, user_freq_record)
return redirect(url_for('userpage', username=username))
else:
return 'Under construction'
@app.route("/<username>", methods=['GET', 'POST'])
def userpage(username):
if not session.get('logged_in'):
return '<p>请先<a href="/login">登录</a>。</p>'
user_expiry_date = session.get('expiry_date')
if datetime.now().strftime('%Y%m%d') > user_expiry_date:
return '<p>账号 %s 过期。</p><p>为了提高服务质量,English Pal 收取会员费用, 每天1元。</p> <p>请决定你要试用的时间长度,扫描下面支付宝二维码支付。 支付时请注明<i>English Pal Membership Fee</i>。 我们会于12小时内激活账号。</p><p><img src="static/donate-the-author.jpg" width="120px" alt="支付宝二维码" /></p><p>如果有问题,请加开发者微信 torontohui。</p> <p><a href="/logout">登出</a></p>' % (username)
username = session.get('username')
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
if request.method == 'POST': # when we submit a form
content = request.form['content']
f = WordFreq(content)
lst = f.get_freq()
page = '<meta charset="UTF8">'
page += '<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />'
page += '<p>勾选不认识的单词</p>'
page += '<form method="post" action="/%s/mark">\n' % (username)
count = 1
words_tests_dict = pickle_idea.load_record(path_prefix + 'static/words_and_tests.p')
for x in lst:
page += '<p><font color="grey">%d</font>: <a href="%s" title="%s">%s</a> (%d) <input type="checkbox" name="marked" value="%s"></p>\n' % (count, youdao_link(x[0]), appears_in_test(x[0], words_tests_dict), x[0], x[1], x[0])
count += 1
page += ' <input name="add-btn" type="submit" value="加入我的生词簿"/>\n'
page += '</form>\n'
return page
elif request.method == 'GET': # when we load a html page
page = '<meta charset="UTF8">\n'
page += '<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />\n'
page += '<meta name="format-detection" content="telephone=no" />\n' # forbid treating numbers as cell numbers in smart phones
page += '<title>EnglishPal Study Room for %s</title>' % (username)
page += '<p><b>English Pal for <font color="red">%s</font></b> <a href="/logout">登出</a></p>' % (username)
page += '<p><a href="/%s/reset">下一篇</a></p>' % (username)
page += '<p><b>阅读文章并回答问题</b></p>\n'
page += '<div id="text-content">%s</div>' % (get_today_article(user_freq_record, session['articleID']))
page += '<p><b>收集生词吧</b> (可以在正文中划词,也可以复制黏贴)</p>'
page += '<form method="post" action="/%s">' % (username)
page += ' <textarea name="content" id="selected-words" rows="10" cols="120"></textarea><br/>'
page += ' <input type="submit" value="get 所有词的频率"/>'
page += ' <input type="reset" value="清除"/>'
page += '</form>\n'
page += '''
<script>
function getWord(){
var word = window.getSelection?window.getSelection():document.selection.createRange().text;
return word;
}
function fillinWord(){
var element = document.getElementById("selected-words");
element.value = element.value + " " + getWord();
}
document.getElementById("text-content").addEventListener("click", fillinWord, false);
document.getElementById("text-content").addEventListener("touchstart", fillinWord, false);
</script>
'''
d = load_freq_history(user_freq_record)
if len(d) > 0:
page += '<p><b>我的生词簿</b></p>'
lst = pickle_idea2.dict2lst(d)
lst2 = []
for t in lst:
lst2.append((t[0], len(t[1])))
for x in sort_in_descending_order(lst2):
word = x[0]
freq = x[1]
if isinstance(d[word], list): # d[word] is a list of dates
if freq > 1:
page += '<p class="new-word"> <a href="%s">%s</a> (<a title="%s">%d</a>) </p>\n' % (youdao_link(word), word, '; '.join(d[word]), freq)
else:
page += '<p class="new-word"> <a href="%s">%s</a> <font color="white">(<a title="%s">%d</a>)</font> </p>\n' % (youdao_link(word), word, '; '.join(d[word]), freq)
elif isinstance(d[word], int): # d[word] is a frequency. to migrate from old format.
page += '<a href="%s">%s</a>%d\n' % (youdao_link(word), word, freq)
return page
### Sign-up, login, logout ###
@app.route("/signup", methods=['GET', 'POST'])
def signup():
if request.method == 'GET':
return render_template('signup.html')
elif request.method == 'POST':
username = request.form['username']
password = request.form['password']
available = check_username_availability(username)
if not available:
flash('用户名 %s 已经被注册。' % (username))
return render_template('signup.html')
elif len(password.strip()) < 4:
return '密码过于简单。'
else:
add_user(username, password)
verified = verify_user(username, password)
if verified:
session['logged_in'] = True
session[username] = username
session['username'] = username
return '<p>恭喜,你已成功注册, 你的用户名是 <a href="%s">%s</a>。 请记下用户名与密码。</p><p>为了提高服务质量,我们收取会员费,每天1元。 头100名用户享8折优惠。 请决定使用时间长度后,请扫描下面支付宝二维码支付。 未及时成为会员的账号,12小时后停用。如果有问题,请加开发者微信 torontohui。</p> \
<p><img src="static/donate-the-author.jpg" width="120px" alt="支付宝二维码" /></p> \
<p><a href="/%s">开始使用</a> <a href="/">返回首页</a><p/>' % (username, username, username)
else:
return '用户名密码验证失败。'
@app.route("/login", methods=['GET', 'POST'])
def login():
if request.method == 'GET':
if not session.get('logged_in'):
return render_template('login.html')
else:
return '你已登录 <a href="/%s">%s</a>。 登出点击<a href="/logout">这里</a>。' % (session['username'], session['username'])
elif request.method == 'POST':
# check database and verify user
username = request.form['username']
password = request.form['password']
verified = verify_user(username, password)
if verified:
session['logged_in'] = True
session[username] = username
session['username'] = username
user_expiry_date = get_expiry_date(username)
session['expiry_date'] = user_expiry_date
session['articleID'] = None
return redirect(url_for('userpage', username=username))
else:
return '无法通过验证。'
@app.route("/logout", methods=['GET', 'POST'])
def logout():
session['logged_in'] = False
return redirect(url_for('mainpage'))
if __name__ == '__main__':
#app.secret_key = os.urandom(16)
#app.run(debug=False, port='6000')
app.run(debug=True)
#app.run(debug=True, port='6000')
#app.run(host='0.0.0.0', debug=True, port='6000')
|