diff --git a/.gitignore b/.gitignore index 3d901ba..413c71c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,3 @@ app/static/frequency/frequency.p app/static/wordfreqapp.db app/static/donate-the-author.jpg app/static/donate-the-author-hidden.jpg -app/model/__pycache__/ \ No newline at end of file diff --git a/README.md b/README.md index 14cc9aa..29e74dd 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,6 @@ Bug report: http://118.25.96.118/bugzilla/show_bug.cgi?id=215 - ### 丁锐 修复了以下漏洞 @@ -192,5 +191,4 @@ Bug report: http://118.25.96.118/bugzilla/show_bug.cgi?id=215 Bug report: http://118.25.96.118/bugzilla/show_bug.cgi?id=489 -*Last modified on 2023-01-30* - +*Last modified on 2023-01-30* \ No newline at end of file diff --git a/app/Article.py b/app/Article.py index e40717f..51b9b74 100644 --- a/app/Article.py +++ b/app/Article.py @@ -7,7 +7,7 @@ import random, glob import hashlib from datetime import datetime from flask import Flask, request, redirect, render_template, url_for, session, abort, flash, get_flashed_messages -from difficulty import get_difficulty_level, text_difficulty_level, user_difficulty_level +from difficulty import get_difficulty_level_for_user, text_difficulty_level, user_difficulty_level path_prefix = '/var/www/wordfreq/wordfreq/' @@ -32,20 +32,12 @@ def get_article_body(s): return '\n'.join(lst) -def get_today_article(user_word_list, visited_articles): +def get_today_article(user_word_list, articleID): rq = RecordQuery(path_prefix + 'static/wordfreqapp.db') - if visited_articles is None: - visited_articles = { - "index" : 0, # 为 article_ids 的索引 - "article_ids": [] # 之前显示文章的id列表,越后越新 - } - if visited_articles["index"] > len(visited_articles["article_ids"])-1: # 生成新的文章,因此查找所有的文章 + if articleID == None: rq.instructions("SELECT * FROM article") - else: # 生成阅读过的文章,因此查询指定 article_id 的文章 - if visited_articles["article_ids"][visited_articles["index"]] == 'null': # 可能因为直接刷新页面导致直接去查询了'null',因此当刷新的页面的时候,需要直接进行“上一篇”操作 - visited_articles["index"] -= 1 - visited_articles["article_ids"].pop() - rq.instructions('SELECT * FROM article WHERE article_id=%d' % (visited_articles["article_ids"][visited_articles["index"]])) + else: + rq.instructions('SELECT * FROM article WHERE article_id=%d' % (articleID)) rq.do() result = rq.get_results() random.shuffle(result) @@ -53,51 +45,41 @@ def get_today_article(user_word_list, visited_articles): # 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) + d3 = get_difficulty_level_for_user(d1, d2) - d = None - result_of_generate_article = "not found" + 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. - text_level = 0 - if visited_articles["index"] > len(visited_articles["article_ids"])-1: # 生成新的文章 - amount_of_visited_articles = len(visited_articles["article_ids"]) - amount_of_existing_articles = result.__len__() - if amount_of_visited_articles == amount_of_existing_articles: # 如果当前阅读过的文章的数量 == 存在的文章的数量,即所有的书本都阅读过了 - result_of_generate_article = "had read all articles" - else: - for k in range(3): # 最多尝试3次 - for reading in result: - text_level = text_difficulty_level(reading['text'], d3) - factor = random.gauss(0.8, 0.1) # a number drawn from Gaussian distribution with a mean of 0.8 and a stand deviation of 1 - if reading['article_id'] not in visited_articles["article_ids"] and within_range(text_level, user_level, (8.0 - user_level) * factor): # 新的文章之前没有出现过且符合一定范围的水平 - d = reading - visited_articles["article_ids"].append(d['article_id']) # 列表添加新的文章id;下面进行 - result_of_generate_article = "found" - break - if result_of_generate_article == "found": # 用于成功找到文章后及时退出外层循环 - break - if result_of_generate_article != "found": # 阅读完所有文章,或者循环3次没有找到适合的文章,则放入空(“null”) - visited_articles["article_ids"].append('null') - else: # 生成已经阅读过的文章 - d = random.choice(result) - text_level = text_difficulty_level(d['text'], d3) - result_of_generate_article = "found" + 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) + factor = random.gauss(0.8, + 0.1) # a number drawn from Gaussian distribution with a mean of 0.8 and a stand deviation of 1 + if within_range(text_level, user_level, (8.0 - user_level) * factor): + d = reading + break - today_article = None - if d: - today_article = { - "user_level": '%4.2f' % user_level, - "text_level": '%4.2f' % text_level, - "date": d['date'], - "article_title": get_article_title(d['text']), - "article_body": get_article_body(d['text']), - "source": d["source"], - "question": get_question_part(d['question']), - "answer": get_answer_part(d['question']) - } - - return visited_articles, today_article, result_of_generate_article + s = '' % (user_level, text_level) + s += '

Article added on: %s

' % (d['date']) + s += '
' + article_title = get_article_title(d['text']) + article_body = get_article_body(d['text']) + s += '

%s

' % (article_title) + s += '

%s

' % (article_body) + s += '

%s

' % (d['source']) + s += '

%s

' % (get_question_part(d['question'])) + s = s.replace('\n', '
') + s += '%s' % (get_answer_part(d['question'])) + s += '
' + session['articleID'] = d['article_id'] + return s def load_freq_history(path): @@ -137,4 +119,21 @@ def get_answer_part(s): flag = 1 elif flag == 1: result.append(line) - return '\n'.join(result) + # https://css-tricks.com/snippets/javascript/showhide-element/ + js = ''' + + ''' + html_code = js + html_code += '\n' + html_code += '\n' + html_code += '\n' % ('\n'.join(result)) + return html_code \ No newline at end of file diff --git a/app/Login.py b/app/Login.py index cd750d1..8e0030b 100644 --- a/app/Login.py +++ b/app/Login.py @@ -3,18 +3,6 @@ import string from datetime import datetime, timedelta from UseSqlite import InsertQuery, RecordQuery -def md5(s): - ''' - MD5摘要 - :param str: 字符串 - :return: 经MD5以后的字符串 - ''' - h = hashlib.md5(s.encode(encoding='utf-8')) - return h.hexdigest() - -# import model.user after the defination of md5(s) to avoid circular import -from model.user import get_user_by_username, insert_user, update_password_by_username - path_prefix = '/var/www/wordfreq/wordfreq/' path_prefix = './' # comment this line in deployment @@ -24,9 +12,13 @@ def verify_pass(newpass,oldpass): def verify_user(username, password): - user = get_user_by_username(username) - encoded_password = md5(username + password) - return user is not None and user.password == encoded_password + rq = RecordQuery(path_prefix + 'static/wordfreqapp.db') + password = md5(username + password) + rq.instructions_with_parameters("SELECT * FROM user WHERE name=:username AND password=:password", dict( + username=username, password=password)) # the named style https://docs.python.org/3/library/sqlite3.html + rq.do_with_parameters() + result = rq.get_results() + return result != [] def add_user(username, password): @@ -34,12 +26,19 @@ def add_user(username, password): expiry_date = (datetime.now() + timedelta(days=30)).strftime('%Y%m%d') # will expire after 30 days # 将用户名和密码一起加密,以免暴露不同用户的相同密码 password = md5(username + password) - insert_user(username=username, password=password, start_date=start_date, expiry_date=expiry_date) + rq = InsertQuery(path_prefix + 'static/wordfreqapp.db') + rq.instructions_with_parameters("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() def check_username_availability(username): - existed_user = get_user_by_username(username) - return existed_user is None + rq = RecordQuery(path_prefix + 'static/wordfreqapp.db') + rq.instructions_with_parameters( + "SELECT * FROM user WHERE name=:username", dict(username=username)) + rq.do_with_parameters() + result = rq.get_results() + return result == [] def change_password(username, old_password, new_password): @@ -55,16 +54,35 @@ def change_password(username, old_password, new_password): # 将用户名和密码一起加密,以免暴露不同用户的相同密码 if verify_pass(new_password,old_password): #新旧密码一致 return False - update_password_by_username(username, new_password) + password = md5(username + new_password) + rq = InsertQuery(path_prefix + 'static/wordfreqapp.db') + rq.instructions_with_parameters("UPDATE user SET password=:password WHERE name=:username", dict( + password=password, username=username)) + rq.do_with_parameters() return True def get_expiry_date(username): - user = get_user_by_username(username) - if user is None: - return '20191024' + rq = RecordQuery(path_prefix + 'static/wordfreqapp.db') + rq.instructions_with_parameters( + "SELECT expiry_date FROM user WHERE name=:username", dict(username=username)) + rq.do_with_parameters() + result = rq.get_results() + if len(result) > 0: + return result[0]['expiry_date'] else: - return user.expiry_date + return '20191024' + + +def md5(s): + ''' + MD5摘要 + :param str: 字符串 + :return: 经MD5以后的字符串 + ''' + h = hashlib.md5(s.encode(encoding='utf-8')) + return h.hexdigest() + class UserName: def __init__(self, username): @@ -78,9 +96,9 @@ class UserName: if ' ' in self.username: # a user name must not include a whitespace 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 - if c in string.punctuation and c != '.' and c != '_': + if c in string.punctuation and c is not '.' and c is not '_': return f'{c} is not allowed in the user name.' - if self.username in ['signup', 'login', 'logout', 'reset', 'mark', 'back', 'unfamiliar', 'familiar', 'del', 'admin']: + 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 'OK' diff --git a/app/account_service.py b/app/account_service.py index a7ed0c4..9b1c46b 100644 --- a/app/account_service.py +++ b/app/account_service.py @@ -19,15 +19,21 @@ def signup(): # POST方法需判断是否注册成功,再根据结果返回不同的内容 username = escape(request.form['username']) password = escape(request.form['password']) + password2 = escape(request.form['password2']) #! 添加如下代码为了过滤注册时的非法字符 warn = WarningMessage(username) if str(warn) != 'OK': - return jsonify({'status': '3', 'warn': str(warn)}) + return str(warn) available = check_username_availability(username) if not available: # 用户名不可用 - return jsonify({'status': '0'}) + flash('用户名 %s 已经被注册。' % (username)) + return render_template('signup.html') + elif len(password.strip()) < 4: # 密码过短 + return '密码过于简单。' + elif password != password2: + return '确认密码与输入密码不一致!' else: # 添加账户信息 add_user(username, password) verified = verify_user(username, password) @@ -37,10 +43,11 @@ def signup(): session[username] = username session['username'] = username session['expiry_date'] = get_expiry_date(username) - session['visited_articles'] = None - return jsonify({'status': '2'}) + session['articleID'] = None + return '

恭喜,你已成功注册, 你的用户名是 %s

\ +

开始使用 返回首页

' % (username, username, username) else: - return jsonify({'status': '1'}) + return '用户名密码验证失败。' @@ -52,7 +59,13 @@ def login(): ''' if request.method == 'GET': # GET请求 - return render_template('login.html') + if not session.get('logged_in'): + # 未登录,返回登录页面 + return render_template('login.html') + else: + # 已登录,提示信息并显示登出按钮 + return '你已登录 %s。 登出点击这里。' % ( + session['username'], session['username']) elif request.method == 'POST': # POST方法用于判断登录是否成功 # check database and verify user @@ -66,10 +79,10 @@ def login(): session['username'] = username user_expiry_date = get_expiry_date(username) session['expiry_date'] = user_expiry_date - session['visited_articles'] = None - return jsonify({'status': '1'}) + session['articleID'] = None + return redirect(url_for('user_bp.userpage', username=username)) else: - return jsonify({'status': '0'}) + return '无法通过验证。' @accountService.route("/logout", methods=['GET', 'POST']) @@ -102,9 +115,31 @@ def reset(): # POST请求用于提交修改后信息 old_password = escape(request.form['old-password']) new_password = escape(request.form['new-password']) + + re_new_password = escape(request.form['re-new-password']) # 确认新密码 + if re_new_password != new_password: #验证新密码两次输入是否相同 + return '新密码不匹配,请重新输入' + if len(new_password) < 4: #验证新密码长度,原则参照注册模块 + return '密码过于简单。(密码长度至少4位)' + flag = change_password(username, old_password, new_password) # flag表示是否修改成功 if flag: session['logged_in'] = False - return jsonify({'status':'1'}) # 修改成功 + return \ +''' + + +''' + else: - return jsonify({'status':'2'}) # 修改失败 + return \ +''' + + +''' diff --git a/app/difficulty.py b/app/difficulty.py index 50aa179..5df3575 100644 --- a/app/difficulty.py +++ b/app/difficulty.py @@ -8,6 +8,7 @@ import pickle import math from wordfreqCMD import remove_punctuation, freq, sort_in_descending_order, sort_in_ascending_order +import snowballstemmer def load_record(pickle_fname): @@ -16,41 +17,48 @@ def load_record(pickle_fname): f.close() return d +def convert_test_type_to_difficulty_level(d): + """ + 对原本的单词库中的单词进行难度评级 + :param d: 存储了单词库pickle文件中的单词的字典 + :return: + """ + result = {} + L = list(d.keys()) # in d, we have test types (e.g., CET4,CET6,BBC) for each word -def difficulty_level_from_frequency(word, d): - level = 1 - if not word in d: - 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) + for k in L: + if 'CET4' in d[k]: + result[k] = 4 # CET4 word has level 4 + elif 'OXFORD3000' in d[k]: + result[k] = 5 + elif 'CET6' in d[k] or 'GRADUATE' in d[k]: + result[k] = 6 + elif 'OXFORD5000' in d[k] or 'IELTS' in d[k]: + result[k] = 7 + elif 'BBC' in d[k]: + result[k] = 8 - level = min(level, 8) - return level + return result # {'apple': 4, ...} -def get_difficulty_level(d1, d2): - d = {} - 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 - L.extend(L2) - L3 = list(set(L)) # L3 contains all words - for k in L3: - if k in d2: - if 'CET4' in d2[k]: - d[k] = 4 # CET4 word has level 4 - elif 'CET6' in d2[k]: - d[k] = 6 - elif 'BBC' in d2[k]: - 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. - d[k] = min(difficulty_level_from_frequency(k, d1), d[k]) - elif k in d1: - d[k] = difficulty_level_from_frequency(k, d1) - - return d +def get_difficulty_level_for_user(d1, d2): + """ + d2 来自于词库的35511个已标记单词 + d1 用户不会的词 + 在d2的后面添加单词,没有新建一个新的字典 + """ + d2 = convert_test_type_to_difficulty_level(d2) # 根据d2的标记评级{'apple': 4, 'abandon': 4, ...} + stem = snowballstemmer.stemmer('english') + for k in d1: # 用户的词 + if k in d2: # 如果用户的词以原型的形式存在于词库d2中 + continue # 无需评级,跳过 + elif stem.stemWord(k) in d2: # 如果用户的词的词根存在于词库d2的词根库中 + d2[k] = d2[stem.stemWord(k)] # 按照词根进行评级 + break + else: + d2[k] = 3 # 如果k的词根都不在,那么就当认为是3级 + return d2 def revert_dict(d): @@ -82,7 +90,7 @@ def user_difficulty_level(d_user, d): for date in sorted(d_user2.keys(), reverse=True): # most recently added words are more important while determining user's level lst = d_user2[date] # a list of words lst2 = [] # a list of tuples, (word, difficulty level) - for word in lst: + for word in lst: if word in d: lst2.append((word, d[word])) @@ -136,7 +144,7 @@ if __name__ == '__main__': #print(d2) - d3 = get_difficulty_level(d1, d2) + d3 = get_difficulty_level_for_user(d1, d2) s = ''' South Lawn diff --git a/app/main.py b/app/main.py index 4e3f829..e311bb0 100644 --- a/app/main.py +++ b/app/main.py @@ -5,24 +5,24 @@ # Copyright 2019 (C) Hui Lan # Written permission must be obtained from the author for commercial uses. ########################################################################### + from flask import escape from Login import * from Article import * import Yaml from user_service import userService from account_service import accountService -from admin_service import adminService, ADMIN_NAME app = Flask(__name__) app.secret_key = 'lunch.time!' # 将蓝图注册到Lab app app.register_blueprint(userService) app.register_blueprint(accountService) -app.register_blueprint(adminService) path_prefix = '/var/www/wordfreq/wordfreq/' path_prefix = './' # comment this line in deployment + def get_random_image(path): ''' 返回随机图 @@ -39,7 +39,8 @@ def get_random_ads(): 返回随机广告 :return: 一个广告(包含HTML标签) ''' - return random.choice(['个性化分析精准提升', '你的专有单词本', '智能捕捉阅读弱点,针对性提高你的阅读水平']) + ads = random.choice(['个性化分析精准提升', '你的专有单词本', '智能捕捉阅读弱点,针对性提高你的阅读水平']) + return ads + '。 试试吧!' def appears_in_test(word, d): @@ -97,13 +98,9 @@ def mainpage(): d = load_freq_history(path_prefix + 'static/frequency/frequency.p') d_len = len(d) lst = sort_in_descending_order(pickle_idea.dict2lst(d)) - return render_template('mainpage_get.html', - admin_name=ADMIN_NAME, - random_ads=random_ads, - d_len=d_len, - lst=lst, - yml=Yaml.yml, - number_of_essays=number_of_essays) + return render_template('mainpage_get.html', random_ads=random_ads, number_of_essays=number_of_essays, + d_len=d_len, lst=lst, yml=Yaml.yml) + if __name__ == '__main__': diff --git a/app/model/__init__.py b/app/model/__init__.py deleted file mode 100644 index 9526313..0000000 --- a/app/model/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -from pony.orm import * - -db = Database() -db.bind("sqlite", "../static/wordfreqapp.db", create_db=True) # bind sqlite file - - -class User(db.Entity): - _table_ = "user" # table name - name = PrimaryKey(str) - password = Optional(str) - start_date = Optional(str) - expiry_date = Optional(str) - - -class Article(db.Entity): - _table_ = "article" # table name - article_id = PrimaryKey(int, auto=True) - text = Optional(str) - source = Optional(str) - date = Optional(str) - level = Optional(str) - question = Optional(str) - - -db.generate_mapping(create_tables=True) # must mapping after class declaration - - -if __name__ == "__main__": - with db_session: - print(Article[2].text) # test get article which id=2 text content diff --git a/app/model/article.py b/app/model/article.py deleted file mode 100644 index a3b4bf7..0000000 --- a/app/model/article.py +++ /dev/null @@ -1,34 +0,0 @@ -from model import * -from datetime import datetime - -def add_article(content, source="manual_input", level="5", question="No question"): - with db_session: - # add one article to sqlite - Article( - text=content, - source=source, - date=datetime.now().strftime("%-d %b %Y"), # format style of `5 Oct 2022` - level=level, - question=question, - ) - - -def delete_article_by_id(article_id): - article_id &= 0xFFFFFFFF # max 32 bits - with db_session: - article = Article.select(article_id=article_id) - if article: - article.first().delete() - - -def get_number_of_articles(): - with db_session: - return len(Article.select()[:]) - - -def get_page_articles(num, size): - with db_session: - return [ - x - for x in Article.select().order_by(desc(Article.article_id)).page(num, size) - ] diff --git a/app/model/user.py b/app/model/user.py deleted file mode 100644 index d684332..0000000 --- a/app/model/user.py +++ /dev/null @@ -1,30 +0,0 @@ -from model import * -from Login import md5 -from pony import orm - -def get_users(): - with db_session: - return User.select().order_by(User.name)[:] - -def get_user_by_username(username): - with db_session: - user = User.select(name=username) - if user: - return user.first() - -def insert_user(username, password, start_date, expiry_date): - with db_session: - user = User(name=username, password=password, start_date=start_date, expiry_date=expiry_date) - orm.commit() - -def update_password_by_username(username, password="123456"): - with db_session: - user = User.select(name=username) - if user: - user.first().password = md5(username + password) - -def update_expiry_time_by_username(username, expiry_time="20230323"): - with db_session: - user = User.select(name=username) - if user: - user.first().expiry_date = expiry_time diff --git a/app/pickle_idea2.py b/app/pickle_idea2.py index 0da55bc..4055fc4 100644 --- a/app/pickle_idea2.py +++ b/app/pickle_idea2.py @@ -68,7 +68,7 @@ def save_frequency_to_pickle(d, pickle_fname): d2 = {} for k in d: if not k in exclusion_lst and not k.isnumeric() and not len(k) < 2: - d2[k] = list(sorted(d[k])) # 原先这里是d2[k] = list(sorted(set(d[k]))) + d2[k] = list(sorted(set(d[k]))) pickle.dump(d2, f) f.close() diff --git a/app/static/config.yml b/app/static/config.yml index e6f7ef1..20aa396 100644 --- a/app/static/config.yml +++ b/app/static/config.yml @@ -1,16 +1,16 @@ # 全局引入的css文件地址 css: item: - - ../static/css/bootstrap.css + - static/css/bootstrap.css # 全局引入的js文件地址 js: head: # 在页面加载之前加载 - - ../static/js/jquery.js - - ../static/js/word_operation.js + - static/js/jquery.js + - static/js/word_operation.js bottom: # 在页面加载完之后加载 - - ../static/js/fillword.js - - ../static/js/highlight.js + - static/js/fillword.js + - static/js/highlight.js # 高亮样式,目前仅支持修改颜色 highlight: diff --git a/app/static/js/highlight.js b/app/static/js/highlight.js index 0cea31a..5ec9663 100644 --- a/app/static/js/highlight.js +++ b/app/static/js/highlight.js @@ -38,18 +38,8 @@ function highLight() { list[i] = list[i].replace('|', ""); list[i] = list[i].replace('?', ""); if (list[i] !== "" && "".indexOf(list[i]) === -1 && "".indexOf(list[i]) === -1) { - //将文章中所有出现该单词word的地方改为:"" + word + ""。 正则表达式RegExp()中,"\\b"代表单词边界匹配。 - - //修改代码 - let articleContent_fb = articleContent; //文章副本 - while(articleContent_fb.toLowerCase().indexOf(list[i].toLowerCase()) !== -1 && list[i]!=""){ - //找到副本中和list[i]匹配的第一个单词(第一种匹配情况),并赋值给list[i]。 - const index = articleContent_fb.toLowerCase().indexOf(list[i].toLowerCase()); - list[i] = articleContent_fb.substring(index, index + list[i].length); - - articleContent_fb = articleContent_fb.substring(index + list[i].length); // 使用副本中list[i]之后的子串替换掉副本 - articleContent = articleContent.replace(new RegExp("\\b"+list[i]+"\\b","g"),"" + list[i] + ""); - } + //将文章中所有出现该单词word的地方改为:" " + word + " "。 正则表达式RegExp()中,"\\s"代表单词前后必须要有空格,以防止只对单词中的部分字符高亮的情况出现。 + articleContent = articleContent.replace(new RegExp("\\s"+list[i]+"\\s", "g"), " " + list[i] + " "); } } document.getElementById("article").innerHTML = articleContent; diff --git a/app/static/words_and_tests.p b/app/static/words_and_tests.p index 62afd6d..5c46c5a 100644 Binary files a/app/static/words_and_tests.p and b/app/static/words_and_tests.p differ diff --git a/app/templates/admin_index.html b/app/templates/admin_index.html deleted file mode 100644 index f62a137..0000000 --- a/app/templates/admin_index.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - {{ yml['header'] | safe }} - {% if yml['css']['item'] %} - {% for css in yml['css']['item'] %} - - {% endfor %} - {% endif %} - {% if yml['js']['head'] %} - {% for js in yml['js']['head'] %} - - {% endfor %} - {% endif %} - - - - -

- -
-
- 请选择您需要的操作 -
- -
- - - diff --git a/app/templates/admin_manage_article.html b/app/templates/admin_manage_article.html deleted file mode 100644 index 272b54e..0000000 --- a/app/templates/admin_manage_article.html +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - {% for message in get_flashed_messages() %} - - {% endfor %} - -
- {% if tips %} - - {% endif %} -
-
录入文章
-
-
- - - - - - - - -
- -
-
-
- -
-
文章列表
-
- {% for text in text_list %} -
-
- 删除 -
-
-
{{ text.title }}
-
-
{{ text.source }}
-
- Level: {{text.level }} - Date: {{ text.date }} -
- {{ text.content | safe }} -
- {% endfor %} -
-
-
- -
- - - diff --git a/app/templates/admin_manage_user.html b/app/templates/admin_manage_user.html deleted file mode 100644 index cee4667..0000000 --- a/app/templates/admin_manage_user.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - {% for message in get_flashed_messages() %} - - {% endfor %} - -
-
重置选中用户的信息
-
-
- - - - -
- - -
- - -
- -
- -
- - -
-
- - - - - - diff --git a/app/templates/login.html b/app/templates/login.html index 2507f75..a347e22 100644 --- a/app/templates/login.html +++ b/app/templates/login.html @@ -1,47 +1,28 @@ {% block body %} {% if session['logged_in'] %} -你已登录 {{ session['username'] }}。 登出点击这里。 +You're logged in already! {% else %} - - +

Sign In

- - - - 注册 +
+ + + +
+注册 + {% endif %} {% endblock %} diff --git a/app/templates/mainpage_get.html b/app/templates/mainpage_get.html index 344943d..cbb51a6 100644 --- a/app/templates/mainpage_get.html +++ b/app/templates/mainpage_get.html @@ -23,15 +23,12 @@

English Pal - Learn English smartly!

{% if session['logged_in'] %} - {{ session['username'] }} - {% if session['username'] == admin_name %} - 管理

- {% endif %} + {{session['username']}}

{% else %}

登录 注册 使用说明

-

{{ random_ads }}。 试试吧!

+

{{random_ads|safe}}

{% endif %} - +

粘贴1篇文章 (English only)


diff --git a/app/templates/reset.html b/app/templates/reset.html index 3425c97..902d046 100644 --- a/app/templates/reset.html +++ b/app/templates/reset.html @@ -2,38 +2,6 @@ - -
@@ -41,11 +9,14 @@

Reset Password

- - - - - + + + + + + +
{% endblock %} \ No newline at end of file diff --git a/app/templates/signup.html b/app/templates/signup.html index 9030d41..1fd05f0 100644 --- a/app/templates/signup.html +++ b/app/templates/signup.html @@ -6,47 +6,6 @@ You're logged in already! Logout. {% else %} - -

{{ get_flashed_messages()[0] | safe }}

@@ -56,10 +15,12 @@ You're logged in already! Logout.

Sign Up

-

-

-

- +
+

+

+

+ +
diff --git a/app/templates/userpage_get.html b/app/templates/userpage_get.html index e134d94..dc0d497 100644 --- a/app/templates/userpage_get.html +++ b/app/templates/userpage_get.html @@ -37,51 +37,20 @@

English Pal for {{ username }} - - {% if username == admin_name %} - 管理 - {% endif %} - 退出 - 重设密码 - + 退出 + 重设密码

-{# {% for message in flashed_messages %}#} {# 根据user_service.userpage,取消了参数flashed_messages,因此注释了这段代码 #} -{# #} -{# {% endfor %}#} + {{ flashed_messages|safe }} - - + 下一篇 Next Article + {% if session.get('articleID') != session.get('old_articleID') %} + {% if session.get('old_articleID') != None %} + 上一篇 Previous Article + {% endif%} + {% endif %}

阅读文章并回答问题

-
-
- -

Article added on: {{ today_article["date"] }}


-

-

{{ today_article["article_title"] }}


-

{{ today_article["article_body"] }}


-

{{ today_article['source'] }}


-

{{ today_article['question'] }}


- - -
-
-
- - -
+
{{ today_article|safe }}
生词高亮 大声朗读 @@ -91,11 +60,11 @@
- +
- +

收集生词吧 (可以在正文中划词,也可以复制黏贴)

-
+
@@ -145,73 +114,12 @@ {% endfor %} {% endif %} - diff --git a/app/user_service.py b/app/user_service.py index 2e5feed..2d10404 100644 --- a/app/user_service.py +++ b/app/user_service.py @@ -1,5 +1,5 @@ from datetime import datetime -from admin_service import ADMIN_NAME + from flask import * # from app import Yaml @@ -21,46 +21,33 @@ userService = Blueprint("user_bp", __name__) path_prefix = '/var/www/wordfreq/wordfreq/' path_prefix = './' # comment this line in deployment -@userService.route("/get_next_article/",methods=['GET','POST']) -def get_next_article(username): - user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username) + +@userService.route("//reset", methods=['GET', 'POST']) +def user_reset(username): + ''' + 用户界面 + :param username: 用户名 + :return: 返回页面内容 + ''' session['old_articleID'] = session.get('articleID') if request.method == 'GET': - visited_articles = session.get("visited_articles") - if visited_articles['article_ids'][-1] == "null": # 如果当前还是“null”,则将“null”pop出来,无需index+=1 - visited_articles['article_ids'].pop() - else: # 当前不为“null”,直接 index+=1 - visited_articles["index"] += 1 - session["visited_articles"] = visited_articles - visited_articles, today_article, result_of_generate_article = get_today_article(user_freq_record, session.get('visited_articles')) - data = { - 'visited_articles': visited_articles, - 'today_article': today_article, - 'result_of_generate_article': result_of_generate_article - } + session['articleID'] = None + return redirect(url_for('user_bp.userpage', username=username)) else: return 'Under construction' - return json.dumps(data) -@userService.route("/get_pre_article/",methods=['GET']) -def get_pre_article(username): - user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username) +@userService.route("//back", methods=['GET']) +def user_back(username): + ''' + 用户界面 + :param username: 用户名 + :return: 返回页面内容 + ''' if request.method == 'GET': - visited_articles = session.get("visited_articles") - if(visited_articles["index"]==0): - data='' - else: - visited_articles["index"] -= 1 # 上一篇,index-=1 - if visited_articles['article_ids'][-1] == "null": # 如果当前还是“null”,则将“null”pop出来 - visited_articles['article_ids'].pop() - session["visited_articles"] = visited_articles - visited_articles, today_article, result_of_generate_article = get_today_article(user_freq_record, session.get('visited_articles')) - data = { - 'visited_articles': visited_articles, - 'today_article': today_article, - 'result_of_generate_article':result_of_generate_article - } - return json.dumps(data) + session['articleID'] = session.get('old_articleID') + return redirect(url_for('user_bp.userpage', username=username)) + + @userService.route("///unfamiliar", methods=['GET', 'POST']) def unfamiliar(username, word): @@ -102,12 +89,11 @@ def deleteword(username, word): ''' user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username) pickle_idea2.deleteRecord(user_freq_record, word) - # 模板userpage_get.html中删除单词是异步执行,而flash的信息后续是同步执行的,所以注释这段代码;同时如果这里使用flash但不提取信息,则会影响 signup.html的显示。bug复现:删除单词后,点击退出,点击注册,注册页面就会出现提示信息 - # flash(f'{word} is no longer in your word list.') + flash(f'{word} is no longer in your word list.') return "success" -@userService.route("//userpage", methods=['GET', 'POST']) +@userService.route("/", methods=['GET', 'POST']) def userpage(username): ''' 用户界面 @@ -144,21 +130,20 @@ def userpage(username): words = '' for x in lst3: words += x[0] + ' ' - visited_articles, today_article, result_of_generate_article = get_today_article(user_freq_record, session.get('visited_articles')) - session['visited_articles'] = visited_articles - # 通过 today_article,加载前端的显示页面 return render_template('userpage_get.html', - admin_name=ADMIN_NAME, username=username, session=session, - # flashed_messages=get_flashed_messages(), 仅有删除单词的时候使用到flash,而删除单词是异步执行,这里的信息提示是同步执行,所以就没有存在的必要了 - today_article=today_article, - result_of_generate_article=result_of_generate_article, + flashed_messages=get_flashed_messages_if_any(), + today_article=get_today_article(user_freq_record, session['articleID']), d_len=len(d), lst3=lst3, yml=Yaml.yml, words=words) + + + + @userService.route("//mark", methods=['GET', 'POST']) def user_mark_word(username): ''' @@ -188,3 +173,15 @@ def get_time(): ''' return datetime.now().strftime('%Y%m%d%H%M') # upper to minutes +def get_flashed_messages_if_any(): + ''' + 在用户界面显示黄色提示信息 + :return: 包含HTML标签的提示信息 + ''' + messages = get_flashed_messages() + s = '' + for message in messages: + s += '' + return s diff --git a/app/wordfreqCMD.py b/app/wordfreqCMD.py index e56ba0c..c4f8a63 100644 --- a/app/wordfreqCMD.py +++ b/app/wordfreqCMD.py @@ -70,7 +70,7 @@ def sort_in_ascending_order(lst):# 单词按频率降序排列 return lst2 -def make_html_page(lst, fname): # 只是在wordfreqCMD.py中的main函数中调用,所以不做修改 +def make_html_page(lst, fname): ''' 功能:把lst的信息存到fname中,以html格式。 ''' diff --git a/build.sh b/build.sh index e313fce..4348b2f 100755 --- a/build.sh +++ b/build.sh @@ -3,10 +3,6 @@ DEPLOYMENT_DIR=/home/lanhui/englishpal2/EnglishPal cd $DEPLOYMENT_DIR -# Install dependencies - -pip3 install -r requirements.txt - # Stop service sudo docker stop EnglishPal sudo docker rm EnglishPal diff --git a/requirements.txt b/requirements.txt index e2d1e1f..2746a3b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ Flask==1.1.2 selenium==3.141.0 PyYAML~=6.0 -pony==0.7.16