Pylint部分:

1. 补充module docstring,即文档顶部的简要说明 (我做的说明很简陋,只是为了通过pylint的检查而为之)
2. 优化了if-else-return,即删除了不必要的elif或else
3. 拆分了所有在同一行的模块引用(不同模块的引用需要放在不同行,以便理解和阅读)
4. 删除了部分没有被使用的引用(有的引用在文件A中被使用了,但import却写在了文件B,且文件B没有使用这些引用,而文件A引用了文件B,导致文件B的所有引用也被A引用,这似乎并不合理)
5. 修改了引用的顺序(os、random等系统引用应放在自定义模块引用的上方)
6. 修改了过长的代码,注释除外(每行限制在100个字符以内)
7. 优化了条件判断的判断符号:
is和is not 用于判断两个变量是否指向同一个位置
== 和 !=用于比较两个变量
8. 为每个py文件添加了"last new line",删除了多余的尾部空行

Pylint提示了但是没有进行的操作:
1. 将字符串改为f-string的格式
因为f-string需要python3.6以上的版本,为了防止出现版本问题,故不做修改
2. 将所有函数内的return内容改为一致的类型
因为原有的代码逻辑已经确定,修改return的类型会导致方法的无法使用,故不做修改
3. 将标识符的格式改为"snake_case naming style"
标识符的修改内容过于庞大,且可能出现程序不可预见的错误,在pylint中重要等级为0,故不做修改
JSLint Part:
1. 提高代码的可读性:将部分超过80个字符的代码行拆解开(由于文字注释的存在,很多语句仅注释就超过了80个字符,此类型的语句行不做修改)
2. 用===与!==代替==与!=的判断
3. 将i++改为i+=1
4. 分离for循环中的let与var到其他行中,以适应JSLint在循环语句中对var和let的偏好
5. 给三元表达式外围添加圆括号,避免出现优先级的问题
6. 将正则表达式修改为JSLint要求的多行模式

疑问:
形如:
for (let i = 0; i < list.length; i+=1) {
的语句,JSLint报错: [JSLint was unable to finish] Unexpected 'let'.
但是网上搜索到的结果是ES6是支持let的使用的,暂且不做修改。
Refactor_qianjunqi
钱骏琪 2023-05-26 22:18:11 +08:00
parent 43c719b6b2
commit ea16ea6673
13 changed files with 254 additions and 227 deletions

View File

@ -1,3 +1,6 @@
"""
This module provides functions about article
"""
from WordFreq import WordFreq from WordFreq import WordFreq
from wordfreqCMD import youdao_link, sort_in_descending_order from wordfreqCMD import youdao_link, sort_in_descending_order
from UseSqlite import InsertQuery, RecordQuery from UseSqlite import InsertQuery, RecordQuery
@ -46,7 +49,6 @@ def get_today_article(user_word_list, articleID):
d1 = load_freq_history(path_prefix + 'static/frequency/frequency.p') d1 = load_freq_history(path_prefix + 'static/frequency/frequency.p')
d2 = load_freq_history(path_prefix + 'static/words_and_tests.p') d2 = load_freq_history(path_prefix + 'static/words_and_tests.p')
d3 = get_difficulty_level(d1, d2) d3 = get_difficulty_level(d1, d2)
d = {} d = {}
d_user = load_freq_history(user_word_list) 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. user_level = user_difficulty_level(d_user, d3) # more consideration as user's behaviour is dynamic. Time factor should be considered.
@ -133,4 +135,4 @@ def get_answer_part(s):
html_code += '\n' html_code += '\n'
html_code += '<button onclick="toggle_visibility(\'answer\');">ANSWER</button>\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)) html_code += '<div id="answer" style="display:none;">%s</div>\n' % ('\n'.join(result))
return html_code return html_code

View File

@ -1,3 +1,6 @@
"""
This module provides methods for Login
"""
import hashlib import hashlib
import string import string
from datetime import datetime, timedelta from datetime import datetime, timedelta
@ -7,15 +10,17 @@ path_prefix = '/var/www/wordfreq/wordfreq/'
path_prefix = './' # comment this line in deployment path_prefix = './' # comment this line in deployment
def verify_pass(newpass,oldpass): def verify_pass(newpass,oldpass):
if(newpass==oldpass): if newpass==oldpass:
return True return True
def verify_user(username, password): def verify_user(username, password):
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db') rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
password = md5(username + password) password = md5(username + password)
rq.instructions_with_parameters("SELECT * FROM user WHERE name=:username AND password=:password", dict( rq.instructions_with_parameters(
username=username, password=password)) # the named style https://docs.python.org/3/library/sqlite3.html "SELECT * FROM user WHERE name=:username AND password=:password",
dict( # the named style https://docs.python.org/3/library/sqlite3.html
username=username,
password=password))
rq.do_with_parameters() rq.do_with_parameters()
result = rq.get_results() result = rq.get_results()
return result != [] return result != []
@ -23,12 +28,19 @@ def verify_user(username, password):
def add_user(username, password): def add_user(username, password):
start_date = datetime.now().strftime('%Y%m%d') start_date = datetime.now().strftime('%Y%m%d')
expiry_date = (datetime.now() + timedelta(days=30)).strftime('%Y%m%d') # will expire after 30 days # will expire after 30 days
expiry_date = (datetime.now() + timedelta(days=30)).strftime('%Y%m%d')
# 将用户名和密码一起加密,以免暴露不同用户的相同密码 # 将用户名和密码一起加密,以免暴露不同用户的相同密码
password = md5(username + password) password = md5(username + password)
rq = InsertQuery(path_prefix + 'static/wordfreqapp.db') rq = InsertQuery(path_prefix + 'static/wordfreqapp.db')
rq.instructions_with_parameters("INSERT INTO user VALUES (:username, :password, :start_date, :expiry_date)", dict( rq.instructions_with_parameters(
username=username, password=password, start_date=start_date, expiry_date=expiry_date)) "INSERT INTO user VALUES (:username, :password, :start_date, :expiry_date)",
dict(
username=username,
password=password,
start_date=start_date,
expiry_date=expiry_date
))
rq.do_with_parameters() rq.do_with_parameters()
@ -96,7 +108,7 @@ class UserName:
if ' ' in self.username: # a user name must not include a whitespace if ' ' in self.username: # a user name must not include a whitespace
return 'Whitespace is not allowed in the user name.' return 'Whitespace is not allowed in the user name.'
for c in self.username: # a user name must not include special characters, except non-leading periods or underscores for c in self.username: # a user name must not include special characters, except non-leading periods or underscores
if c in string.punctuation and c is not '.' and c is not '_': if c in string.punctuation and c != '.' and c != '_':
return f'{c} is not allowed in the user name.' return f'{c} is not allowed in the user name.'
if self.username in ['signup', 'login', 'logout', 'reset', 'mark', 'back', 'unfamiliar', 'familiar', 'del']: if self.username in ['signup', 'login', 'logout', 'reset', 'mark', 'back', 'unfamiliar', 'familiar', 'del']:
return 'You used a restricted word as your user name. Please come up with a better one.' return 'You used a restricted word as your user name. Please come up with a better one.'
@ -110,4 +122,3 @@ class WarningMessage:
def __str__(self): def __str__(self):
return UserName(self.s).validate() return UserName(self.s).validate()

View File

@ -2,9 +2,12 @@
# Copyright 2019 (C) Hui Lan <hui.lan@cantab.net> # Copyright 2019 (C) Hui Lan <hui.lan@cantab.net>
# Written permission must be obtained from the author for commercial uses. # Written permission must be obtained from the author for commercial uses.
########################################################################### ###########################################################################
"""
from wordfreqCMD import remove_punctuation, freq, sort_in_descending_order This module produces word frequency
"""
import string import string
from wordfreqCMD import remove_punctuation, freq, sort_in_descending_order
class WordFreq: class WordFreq:
def __init__(self, s): def __init__(self, s):
@ -17,9 +20,8 @@ class WordFreq:
if len(word) > 0 and word[0] in string.ascii_letters: if len(word) > 0 and word[0] in string.ascii_letters:
lst.append(t) lst.append(t)
return sort_in_descending_order(lst) return sort_in_descending_order(lst)
if __name__ == '__main__': if __name__ == '__main__':
f = WordFreq('BANANA; Banana, apple ORANGE Banana banana.') f = WordFreq('BANANA; Banana, apple ORANGE Banana banana.')
print(f.get_freq()) print(f.get_freq())

View File

@ -1,54 +1,54 @@
"""
This module provides services about account.
"""
from flask import * from flask import *
from Login import check_username_availability, verify_user, add_user, get_expiry_date, change_password, WarningMessage from Login import check_username_availability, \
verify_user, add_user, get_expiry_date, change_password, WarningMessage
# 初始化蓝图 # 初始化蓝图
accountService = Blueprint("accountService", __name__) accountService = Blueprint("accountService", __name__)
### Sign-up, login, logout ###
# Sign-up, login, logout
@accountService.route("/signup", methods=['GET', 'POST']) @accountService.route("/signup", methods=['GET', 'POST'])
def signup(): def signup():
''' '''
注册 注册
:return: 根据注册是否成功返回不同界面 :return: 根据注册是否成功返回不同界面
''' '''
# GET方法直接返回注册页面
if request.method == 'GET': if request.method == 'GET':
# GET方法直接返回注册页面
return render_template('signup.html') return render_template('signup.html')
elif request.method == 'POST': if request.method == 'POST':
# POST方法需判断是否注册成功再根据结果返回不同的内容 # POST方法需判断是否注册成功再根据结果返回不同的内容
username = escape(request.form['username']) username = escape(request.form['username'])
password = escape(request.form['password']) password = escape(request.form['password'])
password2 = escape(request.form['password2']) password2 = escape(request.form['password2'])
#! 添加如下代码为了过滤注册时的非法字符 #! 添加如下代码为了过滤注册时的非法字符
warn = WarningMessage(username) warn = WarningMessage(username)
if str(warn) != 'OK': if str(warn) != 'OK':
return str(warn) return str(warn)
available = check_username_availability(username) available = check_username_availability(username)
if not available: # 用户名不可用 if not available: # 用户名不可用
flash('用户名 %s 已经被注册。' % (username)) flash('用户名 %s 已经被注册。' % (username))
return render_template('signup.html') return render_template('signup.html')
elif len(password.strip()) < 4: # 密码过短 if len(password.strip()) < 4: # 密码过短
return '密码过于简单。' return '密码过于简单。'
elif password != password2: if password != password2:
return '确认密码与输入密码不一致!' return '确认密码与输入密码不一致!'
else: # 添加账户信息 add_user(username, password)# 添加账户信息
add_user(username, password) verified = verify_user(username, password)
verified = verify_user(username, password) if verified:
if verified: # 写入session
# 写入session session['logged_in'] = True
session['logged_in'] = True session[username] = username
session[username] = username session['username'] = username
session['username'] = username session['expiry_date'] = get_expiry_date(username)
session['expiry_date'] = get_expiry_date(username) session['articleID'] = None
session['articleID'] = None return '<p>恭喜,你已成功注册, 你的用户名是 <a href="%s">%s</a>。</p>\
return '<p>恭喜,你已成功注册, 你的用户名是 <a href="%s">%s</a>。</p>\ <p><a href="/%s">开始使用</a> <a href="/">返回首页</a><p/>' % (username, username, username)
<p><a href="/%s">开始使用</a> <a href="/">返回首页</a><p/>' % (username, username, username) return '用户名密码验证失败。'
else:
return '用户名密码验证失败。'
@accountService.route("/login", methods=['GET', 'POST']) @accountService.route("/login", methods=['GET', 'POST'])
@ -62,11 +62,10 @@ def login():
if not session.get('logged_in'): if not session.get('logged_in'):
# 未登录,返回登录页面 # 未登录,返回登录页面
return render_template('login.html') return render_template('login.html')
else: # 已登录,提示信息并显示登出按钮
# 已登录,提示信息并显示登出按钮 return '你已登录 <a href="/%s">%s</a>。 登出点击<a href="/logout">这里</a>。' % (
return '你已登录 <a href="/%s">%s</a>。 登出点击<a href="/logout">这里</a>。' % (
session['username'], session['username']) session['username'], session['username'])
elif request.method == 'POST': if request.method == 'POST':
# POST方法用于判断登录是否成功 # POST方法用于判断登录是否成功
# check database and verify user # check database and verify user
username = escape(request.form['username']) username = escape(request.form['username'])
@ -81,8 +80,7 @@ def login():
session['expiry_date'] = user_expiry_date session['expiry_date'] = user_expiry_date
session['articleID'] = None session['articleID'] = None
return redirect(url_for('user_bp.userpage', username=username)) return redirect(url_for('user_bp.userpage', username=username))
else: return '无法通过验证。'
return '无法通过验证。'
@accountService.route("/logout", methods=['GET', 'POST']) @accountService.route("/logout", methods=['GET', 'POST'])
@ -111,21 +109,18 @@ def reset():
if request.method == 'GET': if request.method == 'GET':
# GET请求返回修改密码页面 # GET请求返回修改密码页面
return render_template('reset.html', username=session['username'], state='wait') return render_template('reset.html', username=session['username'], state='wait')
else: # POST请求用于提交修改后信息
# POST请求用于提交修改后信息 old_password = escape(request.form['old-password'])
old_password = escape(request.form['old-password']) new_password = escape(request.form['new-password'])
new_password = escape(request.form['new-password']) re_new_password = escape(request.form['re-new-password']) # 确认新密码
if re_new_password != new_password: #验证新密码两次输入是否相同
re_new_password = escape(request.form['re-new-password']) # 确认新密码 return '新密码不匹配,请重新输入'
if re_new_password != new_password: #验证新密码两次输入是否相同 if len(new_password) < 4: #验证新密码长度,原则参照注册模块
return '新密码不匹配,请重新输入' return '密码过于简单。(密码长度至少4位)'
if len(new_password) < 4: #验证新密码长度,原则参照注册模块 flag = change_password(username, old_password, new_password) # flag表示是否修改成功
return '密码过于简单。(密码长度至少4位)' if flag:
session['logged_in'] = False
flag = change_password(username, old_password, new_password) # flag表示是否修改成功 return \
if flag:
session['logged_in'] = False
return \
''' '''
<script> <script>
alert('密码修改成功,请重新登录。'); alert('密码修改成功,请重新登录。');
@ -134,8 +129,7 @@ window.location.href="/login";
''' '''
else:
return \
''' '''
<script> <script>
alert('密码修改失败'); alert('密码修改失败');

View File

@ -3,8 +3,10 @@
# Written permission must be obtained from the author for commercial uses. # Written permission must be obtained from the author for commercial uses.
########################################################################### ###########################################################################
# Purpose: compute difficulty level of a English text """
This module provides functions for calculating user words difficulty
Purpose: compute difficulty level of a English text
"""
import pickle import pickle
import math import math
from wordfreqCMD import remove_punctuation, freq, sort_in_descending_order, sort_in_ascending_order from wordfreqCMD import remove_punctuation, freq, sort_in_descending_order, sort_in_ascending_order
@ -21,37 +23,36 @@ def difficulty_level_from_frequency(word, d):
level = 1 level = 1
if not word in d: if not word in d:
return level return level
if 'what' in d:
ratio = (d['what']+1)/(d[word]+1) # what is a frequent word
level = math.log( max(ratio, 1), 2)
level = min(level, 8) if 'what' in d:
ratio = (d['what'] + 1) / (d[word] + 1) # what is a frequent word
level = math.log(max(ratio, 1), 2)
level = min(level, 8)
return level return level
def get_difficulty_level(d1, d2): def get_difficulty_level(d1, d2):
d = {} d = {}
L = list(d1.keys()) # in d1, we have freuqence for each word L = list(d1.keys()) # in d1, we have freuqence for each word
L2 = list(d2.keys()) # in d2, we have test types (e.g., CET4,CET6,BBC) for each word L2 = list(d2.keys()) # in d2, we have test types (e.g., CET4,CET6,BBC) for each word
L.extend(L2) L.extend(L2)
L3 = list(set(L)) # L3 contains all words L3 = list(set(L)) # L3 contains all words
for k in L3: for k in L3:
if k in d2: if k in d2:
if 'CET4' in d2[k]: if 'CET4' in d2[k]:
d[k] = 4 # CET4 word has level 4 d[k] = 4 # CET4 word has level 4
elif 'CET6' in d2[k]: elif 'CET6' in d2[k]:
d[k] = 6 d[k] = 6
elif 'BBC' in d2[k]: elif 'BBC' in d2[k]:
d[k] = 8 d[k] = 8
if k in d1: # BBC could contain easy words that are not in CET4 or CET6. So 4 is not reasonable. Recompute difficulty level. if k in d1: # BBC could contain easy words that are not in CET4 or CET6. So 4 is not reasonable. Recompute difficulty level.
d[k] = min(difficulty_level_from_frequency(k, d1), d[k]) d[k] = min(difficulty_level_from_frequency(k, d1), d[k])
elif k in d1: elif k in d1:
d[k] = difficulty_level_from_frequency(k, d1) d[k] = difficulty_level_from_frequency(k, d1)
return d return d
def revert_dict(d): def revert_dict(d):
''' '''
@ -62,12 +63,13 @@ def revert_dict(d):
for k in d: for k in d:
if type(d[k]) is list: # d[k] is a list of dates. if type(d[k]) is list: # d[k] is a list of dates.
lst = d[k] lst = d[k]
elif type(d[k]) is int: # for backward compatibility. d was sth like {'word':1}. The value d[k] is not a list of dates, but a number representing how frequent this word had been added to the new word book. elif type(d[
k]) is int: # for backward compatibility. d was sth like {'word':1}. The value d[k] is not a list of dates, but a number representing how frequent this word had been added to the new word book.
freq = d[k] freq = d[k]
lst = freq*['2021082019'] # why choose this date? No particular reasons. I fix the bug in this date. lst = freq * ['2021082019'] # why choose this date? No particular reasons. I fix the bug in this date.
for time_info in lst: for time_info in lst:
date = time_info[:10] # until hour date = time_info[:10] # until hour
if not date in d2: if not date in d2:
d2[date] = [k] d2[date] = [k]
else: else:
@ -76,42 +78,43 @@ def revert_dict(d):
def user_difficulty_level(d_user, d): def user_difficulty_level(d_user, d):
d_user2 = revert_dict(d_user) # key is date, and value is a list of words added in that date d_user2 = revert_dict(d_user) # key is date, and value is a list of words added in that date
count = 0 count = 0
geometric = 1 geometric = 1
for date in sorted(d_user2.keys(), reverse=True): # most recently added words are more important while determining user's level for date in sorted(d_user2.keys(),
lst = d_user2[date] # a list of words reverse=True): # most recently added words are more important while determining user's level
lst2 = [] # a list of tuples, (word, difficulty level) lst = d_user2[date] # a list of words
for word in lst: lst2 = [] # a list of tuples, (word, difficulty level)
for word in lst:
if word in d: if word in d:
lst2.append((word, d[word])) lst2.append((word, d[word]))
lst3 = sort_in_ascending_order(lst2) # easiest tuple first lst3 = sort_in_ascending_order(lst2) # easiest tuple first
#print(lst3) # print(lst3)
for t in lst3: for t in lst3:
word = t[0] word = t[0]
hard = t[1] hard = t[1]
#print('WORD %s HARD %4.2f' % (word, hard)) # print('WORD %s HARD %4.2f' % (word, hard))
geometric = geometric * (hard) geometric = geometric * (hard)
count += 1 count += 1
if count >= 10: if count >= 10:
return geometric**(1/count) return geometric ** (1 / count)
return geometric**(1/max(count,1)) return geometric ** (1 / max(count, 1))
def text_difficulty_level(s, d): def text_difficulty_level(s, d):
s = remove_punctuation(s) s = remove_punctuation(s)
L = freq(s) L = freq(s)
lst = [] # a list of tuples, each tuple being (word, difficulty level) lst = [] # a list of tuples, each tuple being (word, difficulty level)
for x in L: for x in L:
word = x[0] word = x[0]
if word in d: if word in d:
lst.append((word, d[word])) lst.append((word, d[word]))
lst2 = sort_in_descending_order(lst) # most difficult words on top lst2 = sort_in_descending_order(lst) # most difficult words on top
#print(lst2) # print(lst2)
count = 0 count = 0
geometric = 1 geometric = 1
for t in lst2: for t in lst2:
@ -119,22 +122,18 @@ def text_difficulty_level(s, d):
hard = t[1] hard = t[1]
geometric = geometric * (hard) geometric = geometric * (hard)
count += 1 count += 1
if count >= 20: # we look for n most difficult words if count >= 20: # we look for n most difficult words
return geometric**(1/count) return geometric ** (1 / count)
return geometric**(1/max(count,1))
return geometric ** (1 / max(count, 1))
if __name__ == '__main__': if __name__ == '__main__':
d1 = load_record('frequency.p') d1 = load_record('frequency.p')
#print(d1) # print(d1)
d2 = load_record('words_and_tests.p') d2 = load_record('words_and_tests.p')
#print(d2) # print(d2)
d3 = get_difficulty_level(d1, d2) d3 = get_difficulty_level(d1, d2)
@ -197,7 +196,6 @@ Amidst the aftermath of this shocking referendum vote, there is great uncertaint
''' '''
s = ''' s = '''
British Prime Minister Boris Johnson walks towards a voting station during the Brexit referendum in Britain, June 23, 2016. (Photo: EPA-EFE) British Prime Minister Boris Johnson walks towards a voting station during the Brexit referendum in Britain, June 23, 2016. (Photo: EPA-EFE)
@ -218,7 +216,6 @@ The prime minister was forced to ask for an extension to Britain's EU departure
Johnson has repeatedly pledged to finalize the first stage, a transition deal, of Britain's EU divorce battle by Oct. 31. A second stage will involve negotiating its future relationship with the EU on trade, security and other salient issues. Johnson has repeatedly pledged to finalize the first stage, a transition deal, of Britain's EU divorce battle by Oct. 31. A second stage will involve negotiating its future relationship with the EU on trade, security and other salient issues.
''' '''
s = ''' s = '''
Thank you very much. We have a Cabinet meeting. Well have a few questions after grace. And, if you would, Ben, please do the honors. Thank you very much. We have a Cabinet meeting. Well have a few questions after grace. And, if you would, Ben, please do the honors.
@ -233,17 +230,9 @@ We need — for our farmers, our manufacturers, for, frankly, unions and non-uni
''' '''
# f = open('bbc-fulltext/bbc/entertainment/001.txt')
f = open('wordlist.txt',encoding="utf-8")
#f = open('bbc-fulltext/bbc/entertainment/001.txt')
f = open('wordlist.txt')
s = f.read() s = f.read()
f.close() f.close()
print(text_difficulty_level(s, d3)) print(text_difficulty_level(s, d3))

View File

@ -52,8 +52,7 @@ def appears_in_test(word, d):
''' '''
if not word in d: if not word in d:
return '' return ''
else: return ','.join(d[word])
return ','.join(d[word])
@app.route("/mark", methods=['GET', 'POST']) @app.route("/mark", methods=['GET', 'POST'])
@ -71,8 +70,8 @@ def mark_word():
d = pickle_idea.merge_frequency(lst, lst_history) d = pickle_idea.merge_frequency(lst, lst_history)
pickle_idea.save_frequency_to_pickle(d, path_prefix + 'static/frequency/frequency.p') pickle_idea.save_frequency_to_pickle(d, path_prefix + 'static/frequency/frequency.p')
return redirect(url_for('mainpage')) return redirect(url_for('mainpage'))
else: # 不回应GET请求 # 不回应GET请求
return 'Under construction' return 'Under construction'
@app.route("/", methods=['GET', 'POST']) @app.route("/", methods=['GET', 'POST'])
@ -92,7 +91,7 @@ def mainpage():
pickle_idea.save_frequency_to_pickle(d, path_prefix + 'static/frequency/frequency.p') pickle_idea.save_frequency_to_pickle(d, path_prefix + 'static/frequency/frequency.p')
return render_template('mainpage_post.html', lst=lst, yml=Yaml.yml) return render_template('mainpage_post.html', lst=lst, yml=Yaml.yml)
elif request.method == 'GET': # when we load a html page if request.method == 'GET': # when we load a html page
random_ads = get_random_ads() random_ads = get_random_ads()
number_of_essays = total_number_of_essays() number_of_essays = total_number_of_essays()
d = load_freq_history(path_prefix + 'static/frequency/frequency.p') d = load_freq_history(path_prefix + 'static/frequency/frequency.p')
@ -102,7 +101,6 @@ def mainpage():
d_len=d_len, lst=lst, yml=Yaml.yml) d_len=d_len, lst=lst, yml=Yaml.yml)
if __name__ == '__main__': if __name__ == '__main__':
''' '''
运行程序 运行程序

View File

@ -3,16 +3,17 @@
# Written permission must be obtained from the author for commercial uses. # Written permission must be obtained from the author for commercial uses.
########################################################################### ###########################################################################
# Purpose: dictionary & pickle as a simple means of database. """
# Task: incorporate the functions into wordfreqCMD.py such that it will also show cumulative frequency. Purpose: dictionary & pickle as a simple means of database.
Task: incorporate the functions into wordfreqCMD.py such that it will also show cumulative frequency.
"""
import pickle import pickle
from datetime import datetime from datetime import datetime
def lst2dict(lst, d): def lst2dict(lst, d):
''' '''
Store the information in list lst to dictionary d. Store the information in list lst to dictionary d.
Note: nothing is returned. Note: nothing is returned.
''' '''
@ -20,14 +21,14 @@ def lst2dict(lst, d):
word = x[0] word = x[0]
freq = x[1] freq = x[1]
if not word in d: if not word in d:
d[word] = freq d[word] = freq
else: else:
d[word] += freq d[word] += freq
def dict2lst(d): def dict2lst(d):
return list(d.items()) # a list of (key, value) pairs return list(d.items()) # a list of (key, value) pairs
def merge_frequency(lst1, lst2): def merge_frequency(lst1, lst2):
d = {} d = {}

View File

@ -43,7 +43,7 @@ def dict2lst(d):
for k in d: for k in d:
lst.append((k, [datetime.now().strftime('%Y%m%d%H%M')])) lst.append((k, [datetime.now().strftime('%Y%m%d%H%M')]))
return lst return lst
elif isinstance(d[keys[0]], list): if isinstance(d[keys[0]], list):
return list(d.items()) # a list of (key, value) pairs return list(d.items()) # a list of (key, value) pairs
return [] return []

View File

@ -11,34 +11,39 @@ function getWord() {
function fillInWord() { function fillInWord() {
let word = getWord(); let word = getWord();
if (isRead) read(word); if (isRead) {read(word);}
if (!isChoose) return; if (!isChoose) {return;}
const element = document.getElementById("selected-words"); const element = document.getElementById("selected-words");
element.value = element.value + " " + word; element.value = element.value + " " + word;
} }
document.getElementById("text-content").addEventListener("click", fillInWord, false); document
.getElementById("text-content")
.addEventListener("click", fillInWord, false);
function makeUtterance(str, rate) { function makeUtterance(str, rate) {
let msg = new SpeechSynthesisUtterance(str); let msg = new SpeechSynthesisUtterance(str);
msg.rate = rate; msg.rate = rate;
msg.lang = "en-US"; // TODO: add language options menu msg.lang = "en-US"; // TODO: add language options menu
msg.onboundary = ev => { msg.onboundary = function(ev) {
if (ev.name == "word") { if (ev.name === "word") {
current_position = ev.charIndex; current_position = ev.charIndex;
} }
} };
return msg; return msg;
} }
const sliderValue = document.getElementById("rangeValue"); // 显示值 const sliderValue = document.getElementById("rangeValue"); // 显示值
const inputSlider = document.getElementById("rangeComponent"); // 滑块元素 const inputSlider = document.getElementById("rangeComponent"); // 滑块元素
inputSlider.oninput = () => { inputSlider.oninput = function(){
let value = inputSlider.value; // 获取滑块的值 let value = inputSlider.value; // 获取滑块的值
sliderValue.textContent = value + '×'; sliderValue.textContent = value + "×";
if (!reader.speaking) return; if (!reader.speaking) {return;}
reader.cancel(); reader.cancel();
let msg = makeUtterance(to_speak.substring(original_position + current_position), value); let msg = makeUtterance(
to_speak.substring(original_position + current_position),
value
);
original_position = original_position + current_position; original_position = original_position + current_position;
current_position = 0; current_position = 0;
reader.speak(msg); reader.speak(msg);

View File

@ -2,44 +2,60 @@ let isHighlight = true;
function cancelBtnHandler() { function cancelBtnHandler() {
cancelHighlighting(); cancelHighlighting();
document.getElementById("text-content").removeEventListener("click", fillInWord, false); document
document.getElementById("text-content").removeEventListener("touchstart", fillInWord, false); .getElementById("text-content")
document.getElementById("text-content").addEventListener("click", fillInWord2, false); .removeEventListener("click", fillInWord, false);
document.getElementById("text-content").addEventListener("touchstart", fillInWord2, false); document.getElementById("text-content")
.removeEventListener("touchstart", fillInWord, false);
document.getElementById("text-content")
.addEventListener("click", fillInWord2, false);
document.getElementById("text-content")
.addEventListener("touchstart", fillInWord2, false);
} }
function showBtnHandler() { function showBtnHandler() {
document.getElementById("text-content").removeEventListener("click", fillInWord2, false); document.getElementById("text-content")
document.getElementById("text-content").removeEventListener("touchstart", fillInWord2, false); .removeEventListener("click", fillInWord2, false);
document.getElementById("text-content").addEventListener("click", fillInWord, false); document.getElementById("text-content")
document.getElementById("text-content").addEventListener("touchstart", fillInWord, false); .removeEventListener("touchstart", fillInWord2, false);
document.getElementById("text-content")
.addEventListener("click", fillInWord, false);
document.getElementById("text-content")
.addEventListener("touchstart", fillInWord, false);
highLight(); highLight();
} }
function getWord() { function getWord() {
return window.getSelection ? window.getSelection() : document.selection.createRange().text; return (window.getSelection ?
window.getSelection() :
document.selection.createRange().text);
} }
function highLight() { function highLight() {
if (!isHighlight) return; if (!isHighlight) {return;}
let articleContent = document.getElementById("article").innerText; //将原来的.innerText改为.innerHtml使用innerText会把原文章中所包含的<br>标签去除,导致处理后的文章内容失去了原来的格式 let articleContent = document.getElementById("article").innerText; //将原来的.innerText改为.innerHtml使用innerText会把原文章中所包含的<br>标签去除,导致处理后的文章内容失去了原来的格式
let pickedWords = document.getElementById("selected-words"); // words picked to the text area let pickedWords = document.getElementById("selected-words"); // words picked to the text area
let dictionaryWords = document.getElementById("selected-words2"); // words appearing in the user's new words list let dictionaryWords = document.getElementById("selected-words2"); // words appearing in the user's new words list
let allWords = ""; //初始化allWords的值避免进入判断后编译器认为allWords未初始化的问题 let allWords = ""; //初始化allWords的值避免进入判断后编译器认为allWords未初始化的问题
if(dictionaryWords != null){//增加一个判断检查生词本里面是否为空如果为空allWords只添加选中的单词 if(dictionaryWords !== null){//增加一个判断检查生词本里面是否为空如果为空allWords只添加选中的单词
allWords = pickedWords.value + " " + dictionaryWords.value; allWords = pickedWords.value + " " + dictionaryWords.value;
} }
else{ else{
allWords = pickedWords.value + " "; allWords = pickedWords.value + " ";
} }
const list = allWords.split(" ");//将所有的生词放入一个list中用于后续处理 const list = allWords.split(" ");//将所有的生词放入一个list中用于后续处理
for (let i = 0; i < list.length; ++i) { for (let i = 0; i < list.length; i+=1) {
list[i] = list[i].replace(/(^\s*)|(\s*$)/g, ""); //消除单词两边的空字符 list[i] = list[i].replace(/(^\s*)|(\s*$)/gm, ""); //消除单词两边的空字符
list[i] = list[i].replace('|', ""); list[i] = list[i].replace("|", "");
list[i] = list[i].replace('?', ""); list[i] = list[i].replace("?", "");
if (list[i] !== "" && "<mark>".indexOf(list[i]) === -1 && "</mark>".indexOf(list[i]) === -1) { if (list[i] !== "" &&
//将文章中所有出现该单词word的地方改为" <mark>" + word + "<mark> "。 正则表达式RegExp()中,"\\s"代表单词前后必须要有空格,以防止只对单词中的部分字符高亮的情况出现。 "<mark>".indexOf(list[i]) === -1 &&
articleContent = articleContent.replace(new RegExp("\\s"+list[i]+"\\s", "g"), " <mark>" + list[i] + "</mark> "); "</mark>".indexOf(list[i]) === -1
) {
//将文章中所有出现该单词word的地方改为" <mark>" + word + "<mark> "。 正则表达式RegExp()中,"\\s"代表单词前后必须要有空格,以防止只对单词中的部分字符高亮的情况出现。
articleContent = articleContent.replace(
new RegExp("\\s"+list[i]+"\\s", "g"),
" <mark>" + list[i] + "</mark> ");
} }
} }
document.getElementById("article").innerHTML = articleContent; document.getElementById("article").innerHTML = articleContent;
@ -48,23 +64,29 @@ function highLight() {
function cancelHighlighting() { function cancelHighlighting() {
let articleContent = document.getElementById("article").innerText;//将原来的.innerText改为.innerHtml原因同上 let articleContent = document.getElementById("article").innerText;//将原来的.innerText改为.innerHtml原因同上
let pickedWords = document.getElementById("selected-words"); let pickedWords = document.getElementById("selected-words");
const dictionaryWords = document.getElementById("selected-words2"); const dictionaryWords = document.getElementById("selected-words2");
const list = pickedWords.value.split(" "); const list = pickedWords.value.split(" ");
if (pickedWords != null) { if (pickedWords !== null) {
for (let i = 0; i < list.length; ++i) { for (let i = 0; i < list.length; i+=1) {
list[i] = list[i].replace(/(^\s*)|(\s*$)/g, ""); list[i] = list[i].replace(/(^\s*)|(\s*$)/gm, "");
if (list[i] !== "") { //原来判断的代码中替换的内容为“list[i]”这个字符串这明显是错误的我们需要替换的是list[i]里的内容 if (list[i] !== "") { //原来判断的代码中替换的内容为“list[i]”这个字符串这明显是错误的我们需要替换的是list[i]里的内容
articleContent = articleContent.replace(new RegExp("<mark>"+list[i]+"</mark>", "g"), list[i]); articleContent = articleContent.replace(
new RegExp("<mark>"+list[i]+"</mark>", "g"),
list[i]
);
} }
} }
} }
if (dictionaryWords != null) { if (dictionaryWords !== null) {
let list2 = pickedWords.value.split(" "); let list2 = pickedWords.value.split(" ");
for (let i = 0; i < list2.length; ++i) { for (let i = 0; i < list2.length; i+=1) {
list2 = dictionaryWords.value.split(" "); list2 = dictionaryWords.value.split(" ");
list2[i] = list2[i].replace(/(^\s*)|(\s*$)/g, ""); list2[i] = list2[i].replace(/(^\s*)|(\s*$)/gm, "");
if (list2[i] !== "") { //原来代码中替换的内容为“list[i]”这个字符串这明显是错误的我们需要替换的是list[i]里的内容 if (list2[i] !== "") { //原来代码中替换的内容为“list[i]”这个字符串这明显是错误的我们需要替换的是list[i]里的内容
articleContent = articleContent.replace(new RegExp("<mark>"+list2[i]+"</mark>", "g"), list2[i]); articleContent = articleContent.replace(
new RegExp("<mark>"+list2[i]+"</mark>", "g"),
list2[i]
);
} }
} }
} }

View File

@ -3,17 +3,17 @@ function familiar(theWord) {
let word = $("#word_" + theWord).text(); let word = $("#word_" + theWord).text();
let freq = $("#freq_" + theWord).text(); let freq = $("#freq_" + theWord).text();
$.ajax({ $.ajax({
type:"GET",
url:"/" + username + "/" + word + "/familiar",
success:function(response){ success:function(response){
let new_freq = freq - 1; let new_freq = freq - 1;
const allow_move = document.getElementById("move_dynamiclly").checked; const allow_move = document
.getElementById("move_dynamiclly")
.checked;
if (allow_move) { if (allow_move) {
if (new_freq <= 0) { if (new_freq <= 0) {
removeWord(theWord); removeWord(theWord);
} else { } else {
renderWord({ word: theWord, freq: new_freq }); renderWord({ freq: new_freq,word: theWord });
} }
} else { } else {
if(new_freq <1) { if(new_freq <1) {
@ -22,7 +22,9 @@ function familiar(theWord) {
$("#freq_" + theWord).text(new_freq); $("#freq_" + theWord).text(new_freq);
} }
} }
} },
type:"GET",
url:"/" + username + "/" + word + "/familiar"
}); });
} }
@ -31,38 +33,42 @@ function unfamiliar(theWord) {
let word = $("#word_" + theWord).text(); let word = $("#word_" + theWord).text();
let freq = $("#freq_" + theWord).text(); let freq = $("#freq_" + theWord).text();
$.ajax({ $.ajax({
type:"GET",
url:"/" + username + "/" + word + "/unfamiliar",
success:function(response){ success:function(response){
let new_freq = parseInt(freq) + 1; let new_freq = parseInt(freq) + 1;
const allow_move = document.getElementById("move_dynamiclly").checked; const allow_move = document
.getElementById("move_dynamiclly")
.checked;
if (allow_move) { if (allow_move) {
renderWord({ word: theWord, freq: new_freq }); renderWord({ freq: new_freq,word: theWord });
} else { } else {
$("#freq_" + theWord).text(new_freq); $("#freq_" + theWord).text(new_freq);
} }
} },
type:"GET",
url:"/" + username + "/" + word + "/unfamiliar"
}); });
} }
function delete_word(theWord) { function delete_word(theWord) {
let username = $("#username").text(); let username = $("#username").text();
let word = theWord.replace('&amp;', '&'); let word = theWord.replace("&amp;", "&");
$.ajax({ $.ajax({
type:"GET",
url:"/" + username + "/" + word + "/del",
success:function(response){ success:function(response){
const allow_move = document.getElementById("move_dynamiclly").checked; const allow_move = document
.getElementById("move_dynamiclly")
.checked;
if (allow_move) { if (allow_move) {
removeWord(theWord); removeWord(theWord);
} else { } else {
$("#p_" + theWord).remove(); $("#p_" + theWord).remove();
} }
} },
type:"GET",
url:"/" + username + "/" + word + "/del"
}); });
} }
/* /*
* interface Word { * interface Word {
* word: string, * word: string,
* freq: number * freq: number
@ -75,11 +81,12 @@ function delete_word(theWord) {
function parseWord(element) { function parseWord(element) {
const word = element const word = element
.querySelector("a.btn.btn-light[role=button]") // 获取当前词频元素的词汇元素 .querySelector("a.btn.btn-light[role=button]") // 获取当前词频元素的词汇元素
.innerText // 获取词汇值; .innerText; // 获取词汇值;
const freq = Number.parseInt(element.querySelector(`#freq_${word}`).innerText); // 获取词汇的数量 const freq = Number// 获取词汇的数量
.parseInt(element.querySelector(`#freq_${word}`).innerText);
return { return {
word, freq,
freq word
}; };
} }
@ -104,7 +111,7 @@ function wordTemplate(word) {
*/ */
function removeWord(word) { function removeWord(word) {
// 根据词频信息删除元素 // 根据词频信息删除元素
word = word.replace('&amp;', '&'); word = word.replace("&amp;", "&");
const element_to_remove = document.getElementById(`p_${word}`); const element_to_remove = document.getElementById(`p_${word}`);
if (element_to_remove != null) { if (element_to_remove != null) {
element_to_remove.remove(); element_to_remove.remove();
@ -134,7 +141,11 @@ function renderWord(word) {
// 让发生变化的元素抖动 // 让发生变化的元素抖动
new_element.classList.add("shaking"); new_element.classList.add("shaking");
// 移动到该元素 // 移动到该元素
new_element.scrollIntoView({behavior: "smooth", block: "center", inline: "nearest"}); new_element.scrollIntoView({
behavior: "smooth",
block: "center",
inline: "nearest"
});
// 抖动完毕后删除抖动类 // 抖动完毕后删除抖动类
setTimeout(() => { setTimeout(() => {
new_element.classList.remove("shaking"); new_element.classList.remove("shaking");
@ -145,7 +156,7 @@ function renderWord(word) {
* 从string中创建一个HTML元素并返回 * 从string中创建一个HTML元素并返回
*/ */
function elementFromString(string) { function elementFromString(string) {
const d = document.createElement('div'); const d = document.createElement("div");
d.innerHTML = string; d.innerHTML = string;
return d.children.item(0); return d.children.item(0);
} }

View File

@ -1,5 +1,7 @@
"""
This module provides methods for user_services
"""
from datetime import datetime from datetime import datetime
from flask import * from flask import *
# from app import Yaml # from app import Yaml
@ -36,6 +38,7 @@ def user_reset(username):
else: else:
return 'Under construction' return 'Under construction'
@userService.route("/<username>/back", methods=['GET']) @userService.route("/<username>/back", methods=['GET'])
def user_back(username): def user_back(username):
''' '''
@ -48,7 +51,6 @@ def user_back(username):
return redirect(url_for('user_bp.userpage', username=username)) return redirect(url_for('user_bp.userpage', username=username))
@userService.route("/<username>/<word>/unfamiliar", methods=['GET', 'POST']) @userService.route("/<username>/<word>/unfamiliar", methods=['GET', 'POST'])
def unfamiliar(username, word): def unfamiliar(username, word):
''' '''
@ -141,9 +143,6 @@ def userpage(username):
words=words) words=words)
@userService.route("/<username>/mark", methods=['GET', 'POST']) @userService.route("/<username>/mark", methods=['GET', 'POST'])
def user_mark_word(username): def user_mark_word(username):
''' '''

View File

@ -2,13 +2,16 @@
# Copyright 2019 (C) Hui Lan <hui.lan@cantab.net> # Copyright 2019 (C) Hui Lan <hui.lan@cantab.net>
# Written permission must be obtained from the author for commercial uses. # Written permission must be obtained from the author for commercial uses.
########################################################################### ###########################################################################
"""
This Module produces word frequency
"""
import collections import collections
import string import string
import operator import operator
import os, sys # 引入模块sys因为我要用里面的sys.argv列表中的信息来读取命令行参数。 import os, sys # 引入模块sys因为我要用里面的sys.argv列表中的信息来读取命令行参数。
import pickle_idea import pickle_idea
def freq(fruit): def freq(fruit):
''' '''
功能 把字符串转成列表 目的是得到每个单词的频率 功能 把字符串转成列表 目的是得到每个单词的频率
@ -18,40 +21,39 @@ def freq(fruit):
''' '''
result = [] result = []
fruit = fruit.lower() # 字母转小写
fruit = fruit.lower() # 字母转小写
flst = fruit.split() # 字符串转成list flst = fruit.split() # 字符串转成list
c = collections.Counter(flst) c = collections.Counter(flst)
result = c.most_common() result = c.most_common()
return result return result
def youdao_link(s): # 有道链接 def youdao_link(s): # 有道链接
link = 'http://youdao.com/w/eng/' + s + '/#keyfrom=dict2.index'# 网址 link = 'http://youdao.com/w/eng/' + s + '/#keyfrom=dict2.index' # 网址
return link return link
def file2str(fname):#文件转字符 def file2str(fname): # 文件转字符
f = open(fname) #打开 f = open(fname) # 打开
s = f.read() #读取 s = f.read() # 读取
f.close() #关闭 f.close() # 关闭
return s return s
def remove_punctuation(s): # 这里是s是形参 (parameter)。函数被调用时才给s赋值。 def remove_punctuation(s): # 这里是s是形参 (parameter)。函数被调用时才给s赋值。
special_characters = '\_©~<=>+-/[]*&$%^@.,?!:;#()"“”—‘’{}|' # 把里面的字符都去掉 special_characters = '\_©~<=>+-/[]*&$%^@.,?!:;#()"“”—‘’{}|' # 把里面的字符都去掉
for c in special_characters: for c in special_characters:
s = s.replace(c, ' ') # 防止出现把 apple,apple 移掉逗号后变成 appleapple 情况 s = s.replace(c, ' ') # 防止出现把 apple,apple 移掉逗号后变成 appleapple 情况
s = s.replace('--', ' ') s = s.replace('--', ' ')
s = s.strip() # 去除前后的空格 s = s.strip() # 去除前后的空格
if '\'' in s: if '\'' in s:
n = len(s) n = len(s)
t = '' # 用来收集我需要保留的字符 t = '' # 用来收集我需要保留的字符
for i in range(n): # 只有单引号前后都有英文字符,才保留 for i in range(n): # 只有单引号前后都有英文字符,才保留
if s[i] == '\'': if s[i] == '\'':
i_is_ok = i - 1 >= 0 and i + 1 < n i_is_ok = i - 1 >= 0 and i + 1 < n
if i_is_ok and s[i-1] in string.ascii_letters and s[i+1] in string.ascii_letters: if i_is_ok and s[i - 1] in string.ascii_letters and s[i + 1] in string.ascii_letters:
t += s[i] t += s[i]
else: else:
t += s[i] t += s[i]
@ -60,12 +62,12 @@ def remove_punctuation(s): # 这里是s是形参 (parameter)。函数被调用
return s return s
def sort_in_descending_order(lst):# 单词按频率降序排列 def sort_in_descending_order(lst): # 单词按频率降序排列
lst2 = sorted(lst, reverse=True, key=lambda x: (x[1], x[0])) lst2 = sorted(lst, reverse=True, key=lambda x: (x[1], x[0]))
return lst2 return lst2
def sort_in_ascending_order(lst):# 单词按频率降序排列 def sort_in_ascending_order(lst): # 单词按频率降序排列
lst2 = sorted(lst, reverse=False, key=lambda x: (x[1], x[0])) lst2 = sorted(lst, reverse=False, key=lambda x: (x[1], x[0]))
return lst2 return lst2
@ -85,39 +87,30 @@ def make_html_page(lst, fname):
f.close() f.close()
## main程序入口 # main程序入口
if __name__ == '__main__': if __name__ == '__main__':
num = len(sys.argv) num = len(sys.argv)
if num == 1: # 从键盘读入字符串
if num == 1: # 从键盘读入字符串
s = input() s = input()
elif num == 2: # 从文件读入字符串 elif num == 2: # 从文件读入字符串
fname = sys.argv[1] fname = sys.argv[1]
s = file2str(fname) s = file2str(fname)
else: else:
print('I can accept at most 2 arguments.') print('I can accept at most 2 arguments.')
sys.exit()# 结束程序运行, 下面的代码不会被执行了。 sys.exit() # 结束程序运行, 下面的代码不会被执行了。
s = remove_punctuation(s) # 这里是s是实参(argument),里面有值
s = remove_punctuation(s) # 这里是s是实参(argument),里面有值
L = freq(s) L = freq(s)
for x in sort_in_descending_order(L): for x in sort_in_descending_order(L):
print('%s\t%d\t%s' % (x[0], x[1], youdao_link(x[0])))#函数导出 print('%s\t%d\t%s' % (x[0], x[1], youdao_link(x[0]))) # 函数导出
# 把频率的结果放result.html中 # 把频率的结果放result.html中
make_html_page(sort_in_descending_order(L), 'result.html') make_html_page(sort_in_descending_order(L), 'result.html')
print('\nHistory:\n') print('\nHistory:\n')
if os.path.exists('frequency.p'): if os.path.exists('frequency.p'):
d = pickle_idea.load_record('frequency.p') d = pickle_idea.load_record('frequency.p')
else: else:
d = {} d = {}
print(sort_in_descending_order(pickle_idea.dict2lst(d))) print(sort_in_descending_order(pickle_idea.dict2lst(d)))
# 合并频率 # 合并频率
lst_history = pickle_idea.dict2lst(d) lst_history = pickle_idea.dict2lst(d)
d = pickle_idea.merge_frequency(L, lst_history) d = pickle_idea.merge_frequency(L, lst_history)
pickle_idea.save_frequency_to_pickle(d, 'frequency.p') pickle_idea.save_frequency_to_pickle(d, 'frequency.p')