diff --git a/app/Article.py b/app/Article.py index df9ac3a..d31915b 100644 --- a/app/Article.py +++ b/app/Article.py @@ -1,14 +1,8 @@ -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 -import hashlib -from datetime import datetime -from flask import Flask, request, redirect, render_template, url_for, session, abort, flash, get_flashed_messages +import random +import pickle_idea from difficulty import get_difficulty_level_for_user, text_difficulty_level, user_difficulty_level - +from UseSqlite import RecordQuery path_prefix = '/var/www/wordfreq/wordfreq/' path_prefix = './' # comment this line in deployment @@ -33,19 +27,26 @@ def get_article_body(s): def get_today_article(user_word_list, visited_articles): + ''' + 根据用户的单词列表和阅读过的文章返回需要的文章的全部信息 + ''' rq = RecordQuery(path_prefix + 'static/wordfreqapp.db') if visited_articles is None: visited_articles = { - "index" : 0, # 为 article_ids 的索引 + "index": 0, # 为 article_ids 的索引 "article_ids": [] # 之前显示文章的id列表,越后越新 } if visited_articles["index"] > len(visited_articles["article_ids"])-1: # 生成新的文章,因此查找所有的文章 rq.instructions("SELECT * FROM article") else: # 生成阅读过的文章,因此查询指定 article_id 的文章 - if visited_articles["article_ids"][visited_articles["index"]] == 'null': # 可能因为直接刷新页面导致直接去查询了'null',因此当刷新的页面的时候,需要直接进行“上一篇”操作 + # 可能因为直接刷新页面导致直接去查询了'null',因此当刷新的页面的时候,需要直接进行“上一篇”操作 + if visited_articles["article_ids"][visited_articles["index"]] == '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"]])) + rq.instructions( + f'SELECT * FROM article WHERE article_id=' + f'{visited_articles["article_ids"][visited_articles["index"]]}' + ) rq.do() result = rq.get_results() random.shuffle(result) @@ -58,19 +59,23 @@ def get_today_article(user_word_list, visited_articles): d = None result_of_generate_article = "not found" 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) 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: # 如果当前阅读过的文章的数量 == 存在的文章的数量,即所有的书本都阅读过了 + amount_of_existing_articles = len(result) + # 如果当前阅读过的文章的数量 == 存在的文章的数量,即所有的书本都阅读过了 + 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): # 新的文章之前没有出现过且符合一定范围的水平 + # 从高斯分布中得出的平均值为 0.8,站位偏差为 1 的数字 + factor = random.gauss(0.8, 0.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" @@ -87,8 +92,8 @@ def get_today_article(user_word_list, visited_articles): today_article = None if d: today_article = { - "user_level": '%4.2f' % user_level, - "text_level": '%4.2f' % text_level, + "user_level": f'{user_level:4.2f}', + "text_level": f'{text_level:4.2f}', "date": d['date'], "article_title": get_article_title(d['text']), "article_body": get_article_body(d['text']), diff --git a/app/Login.py b/app/Login.py index cd750d1..e329fc1 100644 --- a/app/Login.py +++ b/app/Login.py @@ -1,7 +1,6 @@ import hashlib import string from datetime import datetime, timedelta -from UseSqlite import InsertQuery, RecordQuery def md5(s): ''' @@ -12,14 +11,13 @@ def md5(s): 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 +from app.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 def verify_pass(newpass,oldpass): - if(newpass==oldpass): + if newpass==oldpass: return True @@ -63,8 +61,7 @@ def get_expiry_date(username): user = get_user_by_username(username) if user is None: return '20191024' - else: - return user.expiry_date + return user.expiry_date class UserName: def __init__(self, username): @@ -77,6 +74,7 @@ class UserName: return 'Period (.) is not allowed as the first letter in the user name.' 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 != '_': return f'{c} is not allowed in the user name.' @@ -91,5 +89,4 @@ class WarningMessage: self.s = s def __str__(self): - return UserName(self.s).validate() - + return UserName(self.s).validate() \ No newline at end of file diff --git a/app/WordFreq.py b/app/WordFreq.py index 3620a41..7e01491 100644 --- a/app/WordFreq.py +++ b/app/WordFreq.py @@ -2,9 +2,9 @@ # Copyright 2019 (C) Hui Lan # Written permission must be obtained from the author for commercial uses. ########################################################################### - -from wordfreqCMD import remove_punctuation, freq, sort_in_descending_order import string +from wordfreqCMD import remove_punctuation, freq, sort_in_descending_order + class WordFreq: def __init__(self, s): @@ -17,7 +17,7 @@ class WordFreq: if len(word) > 0 and word[0] in string.ascii_letters: lst.append(t) return sort_in_descending_order(lst) - + if __name__ == '__main__': f = WordFreq('BANANA; Banana, apple ORANGE Banana banana.') diff --git a/app/Yaml.py b/app/Yaml.py index 00974aa..0057f4a 100644 --- a/app/Yaml.py +++ b/app/Yaml.py @@ -6,7 +6,6 @@ Yaml.py ./layout/partial/footer.html ''' import yaml as YAML -import os path_prefix = './' # comment this line in deployment diff --git a/app/account_service.py b/app/account_service.py index a7ed0c4..52aa1c0 100644 --- a/app/account_service.py +++ b/app/account_service.py @@ -1,46 +1,44 @@ -from flask import * -from Login import check_username_availability, verify_user, add_user, get_expiry_date, change_password, WarningMessage - +from flask import Blueprint, request, render_template, jsonify,session,redirect, url_for,escape +from Login import check_username_availability, verify_user, add_user,get_expiry_date, change_password, WarningMessage # 初始化蓝图 accountService = Blueprint("accountService", __name__) -### Sign-up, login, logout ### +### sign-up, login, logout, reset### @accountService.route("/signup", methods=['GET', 'POST']) def signup(): ''' 注册 :return: 根据注册是否成功返回不同界面 ''' + # GET方法直接返回注册页面 if request.method == 'GET': - # GET方法直接返回注册页面 return render_template('signup.html') - elif request.method == 'POST': - # POST方法需判断是否注册成功,再根据结果返回不同的内容 - username = escape(request.form['username']) - password = escape(request.form['password']) - - #! 添加如下代码为了过滤注册时的非法字符 - warn = WarningMessage(username) - if str(warn) != 'OK': - return jsonify({'status': '3', 'warn': str(warn)}) - - available = check_username_availability(username) - if not available: # 用户名不可用 - return jsonify({'status': '0'}) - else: # 添加账户信息 - add_user(username, password) - verified = verify_user(username, password) - if verified: - # 写入session - session['logged_in'] = True - session[username] = username - session['username'] = username - session['expiry_date'] = get_expiry_date(username) - session['visited_articles'] = None - return jsonify({'status': '2'}) - else: - return jsonify({'status': '1'}) + + # POST方法需判断是否注册成功,再根据结果返回不同的内容 + username = escape(request.form['username']) + password = escape(request.form['password']) + #! 添加如下代码为了过滤注册时的非法字符 + warn = WarningMessage(username) + if str(warn) != 'OK': + return jsonify({'status': '3', 'warn': str(warn)}) + available = check_username_availability(username) + # 用户名不可用 + if not available: + return jsonify({'status': '0'}) + # 用户名可用,添加账户信息 + add_user(username, password) + verified = verify_user(username, password) + # 注册成功,写入session + if verified: + session['logged_in'] = True + session[username] = username + session['username'] = username + session['expiry_date'] = get_expiry_date(username) + session['visited_articles'] = None + return jsonify({'status': '2'}) + # 验证失败 + return jsonify({'status': '1'}) @@ -50,26 +48,25 @@ def login(): 登录 :return: 根据登录是否成功返回不同页面 ''' + # GET方法直接返回登录页面 if request.method == 'GET': - # GET请求 return render_template('login.html') - elif request.method == 'POST': - # POST方法用于判断登录是否成功 - # check database and verify user - username = escape(request.form['username']) - password = escape(request.form['password']) - verified = verify_user(username, password) - if verified: - # 登录成功,写入session - session['logged_in'] = True - session[username] = username - session['username'] = username - user_expiry_date = get_expiry_date(username) - session['expiry_date'] = user_expiry_date - session['visited_articles'] = None - return jsonify({'status': '1'}) - else: - return jsonify({'status': '0'}) + + # POST方法用于判断登录是否成功,检查数据库并验证用户,再根据结果返回不同的内容 + username = escape(request.form['username']) + password = escape(request.form['password']) + verified = verify_user(username, password) + # 登录成功,写入session + 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['visited_articles'] = None + return jsonify({'status': '1'}) + #验证失败 + return jsonify({'status': '0'}) @accountService.route("/logout", methods=['GET', 'POST']) @@ -95,16 +92,18 @@ def reset(): username = session['username'] if username == '': return redirect('/login') + + # GET请求返回修改密码页面 if request.method == 'GET': - # GET请求返回修改密码页面 return render_template('reset.html', username=session['username'], state='wait') - else: - # POST请求用于提交修改后信息 - old_password = escape(request.form['old-password']) - new_password = escape(request.form['new-password']) - flag = change_password(username, old_password, new_password) # flag表示是否修改成功 - if flag: - session['logged_in'] = False - return jsonify({'status':'1'}) # 修改成功 - else: - return jsonify({'status':'2'}) # 修改失败 + + # POST请求用于提交修改后信息 + old_password = escape(request.form['old-password']) + new_password = escape(request.form['new-password']) + flag = change_password(username, old_password, new_password) # flag表示是否修改成功 + # 修改成功 + if flag: + session['logged_in'] = False + return jsonify({'status':'1'}) + # 修改失败 + return jsonify({'status':'2'}) diff --git a/app/admin_service.py b/app/admin_service.py index a604b5e..424055b 100644 --- a/app/admin_service.py +++ b/app/admin_service.py @@ -1,17 +1,17 @@ # System Library -from flask import * +from flask import Blueprint, session, render_template, request, flash # Personal library from Yaml import yml -from model.user import * -from model.article import * - +from model.user import get_users, update_password_by_username +from model.user import update_expiry_time_by_username, get_user_by_username +from model.article import get_number_of_articles, get_page_articles +from model.article import delete_article_by_id, add_article ADMIN_NAME = "lanhui" # unique admin name _cur_page = 1 # current article page _page_size = 5 # article sizes per page adminService = Blueprint("admin_service", __name__) - def check_is_admin(): # 未登录,跳转到未登录界面 if not session.get("logged_in"): @@ -26,6 +26,10 @@ def check_is_admin(): @adminService.route("/admin", methods=["GET"]) def admin(): + ''' + 判断是否是管理员登录 + :return:不同页面 + ''' is_admin = check_is_admin() if is_admin != "pass": return is_admin @@ -37,6 +41,11 @@ def admin(): @adminService.route("/admin/article", methods=["GET", "POST"]) def article(): + ''' + 管理文章 + 可添加文章、删除文章 + return:不同页面 + ''' global _cur_page, _page_size is_admin = check_is_admin() @@ -49,16 +58,15 @@ def article(): max(1, int(request.args.get("size", 5))), _article_number ) # 最小的size是1 _cur_page = min( - max(1, int(request.args.get("page", 1))), _article_number // _page_size + (_article_number % _page_size > 0) + max(1, int(request.args.get("page", 1))), + _article_number // _page_size + (_article_number % _page_size > 0) ) # 最小的page是1 except ValueError: return "page parmas must be int!" - _articles = get_page_articles(_cur_page, _page_size) - for article in _articles: # 获取每篇文章的title - article.title = article.text.split("\n")[0] - article.content = '
'.join(article.text.split("\n")[1:]) - + for _article in _articles: # 获取每篇文章的title + _article.title = _article.text.split("\n")[0] + _article.content = '
'.join(_article.text.split("\n")[1:]) context = { "article_number": _article_number, "text_list": _articles, @@ -72,19 +80,19 @@ def article(): context["article_number"] = article_len context["text_list"] = get_page_articles(_cur_page, _page_size) _articles = get_page_articles(_cur_page, _page_size) - for article in _articles: # 获取每篇文章的title - article.title = article.text.split("\n")[0] + for _article in _articles: # 获取每篇文章的title + _article.title = _article.text.split("\n")[0] context["text_list"] = _articles if request.method == "GET": try: delete_id = int(request.args.get("delete_id", 0)) - except: + except ValueError: return "Delete article ID must be int!" if delete_id: # delete article delete_article_by_id(delete_id) _update_context() - elif request.method == "POST": + else: data = request.form content = data.get("content", "") source = data.get("source", "") @@ -102,17 +110,21 @@ def article(): @adminService.route("/admin/user", methods=["GET", "POST"]) def user(): + ''' + 用户管理 + 可修改用户密码,过期日期 + return:不同页面 + ''' is_admin = check_is_admin() if is_admin != "pass": return is_admin - context = { "user_list": get_users(), "username": session.get("username"), } if request.method == "POST": data = request.form - username = data.get("username","") + username = data.get("username", "") new_password = data.get("new_password", "") expiry_time = data.get("expiry_time", "") if username: @@ -127,6 +139,9 @@ def user(): @adminService.route("/admin/expiry", methods=["GET"]) def user_expiry_time(): + ''' + 返回用户的过期日期 + ''' is_admin = check_is_admin() if is_admin != "pass": return is_admin @@ -135,8 +150,7 @@ def user_expiry_time(): if not username: return "Username can't be empty." - user = get_user_by_username(username) - if not user: + existed_user = get_user_by_username(username) + if not existed_user: return "User does not exist." - - return user.expiry_date + return existed_user.expiry_date diff --git a/app/difficulty.py b/app/difficulty.py index cb93768..f6ece3b 100644 --- a/app/difficulty.py +++ b/app/difficulty.py @@ -6,15 +6,14 @@ # Purpose: compute difficulty level of a English text import pickle -import math -from wordfreqCMD import remove_punctuation, freq, sort_in_descending_order, sort_in_ascending_order import snowballstemmer +from wordfreqCMD import remove_punctuation, freq, sort_in_descending_order, sort_in_ascending_order + def load_record(pickle_fname): - f = open(pickle_fname, 'rb') - d = pickle.load(f) - f.close() + with open(pickle_fname, 'rb') as f: + d = pickle.load(f) return d @@ -53,9 +52,7 @@ def get_difficulty_level_for_user(d1, d2): stemmer = snowballstemmer.stemmer('english') for k in d1: # 用户的词 - if k in d2: # 如果用户的词以原型的形式存在于词库d2中 - continue # 无需评级,跳过 - else: + if k not in d2: # 如果用户的词以原型的形式不存在于词库d2中 stem = stemmer.stemWord(k) if stem in d2: # 如果用户的词的词根存在于词库d2的词根库中 d2[k] = d2[stem] # 按照词根进行评级 @@ -88,6 +85,9 @@ def revert_dict(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 count = 0 geometric = 1 @@ -242,10 +242,7 @@ 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') - s = f.read() - f.close() - - print(text_difficulty_level(s, d3)) - + with open('wordlist.txt', encoding='utf-8') as f: + s = f.read() + print(text_difficulty_level(s, d3)) \ No newline at end of file diff --git a/app/main.py b/app/main.py index 4e3f829..5553994 100644 --- a/app/main.py +++ b/app/main.py @@ -1,17 +1,24 @@ #! /usr/bin/python3 # -*- coding: utf-8 -*- +import os +import random ########################################################################### # 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 glob +from flask import Flask, request, redirect, render_template, url_for, escape +from WordFreq import WordFreq +import pickle_idea +from wordfreqCMD import sort_in_descending_order +from Article import load_freq_history, total_number_of_essays 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!' @@ -51,8 +58,7 @@ def appears_in_test(word, d): ''' if not word in d: return '' - else: - return ','.join(d[word]) + return ','.join(d[word]) @app.route("/mark", methods=['GET', 'POST']) @@ -70,8 +76,8 @@ def mark_word(): 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: # 不回应GET请求 - return 'Under construction' + # 不回应GET请求 + return 'Under construction' @app.route("/", methods=['GET', 'POST']) @@ -91,7 +97,7 @@ def mainpage(): pickle_idea.save_frequency_to_pickle(d, path_prefix + 'static/frequency/frequency.p') 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() number_of_essays = total_number_of_essays() d = load_freq_history(path_prefix + 'static/frequency/frequency.p') diff --git a/app/pickle_idea.py b/app/pickle_idea.py index 45bd19a..3a8d5fa 100644 --- a/app/pickle_idea.py +++ b/app/pickle_idea.py @@ -19,15 +19,15 @@ def lst2dict(lst, d): for x in lst: word = x[0] freq = x[1] - if not word in d: - d[word] = freq + if word not in d: + d[word] = freq else: d[word] += freq def dict2lst(d): return list(d.items()) # a list of (key, value) pairs - + def merge_frequency(lst1, lst2): d = {} @@ -37,29 +37,27 @@ def merge_frequency(lst1, lst2): def load_record(pickle_fname): - f = open(pickle_fname, 'rb') - d = pickle.load(f) - f.close() + with open(pickle_fname, 'rb') as f: + d = pickle.load(f) return d def save_frequency_to_pickle(d, pickle_fname): - f = open(pickle_fname, 'wb') - #exclusion_lst = ['one', 'no', 'has', 'had', 'do', 'that', 'have', 'by', 'not', 'but', 'we', 'this', 'my', 'him', 'so', 'or', 'as', 'are', 'it', 'from', 'with', 'be', 'can', 'for', 'an', 'if', 'who', 'whom', 'whose', 'which', 'the', 'to', 'a', 'of', 'and', 'you', 'i', 'he', 'she', 'they', 'me', 'was', 'were', 'is', 'in', 'at', 'on', 'their', 'his', 'her', 's', 'said', 'all', 'did', 'been', 'w'] - exclusion_lst = [] - d2 = {} - for k in d: - if not k in exclusion_lst and not k.isnumeric() and len(k) > 1: - d2[k] = d[k] - pickle.dump(d2, f) - f.close() + with open(pickle_fname, 'wb') as f: + #exclusion_lst = ['one', 'no', 'has', 'had', 'do', 'that', 'have', 'by', 'not', 'but', 'we', 'this', 'my', 'him', 'so', 'or', 'as', 'are', 'it', 'from', 'with', 'be', 'can', 'for', 'an', 'if', 'who', 'whom', 'whose', 'which', 'the', 'to', 'a', 'of', 'and', 'you', 'i', 'he', 'she', 'they', 'me', 'was', 'were', 'is', 'in', 'at', 'on', 'their', 'his', 'her', 's', 'said', 'all', 'did', 'been', 'w'] + exclusion_lst = [] + d2 = {} + for k in d: + if not k in exclusion_lst and not k.isnumeric() and len(k) > 1: + d2[k] = d[k] + pickle.dump(d2, f) def unfamiliar(path,word): - f = open(path,"rb") - dic = pickle.load(f) + with open(path,"rb") as f: + dic = pickle.load(f) dic[word] += [datetime.now().strftime('%Y%m%d%H%M')] - fp = open(path,"wb") - pickle.dump(dic,fp) + with open(path,"wb") as fp: + pickle.dump(dic,fp) def familiar(path,word): f = open(path,"rb") @@ -68,8 +66,8 @@ def familiar(path,word): del dic[word][0] else: dic.pop(word) - fp = open(path,"wb") - pickle.dump(dic,fp) + with open(path,"wb") as f: + pickle.dump(dic,f) if __name__ == '__main__': diff --git a/app/pickle_idea2.py b/app/pickle_idea2.py index 0da55bc..b34b80b 100644 --- a/app/pickle_idea2.py +++ b/app/pickle_idea2.py @@ -33,7 +33,7 @@ def deleteRecord(path,word): except KeyError: print("sorry") with open(path, 'wb') as ff: - pickle.dump(db, ff) + pickle.dump(db, ff) def dict2lst(d): if len(d) > 0: @@ -43,7 +43,7 @@ def dict2lst(d): for k in d: lst.append((k, [datetime.now().strftime('%Y%m%d%H%M')])) 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 [] @@ -56,21 +56,19 @@ def merge_frequency(lst1, lst2): def load_record(pickle_fname): - f = open(pickle_fname, 'rb') - d = pickle.load(f) - f.close() + with open(pickle_fname, 'rb') as f: + d = pickle.load(f) return d def save_frequency_to_pickle(d, pickle_fname): - f = open(pickle_fname, 'wb') - exclusion_lst = ['one', 'no', 'has', 'had', 'do', 'that', 'have', 'by', 'not', 'but', 'we', 'this', 'my', 'him', 'so', 'or', 'as', 'are', 'it', 'from', 'with', 'be', 'can', 'for', 'an', 'if', 'who', 'whom', 'whose', 'which', 'the', 'to', 'a', 'of', 'and', 'you', 'i', 'he', 'she', 'they', 'me', 'was', 'were', 'is', 'in', 'at', 'on', 'their', 'his', 'her', 's', 'said', 'all', 'did', 'been', 'w'] - 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]))) - pickle.dump(d2, f) - f.close() + with open(pickle_fname, 'wb') as f: + exclusion_lst = ['one', 'no', 'has', 'had', 'do', 'that', 'have', 'by', 'not', 'but', 'we', 'this', 'my', 'him', 'so', 'or', 'as', 'are', 'it', 'from', 'with', 'be', 'can', 'for', 'an', 'if', 'who', 'whom', 'whose', 'which', 'the', 'to', 'a', 'of', 'and', 'you', 'i', 'he', 'she', 'they', 'me', 'was', 'were', 'is', 'in', 'at', 'on', 'their', 'his', 'her', 's', 'said', 'all', 'did', 'been', 'w'] + 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]))) + pickle.dump(d2, f) @@ -86,4 +84,4 @@ if __name__ == '__main__': d = load_record('frequency.p') lst1 = dict2lst(d) d = merge_frequency(lst2, lst1) - print(d) + print(d) \ No newline at end of file diff --git a/app/static/js/fillword.js b/app/static/js/fillword.js index b967633..317373c 100644 --- a/app/static/js/fillword.js +++ b/app/static/js/fillword.js @@ -29,3 +29,7 @@ function onReadClick() { function onChooseClick() { isChoose = !isChoose; } + +if (performance.navigation.type == 1) { //如果网页刷新,停止播放声音 + Reader.stopRead(); +} \ No newline at end of file diff --git a/app/static/js/highlight.js b/app/static/js/highlight.js index 0cea31a..927f698 100644 --- a/app/static/js/highlight.js +++ b/app/static/js/highlight.js @@ -22,33 +22,19 @@ function getWord() { function highLight() { if (!isHighlight) return; - let articleContent = document.getElementById("article").innerText; //将原来的.innerText改为.innerHtml,使用innerText会把原文章中所包含的
标签去除,导致处理后的文章内容失去了原来的格式 + let articleContent = document.getElementById("article").innerText; 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 allWords = ""; //初始化allWords的值,避免进入判断后编译器认为allWords未初始化的问题 - if(dictionaryWords != null){//增加一个判断,检查生词本里面是否为空,如果为空,allWords只添加选中的单词 - allWords = pickedWords.value + " " + dictionaryWords.value; - } - else{ - allWords = pickedWords.value + " "; - } - const list = allWords.split(" ");//将所有的生词放入一个list中,用于后续处理 + let allWords = dictionaryWords === null ? pickedWords.value + " " : pickedWords.value + " " + dictionaryWords.value; + const list = allWords.split(" "); // 将所有的生词放入一个list中,用于后续处理 for (let i = 0; i < list.length; ++i) { - list[i] = list[i].replace(/(^\s*)|(\s*$)/g, ""); //消除单词两边的空字符 - 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] + ""); + list[i] = list[i].replace(/(^\W*)|(\W*$)/g, ""); // 消除单词两边的非单词字符 + if (list[i] != "" && "".indexOf(list[i]) === -1 && "".indexOf(list[i]) === -1) { + // 返回所有匹配单词的集合, 正则表达式RegExp()中, "\b"匹配一个单词的边界, g 表示全局匹配, i 表示对大小写不敏感。 + let matches = new Set(articleContent.match(new RegExp("\\b" + list[i] + "\\b", "gi"))); + for (let word of matches) { + // 将文章中所有出现该单词word的地方改为:"" + word + ""。 + articleContent = articleContent.replace(new RegExp("\\b" + word + "\\b", "g"), "" + word + ""); } } } diff --git a/app/templates/userpage_get.html b/app/templates/userpage_get.html index 68997ef..f7ba8da 100644 --- a/app/templates/userpage_get.html +++ b/app/templates/userpage_get.html @@ -49,8 +49,8 @@ {# #} {# {% endfor %}#} - - + +

阅读文章并回答问题

@@ -97,7 +97,7 @@

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


- +
{% if session.get['thisWord'] %} diff --git a/app/user_service.py b/app/user_service.py index 2e5feed..239887d 100644 --- a/app/user_service.py +++ b/app/user_service.py @@ -1,17 +1,10 @@ from datetime import datetime +from flask import render_template, session, url_for, redirect, request, json, escape, Blueprint from admin_service import ADMIN_NAME -from flask import * - -# from app import Yaml -# from app.Article import get_today_article, load_freq_history -# from app.WordFreq import WordFreq -# from app.wordfreqCMD import sort_in_descending_order - import Yaml from Article import get_today_article, load_freq_history from WordFreq import WordFreq from wordfreqCMD import sort_in_descending_order - import pickle_idea import pickle_idea2 @@ -23,7 +16,7 @@ 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) + user_freq_record = path_prefix + 'static/frequency/' + 'frequency_{username}.pickle' session['old_articleID'] = session.get('articleID') if request.method == 'GET': visited_articles = session.get("visited_articles") @@ -44,7 +37,7 @@ def get_next_article(username): @userService.route("/get_pre_article/",methods=['GET']) def get_pre_article(username): - user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username) + user_freq_record = path_prefix + 'static/frequency/' + f'frequency_{username}.pickle' if request.method == 'GET': visited_articles = session.get("visited_articles") if(visited_articles["index"]==0): @@ -85,7 +78,7 @@ def familiar(username, word): :param word: :return: ''' - user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username) + user_freq_record = path_prefix + 'static/frequency/' + f'frequency_{username}.pickle' pickle_idea.familiar(user_freq_record, word) session['thisWord'] = word # 1. put a word into session session['time'] = 1 @@ -100,7 +93,7 @@ def deleteword(username, word): :param word: 单词 :return: 重定位到用户界面 ''' - user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username) + user_freq_record = path_prefix + 'static/frequency/' + f'frequency_{username}.pickle' 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.') @@ -126,7 +119,7 @@ def userpage(username): # 获取session里的用户名 username = session.get('username') - user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username) + user_freq_record = path_prefix + 'static/frequency/' + f'frequency_{username}.pickle' if request.method == 'POST': # when we submit a form content = escape(request.form['content']) @@ -134,7 +127,7 @@ def userpage(username): lst = f.get_freq() return render_template('userpage_post.html',username=username,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 d = load_freq_history(user_freq_record) lst = pickle_idea2.dict2lst(d) lst2 = [] @@ -167,7 +160,7 @@ def user_mark_word(username): :return: 重定位到用户界面 ''' username = session[username] - user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username) + user_freq_record = path_prefix + 'static/frequency/' + f'frequency_{username}.pickle' if request.method == 'POST': # 提交标记的单词 d = load_freq_history(user_freq_record) @@ -178,13 +171,11 @@ def user_mark_word(username): d = pickle_idea2.merge_frequency(lst, lst_history) pickle_idea2.save_frequency_to_pickle(d, user_freq_record) return redirect(url_for('user_bp.userpage', username=username)) - else: - return 'Under construction' + return 'Under construction' def get_time(): ''' 获取当前时间 :return: 当前时间 ''' - return datetime.now().strftime('%Y%m%d%H%M') # upper to minutes - + return datetime.now().strftime('%Y%m%d%H%M') # upper to minutes \ No newline at end of file diff --git a/app/wordfreqCMD.py b/app/wordfreqCMD.py index dcee74e..c68dc28 100644 --- a/app/wordfreqCMD.py +++ b/app/wordfreqCMD.py @@ -5,8 +5,8 @@ import collections import string -import operator -import os, sys # 引入模块sys,因为我要用里面的sys.argv列表中的信息来读取命令行参数。 +import os +import sys # 引入模块sys,因为我要用里面的sys.argv列表中的信息来读取命令行参数。 import pickle_idea def freq(fruit): @@ -18,7 +18,7 @@ def freq(fruit): ''' result = [] - + fruit = fruit.lower() # 字母转小写 flst = fruit.split() # 字符串转成list c = collections.Counter(flst) @@ -32,19 +32,18 @@ def youdao_link(s): # 有道链接 def file2str(fname):#文件转字符 - f = open(fname) #打开 - s = f.read() #读取 - f.close() #关闭 + with open(fname, encoding="utf-8") as f: + s = f.read() return s def remove_punctuation(s): # 这里是s是形参 (parameter)。函数被调用时才给s赋值。 - special_characters = '\_©~<=>+/[]*&$%^@.,?!:;#()"“”—‘’{}|' # 把里面的字符都去掉 + special_characters = r'\_©~<=>+/[]*&$%^@.,?!:;#()"“”—‘’{}|' # 把里面的字符都去掉 for c in special_characters: s = s.replace(c, ' ') # 防止出现把 apple,apple 移掉逗号后变成 appleapple 情况 s = s.replace('--', ' ') s = s.strip() # 去除前后的空格 - + if '\'' in s: n = len(s) t = '' # 用来收集我需要保留的字符 @@ -78,11 +77,10 @@ def make_html_page(lst, fname): # 只是在wordfreqCMD.py中的main函数中调 count = 1 for x in lst: # word - s += '

%d %s (%d)

' % (count, youdao_link(x[0]), x[0], x[1]) + s += f'

{count} {x[0]} ({x[1]})

' count += 1 - f = open(fname, 'w') - f.write(s) - f.close() + with open(fname, 'w', encoding="utf-8") as f: + f.write(s) ## main(程序入口) @@ -101,10 +99,10 @@ if __name__ == '__main__': s = remove_punctuation(s) # 这里是s是实参(argument),里面有值 L = freq(s) for x in sort_in_descending_order(L): - print('%s\t%d\t%s' % (x[0], x[1], youdao_link(x[0])))#函数导出 + print(f'{x[0]}\t{x[1]}\t{youdao_link(x[0])}' )#函数导出 # 把频率的结果放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') if os.path.exists('frequency.p'): @@ -118,6 +116,3 @@ if __name__ == '__main__': lst_history = pickle_idea.dict2lst(d) d = pickle_idea.merge_frequency(L, lst_history) pickle_idea.save_frequency_to_pickle(d, 'frequency.p') - - -