Merge pull request 'WangXuan-Highlight-Pronounce' (#20) from WangXuan-Highlight-Pronounce into master
Reviewed-on: http://121.4.94.30:3000/mrlan/EnglishPal/pulls/20BugFix347
commit
922e1ee652
|
@ -0,0 +1,137 @@
|
|||
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
|
||||
from difficulty import get_difficulty_level, text_difficulty_level, user_difficulty_level
|
||||
|
||||
|
||||
path_prefix = '/var/www/wordfreq/wordfreq/'
|
||||
path_prefix = './' # comment this line in deployment
|
||||
|
||||
|
||||
def total_number_of_essays():
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("SELECT * FROM article")
|
||||
rq.do()
|
||||
result = rq.get_results()
|
||||
return len(result)
|
||||
|
||||
|
||||
def get_article_title(s):
|
||||
return s.split('\n')[0]
|
||||
|
||||
|
||||
def get_article_body(s):
|
||||
lst = s.split('\n')
|
||||
lst.pop(0) # remove the first line
|
||||
return '\n'.join(lst)
|
||||
|
||||
|
||||
def get_today_article(user_word_list, articleID):
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
if articleID == None:
|
||||
rq.instructions("SELECT * FROM article")
|
||||
else:
|
||||
rq.instructions('SELECT * FROM article WHERE article_id=%d' % (articleID))
|
||||
rq.do()
|
||||
result = rq.get_results()
|
||||
random.shuffle(result)
|
||||
|
||||
# Choose article according to reader's level
|
||||
d1 = load_freq_history(path_prefix + 'static/frequency/frequency.p')
|
||||
d2 = load_freq_history(path_prefix + 'static/words_and_tests.p')
|
||||
d3 = get_difficulty_level(d1, d2)
|
||||
|
||||
d = {}
|
||||
d_user = load_freq_history(user_word_list)
|
||||
user_level = user_difficulty_level(d_user,
|
||||
d3) # more consideration as user's behaviour is dynamic. Time factor should be considered.
|
||||
random.shuffle(result) # shuffle list
|
||||
d = random.choice(result)
|
||||
text_level = text_difficulty_level(d['text'], d3)
|
||||
if articleID == None:
|
||||
for reading in result:
|
||||
text_level = text_difficulty_level(reading['text'], d3)
|
||||
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
|
||||
|
||||
s = '<div class="alert alert-success" role="alert">According to your word list, your level is <span class="badge bg-success">%4.2f</span> and we have chosen an article with a difficulty level of <span class="badge bg-success">%4.2f</span> for you.</div>' % (
|
||||
user_level, text_level)
|
||||
s += '<p class="text-muted">Article added on: %s</p>' % (d['date'])
|
||||
s += '<div class="p-3 mb-2 bg-light text-dark">'
|
||||
article_title = get_article_title(d['text'])
|
||||
article_body = get_article_body(d['text'])
|
||||
s += '<p class="display-3">%s</p>' % (article_title)
|
||||
s += '<p class="lead"><font id="article" size=2>%s</font></p>' % (article_body)
|
||||
s += '<p><small class="text-muted">%s</small></p>' % (d['source'])
|
||||
s += '<p><b>%s</b></p>' % (get_question_part(d['question']))
|
||||
s = s.replace('\n', '<br/>')
|
||||
s += '%s' % (get_answer_part(d['question']))
|
||||
s += '</div>'
|
||||
session['articleID'] = d['article_id']
|
||||
return s
|
||||
|
||||
|
||||
def load_freq_history(path):
|
||||
d = {}
|
||||
if os.path.exists(path):
|
||||
d = pickle_idea.load_record(path)
|
||||
return d
|
||||
|
||||
|
||||
def within_range(x, y, r):
|
||||
return x > y and abs(x - y) <= r
|
||||
|
||||
|
||||
def get_question_part(s):
|
||||
s = s.strip()
|
||||
result = []
|
||||
flag = 0
|
||||
for line in s.split('\n'):
|
||||
line = line.strip()
|
||||
if line == 'QUESTION':
|
||||
result.append(line)
|
||||
flag = 1
|
||||
elif line == 'ANSWER':
|
||||
flag = 0
|
||||
elif flag == 1:
|
||||
result.append(line)
|
||||
return '\n'.join(result)
|
||||
|
||||
|
||||
def get_answer_part(s):
|
||||
s = s.strip()
|
||||
result = []
|
||||
flag = 0
|
||||
for line in s.split('\n'):
|
||||
line = line.strip()
|
||||
if line == 'ANSWER':
|
||||
flag = 1
|
||||
elif flag == 1:
|
||||
result.append(line)
|
||||
# https://css-tricks.com/snippets/javascript/showhide-element/
|
||||
js = '''
|
||||
<script type="text/javascript">
|
||||
|
||||
function toggle_visibility(id) {
|
||||
var e = document.getElementById(id);
|
||||
if(e.style.display == 'block')
|
||||
e.style.display = 'none';
|
||||
else
|
||||
e.style.display = 'block';
|
||||
}
|
||||
</script>
|
||||
'''
|
||||
html_code = js
|
||||
html_code += '\n'
|
||||
html_code += '<button onclick="toggle_visibility(\'answer\');">ANSWER</button>\n'
|
||||
html_code += '<div id="answer" style="display:none;">%s</div>\n' % ('\n'.join(result))
|
||||
return html_code
|
|
@ -0,0 +1,73 @@
|
|||
import hashlib
|
||||
from datetime import datetime
|
||||
|
||||
from UseSqlite import InsertQuery, RecordQuery
|
||||
|
||||
path_prefix = '/var/www/wordfreq/wordfreq/'
|
||||
path_prefix = './' # comment this line in deployment
|
||||
|
||||
|
||||
def verify_user(username, password):
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
password = md5(username + password)
|
||||
rq.instructions_with_parameters("SELECT * FROM user WHERE name=? AND password=?", (username, password))
|
||||
rq.do_with_parameters()
|
||||
result = rq.get_results()
|
||||
return result != []
|
||||
|
||||
|
||||
def add_user(username, password):
|
||||
start_date = datetime.now().strftime('%Y%m%d')
|
||||
expiry_date = '20221230'
|
||||
# 将用户名和密码一起加密,以免暴露不同用户的相同密码
|
||||
password = md5(username + password)
|
||||
rq = InsertQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("INSERT INTO user VALUES ('%s', '%s', '%s', '%s')" % (username, password, start_date, expiry_date))
|
||||
rq.do()
|
||||
|
||||
|
||||
def check_username_availability(username):
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("SELECT * FROM user WHERE name='%s'" % (username))
|
||||
rq.do()
|
||||
result = rq.get_results()
|
||||
return result == []
|
||||
|
||||
|
||||
def change_password(username, old_password, new_password):
|
||||
'''
|
||||
修改密码
|
||||
:param username: 用户名
|
||||
:param old_password: 旧的密码
|
||||
:param new_password: 新密码
|
||||
:return: 修改成功:True 否则:False
|
||||
'''
|
||||
if not verify_user(username, old_password): # 旧密码错误
|
||||
return False
|
||||
# 将用户名和密码一起加密,以免暴露不同用户的相同密码
|
||||
password = md5(username + new_password)
|
||||
rq = InsertQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("UPDATE user SET password = '%s' WHERE name = '%s'" % (password, username))
|
||||
rq.do()
|
||||
return True
|
||||
|
||||
|
||||
def get_expiry_date(username):
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("SELECT expiry_date FROM user WHERE name='%s'" % (username))
|
||||
rq.do()
|
||||
result = rq.get_results()
|
||||
if len(result) > 0:
|
||||
return result[0]['expiry_date']
|
||||
else:
|
||||
return '20191024'
|
||||
|
||||
|
||||
def md5(s):
|
||||
'''
|
||||
MD5摘要
|
||||
:param str: 字符串
|
||||
:return: 经MD5以后的字符串
|
||||
'''
|
||||
h = hashlib.md5(s.encode(encoding='utf-8'))
|
||||
return h.hexdigest()
|
174
app/UseSqlite.py
174
app/UseSqlite.py
|
@ -1,87 +1,87 @@
|
|||
###########################################################################
|
||||
# Copyright 2019 (C) Hui Lan <hui.lan@cantab.net>
|
||||
# Written permission must be obtained from the author for commercial uses.
|
||||
###########################################################################
|
||||
|
||||
|
||||
# Reference: Dusty Phillips. Python 3 Objected-oriented Programming Second Edition. Pages 326-328.
|
||||
# Copyright (C) 2019 Hui Lan
|
||||
|
||||
import sqlite3
|
||||
|
||||
class Sqlite3Template:
|
||||
def __init__(self, db_fname):
|
||||
self.db_fname = db_fname
|
||||
|
||||
def connect(self, db_fname):
|
||||
self.conn = sqlite3.connect(self.db_fname)
|
||||
|
||||
def instructions(self, query_statement):
|
||||
raise NotImplementedError()
|
||||
|
||||
def operate(self):
|
||||
self.conn.row_factory = sqlite3.Row
|
||||
self.results = self.conn.execute(self.query) # self.query is to be given in the child classes
|
||||
self.conn.commit()
|
||||
|
||||
def format_results(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def do(self):
|
||||
self.connect(self.db_fname)
|
||||
self.instructions(self.query)
|
||||
self.operate()
|
||||
|
||||
def instructions_with_parameters(self, query_statement, parameters):
|
||||
self.query = query_statement
|
||||
self.parameters = parameters
|
||||
|
||||
def do_with_parameters(self):
|
||||
self.connect(self.db_fname)
|
||||
self.instructions_with_parameters(self.query, self.parameters)
|
||||
self.operate_with_parameters()
|
||||
|
||||
def operate_with_parameters(self):
|
||||
self.conn.row_factory = sqlite3.Row
|
||||
self.results = self.conn.execute(self.query, self.parameters) # self.query is to be given in the child classes
|
||||
self.conn.commit()
|
||||
|
||||
|
||||
class InsertQuery(Sqlite3Template):
|
||||
def instructions(self, query):
|
||||
self.query = query
|
||||
|
||||
|
||||
class RecordQuery(Sqlite3Template):
|
||||
def instructions(self, query):
|
||||
self.query = query
|
||||
|
||||
def format_results(self):
|
||||
output = []
|
||||
for row_dict in self.results.fetchall():
|
||||
lst = []
|
||||
for k in dict(row_dict):
|
||||
lst.append( row_dict[k] )
|
||||
output.append(', '.join(lst))
|
||||
return '\n\n'.join(output)
|
||||
|
||||
def get_results(self):
|
||||
result = []
|
||||
for row_dict in self.results.fetchall():
|
||||
result.append( dict(row_dict) )
|
||||
return result
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
#iq = InsertQuery('RiskDB.db')
|
||||
#iq.instructions("INSERT INTO inspection Values ('FoodSupplies', 'RI2019051301', '2019-05-13', '{}')")
|
||||
#iq.do()
|
||||
#iq.instructions("INSERT INTO inspection Values ('CarSupplies', 'RI2019051302', '2019-05-13', '{[{\"risk_name\":\"elevator\"}]}')")
|
||||
#iq.do()
|
||||
|
||||
rq = RecordQuery('wordfreqapp.db')
|
||||
rq.instructions("SELECT * FROM article WHERE level=3")
|
||||
rq.do()
|
||||
#print(rq.format_results())
|
||||
###########################################################################
|
||||
# Copyright 2019 (C) Hui Lan <hui.lan@cantab.net>
|
||||
# Written permission must be obtained from the author for commercial uses.
|
||||
###########################################################################
|
||||
|
||||
|
||||
# Reference: Dusty Phillips. Python 3 Objected-oriented Programming Second Edition. Pages 326-328.
|
||||
# Copyright (C) 2019 Hui Lan
|
||||
|
||||
import sqlite3
|
||||
|
||||
class Sqlite3Template:
|
||||
def __init__(self, db_fname):
|
||||
self.db_fname = db_fname
|
||||
|
||||
def connect(self, db_fname):
|
||||
self.conn = sqlite3.connect(self.db_fname)
|
||||
|
||||
def instructions(self, query_statement):
|
||||
raise NotImplementedError()
|
||||
|
||||
def operate(self):
|
||||
self.conn.row_factory = sqlite3.Row
|
||||
self.results = self.conn.execute(self.query) # self.query is to be given in the child classes
|
||||
self.conn.commit()
|
||||
|
||||
def format_results(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def do(self):
|
||||
self.connect(self.db_fname)
|
||||
self.instructions(self.query)
|
||||
self.operate()
|
||||
|
||||
def instructions_with_parameters(self, query_statement, parameters):
|
||||
self.query = query_statement
|
||||
self.parameters = parameters
|
||||
|
||||
def do_with_parameters(self):
|
||||
self.connect(self.db_fname)
|
||||
self.instructions_with_parameters(self.query, self.parameters)
|
||||
self.operate_with_parameters()
|
||||
|
||||
def operate_with_parameters(self):
|
||||
self.conn.row_factory = sqlite3.Row
|
||||
self.results = self.conn.execute(self.query, self.parameters) # self.query is to be given in the child classes
|
||||
self.conn.commit()
|
||||
|
||||
|
||||
class InsertQuery(Sqlite3Template):
|
||||
def instructions(self, query):
|
||||
self.query = query
|
||||
|
||||
|
||||
class RecordQuery(Sqlite3Template):
|
||||
def instructions(self, query):
|
||||
self.query = query
|
||||
|
||||
def format_results(self):
|
||||
output = []
|
||||
for row_dict in self.results.fetchall():
|
||||
lst = []
|
||||
for k in dict(row_dict):
|
||||
lst.append( row_dict[k] )
|
||||
output.append(', '.join(lst))
|
||||
return '\n\n'.join(output)
|
||||
|
||||
def get_results(self):
|
||||
result = []
|
||||
for row_dict in self.results.fetchall():
|
||||
result.append( dict(row_dict) )
|
||||
return result
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
#iq = InsertQuery('RiskDB.db')
|
||||
#iq.instructions("INSERT INTO inspection Values ('FoodSupplies', 'RI2019051301', '2019-05-13', '{}')")
|
||||
#iq.do()
|
||||
#iq.instructions("INSERT INTO inspection Values ('CarSupplies', 'RI2019051302', '2019-05-13', '{[{\"risk_name\":\"elevator\"}]}')")
|
||||
#iq.do()
|
||||
|
||||
rq = RecordQuery('wordfreqapp.db')
|
||||
rq.instructions("SELECT * FROM article WHERE level=3")
|
||||
rq.do()
|
||||
#print(rq.format_results())
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
'''
|
||||
Yaml.py
|
||||
配置文件包括:
|
||||
./static/config.yml
|
||||
./layout/partial/header.html
|
||||
./layout/partial/footer.html
|
||||
'''
|
||||
import yaml as YAML
|
||||
import os
|
||||
|
||||
path_prefix = './' # comment this line in deployment
|
||||
|
||||
# YAML文件路径
|
||||
ymlPath = path_prefix + 'static/config.yml'
|
||||
|
||||
# partial文件夹路径
|
||||
partialPath = path_prefix + 'layout/partial/'
|
||||
f = open(ymlPath, 'r', encoding='utf-8') # 以'UTF-8'格式打开YAML文件
|
||||
cont = f.read() # 以文本形式读取YAML
|
||||
|
||||
yml = YAML.load(cont, Loader=YAML.FullLoader) # 加载YAML
|
||||
|
||||
with open(partialPath + 'header.html', 'r', encoding='utf-8') as f:
|
||||
yml['header'] = f.read() # header内的文本会被直接添加到所有页面的head标签内
|
||||
|
||||
with open(partialPath + 'footer.html', 'r', encoding='utf-8') as f:
|
||||
yml['footer'] = f.read() # footer内的文本会被直接添加到所有页面的最底部
|
|
@ -0,0 +1,129 @@
|
|||
from flask import *
|
||||
from Login import check_username_availability, verify_user, add_user, get_expiry_date, change_password
|
||||
|
||||
# 初始化蓝图
|
||||
accountService = Blueprint("accountService", __name__)
|
||||
|
||||
|
||||
### Sign-up, login, logout ###
|
||||
@accountService.route("/signup", methods=['GET', 'POST'])
|
||||
def signup():
|
||||
'''
|
||||
注册
|
||||
:return: 根据注册是否成功返回不同界面
|
||||
'''
|
||||
if request.method == 'GET':
|
||||
# GET方法直接返回注册页面
|
||||
return render_template('signup.html')
|
||||
elif request.method == 'POST':
|
||||
# POST方法需判断是否注册成功,再根据结果返回不同的内容
|
||||
username = request.form['username']
|
||||
password = request.form['password']
|
||||
|
||||
available = check_username_availability(username)
|
||||
if not available: # 用户名不可用
|
||||
flash('用户名 %s 已经被注册。' % (username))
|
||||
return render_template('signup.html')
|
||||
elif len(password.strip()) < 4: # 密码过短
|
||||
return '密码过于简单。'
|
||||
else: # 添加账户信息
|
||||
add_user(username, password)
|
||||
verified = verify_user(username, password)
|
||||
if verified:
|
||||
# 写入session
|
||||
session['logged_in'] = True
|
||||
session[username] = username
|
||||
session['username'] = username
|
||||
session['expiry_date'] = get_expiry_date(username)
|
||||
session['articleID'] = None
|
||||
return '<p>恭喜,你已成功注册, 你的用户名是 <a href="%s">%s</a>。</p>\
|
||||
<p><a href="/%s">开始使用</a> <a href="/">返回首页</a><p/>' % (username, username, username)
|
||||
else:
|
||||
return '用户名密码验证失败。'
|
||||
|
||||
|
||||
@accountService.route("/login", methods=['GET', 'POST'])
|
||||
def login():
|
||||
'''
|
||||
登录
|
||||
:return: 根据登录是否成功返回不同页面
|
||||
'''
|
||||
if request.method == 'GET':
|
||||
# GET请求
|
||||
if not session.get('logged_in'):
|
||||
# 未登录,返回登录页面
|
||||
return render_template('login.html')
|
||||
else:
|
||||
# 已登录,提示信息并显示登出按钮
|
||||
return '你已登录 <a href="/%s">%s</a>。 登出点击<a href="/logout">这里</a>。' % (
|
||||
session['username'], session['username'])
|
||||
elif request.method == 'POST':
|
||||
# POST方法用于判断登录是否成功
|
||||
# check database and verify user
|
||||
username = request.form['username']
|
||||
password = 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['articleID'] = None
|
||||
return redirect(url_for('user_bp.userpage', username=username))
|
||||
else:
|
||||
return '无法通过验证。'
|
||||
|
||||
|
||||
@accountService.route("/logout", methods=['GET', 'POST'])
|
||||
def logout():
|
||||
'''
|
||||
登出
|
||||
:return: 重定位到主界面
|
||||
'''
|
||||
# 将session标记为登出状态
|
||||
session['logged_in'] = False
|
||||
return redirect(url_for('mainpage'))
|
||||
|
||||
|
||||
@accountService.route("/reset", methods=['GET', 'POST'])
|
||||
def reset():
|
||||
'''
|
||||
重设密码
|
||||
:return: 返回适当的页面
|
||||
'''
|
||||
# 下列方法用于防止未登录状态下的修改密码
|
||||
if not session.get('logged_in'):
|
||||
return render_template('login.html')
|
||||
username = session['username']
|
||||
if username == '':
|
||||
return redirect('/login')
|
||||
if request.method == 'GET':
|
||||
# GET请求返回修改密码页面
|
||||
return render_template('reset.html', username=session['username'], state='wait')
|
||||
else:
|
||||
# POST请求用于提交修改后信息
|
||||
old_psd = request.form['old-psd']
|
||||
new_psd = request.form['new-psd']
|
||||
flag = change_password(username, old_psd, new_psd) # flag表示是否修改成功
|
||||
if flag:
|
||||
session['logged_in'] = False
|
||||
return \
|
||||
'''
|
||||
<script>
|
||||
alert('修改密码成功!!!请重新登录');
|
||||
window.location.href="/login";
|
||||
</script>
|
||||
|
||||
'''
|
||||
|
||||
else:
|
||||
return \
|
||||
'''
|
||||
<script>
|
||||
alert('修改密码失败!!!');
|
||||
window.location.href="/reset";
|
||||
</script>
|
||||
|
||||
'''
|
|
@ -0,0 +1,21 @@
|
|||
<!--
|
||||
以html的形式插入到所有页面的页尾
|
||||
-->
|
||||
<!--
|
||||
<br/><br/><br/>
|
||||
<div class="aplayer-container">
|
||||
<meting-js
|
||||
theme='#1BCDFC'
|
||||
autoplay='false'
|
||||
volume='0.5'
|
||||
loop='all'
|
||||
order='list'
|
||||
fixed='true'
|
||||
list-max-height='160px'
|
||||
server='netease'
|
||||
type='playlist'
|
||||
id='6927872292'
|
||||
list-folded='true'>
|
||||
</meting-js>
|
||||
</div>
|
||||
-->
|
|
@ -0,0 +1,3 @@
|
|||
<!--
|
||||
以html的形式插入所有网页的head标签中
|
||||
-->
|
612
app/main.py
612
app/main.py
|
@ -1,498 +1,114 @@
|
|||
#! /usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
###########################################################################
|
||||
# Copyright 2019 (C) Hui Lan <hui.lan@cantab.net>
|
||||
# Written permission must be obtained from the author for commercial uses.
|
||||
###########################################################################
|
||||
|
||||
from WordFreq import WordFreq
|
||||
from wordfreqCMD import youdao_link, sort_in_descending_order
|
||||
from UseSqlite import InsertQuery, RecordQuery
|
||||
import pickle_idea, pickle_idea2
|
||||
import os
|
||||
import random, glob
|
||||
from datetime import datetime
|
||||
from flask import Flask, request, redirect, render_template, url_for, session, abort, flash, get_flashed_messages
|
||||
from difficulty import get_difficulty_level, text_difficulty_level, user_difficulty_level
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = 'lunch.time!'
|
||||
|
||||
path_prefix = '/var/www/wordfreq/wordfreq/'
|
||||
path_prefix = './' # comment this line in deployment
|
||||
|
||||
def get_random_image(path):
|
||||
img_path = random.choice(glob.glob(os.path.join(path, '*.jpg')))
|
||||
return img_path[img_path.rfind('/static'):]
|
||||
|
||||
def get_random_ads():
|
||||
ads = random.choice(['个性化分析精准提升', '你的专有单词本', '智能捕捉阅读弱点,针对性提高你的阅读水平'])
|
||||
return ads + '。 <a href="/signup">试试</a>吧!'
|
||||
|
||||
def total_number_of_essays():
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("SELECT * FROM article")
|
||||
rq.do()
|
||||
result = rq.get_results()
|
||||
return len(result)
|
||||
|
||||
def load_freq_history(path):
|
||||
d = {}
|
||||
if os.path.exists(path):
|
||||
d = pickle_idea.load_record(path)
|
||||
return d
|
||||
|
||||
def verify_user(username, password):
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions_with_parameters("SELECT * FROM user WHERE name=? AND password=?", (username, password))
|
||||
rq.do_with_parameters()
|
||||
result = rq.get_results()
|
||||
return result != []
|
||||
|
||||
def add_user(username, password):
|
||||
start_date = datetime.now().strftime('%Y%m%d')
|
||||
expiry_date = '20211230'
|
||||
rq = InsertQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("INSERT INTO user Values ('%s', '%s', '%s', '%s')" % (username, password, start_date, expiry_date))
|
||||
rq.do()
|
||||
|
||||
|
||||
def check_username_availability(username):
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("SELECT * FROM user WHERE name='%s'" % (username))
|
||||
rq.do()
|
||||
result = rq.get_results()
|
||||
return result == []
|
||||
|
||||
def get_expiry_date(username):
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("SELECT expiry_date FROM user WHERE name='%s'" % (username))
|
||||
rq.do()
|
||||
result = rq.get_results()
|
||||
if len(result) > 0:
|
||||
return result[0]['expiry_date']
|
||||
else:
|
||||
return '20191024'
|
||||
|
||||
|
||||
|
||||
def within_range(x, y, r):
|
||||
return x > y and abs(x - y) <= r
|
||||
|
||||
|
||||
def get_article_title(s):
|
||||
return s.split('\n')[0]
|
||||
|
||||
|
||||
def get_article_body(s):
|
||||
lst = s.split('\n')
|
||||
lst.pop(0) # remove the first line
|
||||
return '\n'.join(lst)
|
||||
|
||||
def get_today_article(user_word_list, articleID):
|
||||
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
if articleID == None:
|
||||
rq.instructions("SELECT * FROM article")
|
||||
else:
|
||||
rq.instructions('SELECT * FROM article WHERE article_id=%d' % (articleID))
|
||||
rq.do()
|
||||
result = rq.get_results()
|
||||
random.shuffle(result)
|
||||
|
||||
# Choose article according to reader's level
|
||||
d1 = load_freq_history(path_prefix + 'static/frequency/frequency.p')
|
||||
d2 = load_freq_history(path_prefix + 'static/words_and_tests.p')
|
||||
d3 = get_difficulty_level(d1, d2)
|
||||
|
||||
d = {}
|
||||
d_user = load_freq_history(user_word_list)
|
||||
user_level = user_difficulty_level(d_user, d3) # more consideration as user's behaviour is dynamic. Time factor should be considered.
|
||||
random.shuffle(result) # shuffle list
|
||||
d = random.choice(result)
|
||||
text_level = text_difficulty_level(d['text'], d3)
|
||||
if articleID == None:
|
||||
for reading in result:
|
||||
text_level = text_difficulty_level(reading['text'], d3)
|
||||
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
|
||||
|
||||
s = '<div class="alert alert-success" role="alert">According to your word list, your level is <span class="badge bg-success">%4.2f</span> and we have chosen an article with a difficulty level of <span class="badge bg-success">%4.2f</span> for you.</div>' % (user_level, text_level)
|
||||
s += '<p class="text-muted">Article added on: %s</p>' % (d['date'])
|
||||
s += '<div class="p-3 mb-2 bg-light text-dark">'
|
||||
article_title = get_article_title(d['text'])
|
||||
article_body = get_article_body(d['text'])
|
||||
s += '<p class="display-3">%s</p>' % (article_title)
|
||||
s += '<p class="lead">%s</p>' % (article_body)
|
||||
s += '<p><small class="text-muted">%s</small></p>' % (d['source'])
|
||||
s += '<p><b>%s</b></p>' % (get_question_part(d['question']))
|
||||
s = s.replace('\n', '<br/>')
|
||||
s += '%s' % (get_answer_part(d['question']))
|
||||
s += '</div>'
|
||||
session['articleID'] = d['article_id']
|
||||
return s
|
||||
|
||||
|
||||
def appears_in_test(word, d):
|
||||
if not word in d:
|
||||
return ''
|
||||
else:
|
||||
return ','.join(d[word])
|
||||
|
||||
|
||||
def get_time():
|
||||
return datetime.now().strftime('%Y%m%d%H%M') # upper to minutes
|
||||
|
||||
|
||||
def get_question_part(s):
|
||||
s = s.strip()
|
||||
result = []
|
||||
flag = 0
|
||||
for line in s.split('\n'):
|
||||
line = line.strip()
|
||||
if line == 'QUESTION':
|
||||
result.append(line)
|
||||
flag = 1
|
||||
elif line == 'ANSWER':
|
||||
flag = 0
|
||||
elif flag == 1:
|
||||
result.append(line)
|
||||
return '\n'.join(result)
|
||||
|
||||
|
||||
def get_answer_part(s):
|
||||
s = s.strip()
|
||||
result = []
|
||||
flag = 0
|
||||
for line in s.split('\n'):
|
||||
line = line.strip()
|
||||
if line == 'ANSWER':
|
||||
flag = 1
|
||||
elif flag == 1:
|
||||
result.append(line)
|
||||
# https://css-tricks.com/snippets/javascript/showhide-element/
|
||||
js = '''
|
||||
<script type="text/javascript">
|
||||
|
||||
function toggle_visibility(id) {
|
||||
var e = document.getElementById(id);
|
||||
if(e.style.display == 'block')
|
||||
e.style.display = 'none';
|
||||
else
|
||||
e.style.display = 'block';
|
||||
}
|
||||
</script>
|
||||
'''
|
||||
html_code = js
|
||||
html_code += '\n'
|
||||
html_code += '<button onclick="toggle_visibility(\'answer\');">ANSWER</button>\n'
|
||||
html_code += '<div id="answer" style="display:none;">%s</div>\n' % ('\n'.join(result))
|
||||
return html_code
|
||||
|
||||
|
||||
def get_flashed_messages_if_any():
|
||||
messages = get_flashed_messages()
|
||||
s = ''
|
||||
for message in messages:
|
||||
s += '<div class="alert alert-warning" role="alert">'
|
||||
s += f'Congratulations! {message}'
|
||||
s += '</div>'
|
||||
return s
|
||||
|
||||
|
||||
@app.route("/<username>/reset", methods=['GET', 'POST'])
|
||||
def user_reset(username):
|
||||
if request.method == 'GET':
|
||||
session['articleID'] = None
|
||||
return redirect(url_for('userpage', username=username))
|
||||
else:
|
||||
return 'Under construction'
|
||||
|
||||
|
||||
@app.route("/mark", methods=['GET', 'POST'])
|
||||
def mark_word():
|
||||
if request.method == 'POST':
|
||||
d = load_freq_history(path_prefix + 'static/frequency/frequency.p')
|
||||
lst_history = pickle_idea.dict2lst(d)
|
||||
lst = []
|
||||
for word in request.form.getlist('marked'):
|
||||
lst.append((word, 1))
|
||||
d = pickle_idea.merge_frequency(lst, lst_history)
|
||||
pickle_idea.save_frequency_to_pickle(d, path_prefix + 'static/frequency/frequency.p')
|
||||
return redirect(url_for('mainpage'))
|
||||
else:
|
||||
return 'Under construction'
|
||||
|
||||
|
||||
|
||||
@app.route("/", methods=['GET', 'POST'])
|
||||
def mainpage():
|
||||
if request.method == 'POST': # when we submit a form
|
||||
content = request.form['content']
|
||||
f = WordFreq(content)
|
||||
lst = f.get_freq()
|
||||
page = '<form method="post" action="/mark">\n'
|
||||
count = 1
|
||||
for x in lst:
|
||||
page += '<p><font color="grey">%d</font>: <a href="%s">%s</a> (%d) <input type="checkbox" name="marked" value="%s"></p>\n' % (count, youdao_link(x[0]), x[0], x[1], x[0])
|
||||
count += 1
|
||||
page += ' <input type="submit" value="确定并返回"/>\n'
|
||||
page += '</form>\n'
|
||||
# save history
|
||||
d = load_freq_history(path_prefix + 'static/frequency/frequency.p')
|
||||
lst_history = pickle_idea.dict2lst(d)
|
||||
d = pickle_idea.merge_frequency(lst, lst_history)
|
||||
pickle_idea.save_frequency_to_pickle(d, path_prefix + 'static/frequency/frequency.p')
|
||||
|
||||
return page
|
||||
elif request.method == 'GET': # when we load a html page
|
||||
page = '''
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
|
||||
<title>EnglishPal 英文单词高效记</title>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
'''
|
||||
page += '<div class="container-fluid">'
|
||||
page += '<p><b><font size="+3" color="red">English Pal - Learn English smartly!</font></b></p>'
|
||||
if session.get('logged_in'):
|
||||
page += ' <a href="%s">%s</a></p>\n' % (session['username'], session['username'])
|
||||
else:
|
||||
page += '<p><a href="/login">登录</a> <a href="/signup">成为会员</a> <a href="/static/usr/instructions.html">使用说明</a></p>\n'
|
||||
#page += '<p><img src="%s" width="400px" alt="advertisement"/></p>' % (get_random_image(path_prefix + 'static/img/'))
|
||||
page += '<p><b>%s</b></p>' % (get_random_ads())
|
||||
page += '<div class="alert alert-success" role="alert">共有文章 <span class="badge bg-success"> %d </span> 篇</div>' % (total_number_of_essays())
|
||||
page += '<p>粘帖1篇文章 (English only)</p>'
|
||||
page += '<form method="post" action="/">'
|
||||
page += ' <textarea name="content" rows="10" cols="120"></textarea><br/>'
|
||||
page += ' <input type="submit" value="get文章中的词频"/>'
|
||||
page += ' <input type="reset" value="清除"/>'
|
||||
page += '</form>'
|
||||
d = load_freq_history(path_prefix + 'static/frequency/frequency.p')
|
||||
if len(d) > 0:
|
||||
page += '<p><b>最常见的词</b></p>'
|
||||
for x in sort_in_descending_order(pickle_idea.dict2lst(d)):
|
||||
if x[1] <= 99:
|
||||
break
|
||||
page += '<a href="%s">%s</a> %d\n' % (youdao_link(x[0]), x[0], x[1])
|
||||
|
||||
page += ' <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>'
|
||||
page += '</div>'
|
||||
page += '</body></html>'
|
||||
return page
|
||||
|
||||
|
||||
@app.route("/<username>/mark", methods=['GET', 'POST'])
|
||||
def user_mark_word(username):
|
||||
username = session[username]
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
if request.method == 'POST':
|
||||
d = load_freq_history(user_freq_record)
|
||||
lst_history = pickle_idea2.dict2lst(d)
|
||||
lst = []
|
||||
for word in request.form.getlist('marked'):
|
||||
lst.append((word, [get_time()]))
|
||||
d = pickle_idea2.merge_frequency(lst, lst_history)
|
||||
pickle_idea2.save_frequency_to_pickle(d, user_freq_record)
|
||||
return redirect(url_for('userpage', username=username))
|
||||
else:
|
||||
return 'Under construction'
|
||||
|
||||
|
||||
@app.route("/<username>/<word>/unfamiliar", methods=['GET', 'POST'])
|
||||
def unfamiliar(username,word):
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
pickle_idea.unfamiliar(user_freq_record,word)
|
||||
session['thisWord'] = word # 1. put a word into session
|
||||
session['time'] = 1
|
||||
return redirect(url_for('userpage', username=username))
|
||||
|
||||
@app.route("/<username>/<word>/familiar", methods=['GET', 'POST'])
|
||||
def familiar(username,word):
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
pickle_idea.familiar(user_freq_record,word)
|
||||
session['thisWord'] = word # 1. put a word into session
|
||||
session['time'] = 1
|
||||
return redirect(url_for('userpage', username=username))
|
||||
|
||||
@app.route("/<username>/<word>/del", methods=['GET', 'POST'])
|
||||
def deleteword(username,word):
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
pickle_idea2.deleteRecord(user_freq_record,word)
|
||||
flash(f'<strong>{word}</strong> is no longer in your word list.')
|
||||
return redirect(url_for('userpage', username=username))
|
||||
|
||||
@app.route("/<username>", methods=['GET', 'POST'])
|
||||
def userpage(username):
|
||||
|
||||
if not session.get('logged_in'):
|
||||
return '<p>请先<a href="/login">登录</a>。</p>'
|
||||
|
||||
user_expiry_date = session.get('expiry_date')
|
||||
if datetime.now().strftime('%Y%m%d') > user_expiry_date:
|
||||
return '<p>账号 %s 过期。</p><p>为了提高服务质量,English Pal 收取会员费用, 每天0元。</p> <p>请决定你要试用的时间长度,扫描下面支付宝二维码支付。 支付时请注明<i>English Pal Membership Fee</i>。 我们会于12小时内激活账号。</p><p><img src="static/donate-the-author-hidden.jpg" width="120px" alt="支付宝二维码" /></p><p>如果有问题,请加开发者微信 torontohui。</p> <p><a href="/logout">登出</a></p>' % (username)
|
||||
|
||||
|
||||
username = session.get('username')
|
||||
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
|
||||
if request.method == 'POST': # when we submit a form
|
||||
content = request.form['content']
|
||||
f = WordFreq(content)
|
||||
lst = f.get_freq()
|
||||
page = '<meta charset="UTF8">'
|
||||
page += '<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />'
|
||||
page += '<p>勾选不认识的单词</p>'
|
||||
page += '<form method="post" action="/%s/mark">\n' % (username)
|
||||
page += ' <input type="submit" name="add-btn" value="加入我的生词簿"/>\n'
|
||||
count = 1
|
||||
words_tests_dict = pickle_idea.load_record(path_prefix + 'static/words_and_tests.p')
|
||||
for x in lst:
|
||||
page += '<p><font color="grey">%d</font>: <a href="%s" title="%s">%s</a> (%d) <input type="checkbox" name="marked" value="%s"></p>\n' % (count, youdao_link(x[0]), appears_in_test(x[0], words_tests_dict), x[0], x[1], x[0])
|
||||
count += 1
|
||||
page += '</form>\n'
|
||||
return page
|
||||
|
||||
elif request.method == 'GET': # when we load a html page
|
||||
page = '<meta charset="UTF8">\n'
|
||||
page += '<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />\n'
|
||||
page += '<meta name="format-detection" content="telephone=no" />\n' # forbid treating numbers as cell numbers in smart phones
|
||||
page += '<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">'
|
||||
page += '<title>EnglishPal Study Room for %s</title>' % (username)
|
||||
page += '<div class="container-fluid">'
|
||||
page += '<p><b>English Pal for <font color="red">%s</font></b> <a class="btn btn-secondary" href="/logout" role="button">登出</a></p>' % (username)
|
||||
page += get_flashed_messages_if_any()
|
||||
page += '<p><b>阅读文章并回答问题</b></p>\n'
|
||||
page += '<p><a class="btn btn-success" href="/%s/reset" role="button"> 下一篇 Next Article </a></p>' % (username)
|
||||
page += '<div id="text-content">%s</div>' % (get_today_article(user_freq_record, session['articleID']))
|
||||
page += '<p><b>收集生词吧</b> (可以在正文中划词,也可以复制黏贴)</p>'
|
||||
page += '<form method="post" action="/%s">' % (username)
|
||||
page += ' <textarea name="content" id="selected-words" rows="10" cols="120"></textarea><br/>'
|
||||
page += ' <input type="submit" value="get 所有词的频率"/>'
|
||||
page += ' <input type="reset" value="清除"/>'
|
||||
page += '</form>\n'
|
||||
page += '''
|
||||
<script>
|
||||
function getWord(){
|
||||
var word = window.getSelection?window.getSelection():document.selection.createRange().text;
|
||||
return word;
|
||||
}
|
||||
function fillinWord(){
|
||||
var element = document.getElementById("selected-words");
|
||||
element.value = element.value + " " + getWord();
|
||||
}
|
||||
document.getElementById("text-content").addEventListener("click", fillinWord, false);
|
||||
document.getElementById("text-content").addEventListener("touchstart", fillinWord, false);
|
||||
</script>
|
||||
'''
|
||||
if session.get('thisWord'):
|
||||
page += '''
|
||||
<script type="text/javascript">
|
||||
//point to the anchor in the page whose id is aaa if it exists
|
||||
window.onload = function(){
|
||||
var element = document.getElementsByName("aaa");
|
||||
if (element != null)
|
||||
document.getElementsByName("aaa")[0].scrollIntoView(true);
|
||||
}
|
||||
</script>
|
||||
'''
|
||||
|
||||
d = load_freq_history(user_freq_record)
|
||||
if len(d) > 0:
|
||||
page += '<p><b>我的生词簿</b></p>'
|
||||
lst = pickle_idea2.dict2lst(d)
|
||||
lst2 = []
|
||||
for t in lst:
|
||||
lst2.append((t[0], len(t[1])))
|
||||
for x in sort_in_descending_order(lst2):
|
||||
word = x[0]
|
||||
freq = x[1]
|
||||
if session.get('thisWord') == x[0] and session.get('time') == 1:
|
||||
page += '<a name="aaa"></a>' # 3. anchor
|
||||
session['time'] = 0 # discard anchor
|
||||
if isinstance(d[word], list): # d[word] is a list of dates
|
||||
if freq > 1:
|
||||
page += '<p class="new-word"> <a class="btn btn-light" href="%s" role="button">%s</a>(<a title="%s">%d</a>) <a class="btn btn-success" href="%s/%s/familiar" role="button">熟悉</a> <a class="btn btn-warning" href="%s/%s/unfamiliar" role="button">不熟悉</a> <a class="btn btn-danger" href="%s/%s/del" role="button">删除</a> </p>\n' % (youdao_link(word), word, '; '.join(d[word]), freq,username, word,username,word, username,word)
|
||||
else:
|
||||
page += '<p class="new-word"> <a class="btn btn-light" href="%s" role="button">%s</a>(<a title="%s">%d</a>) <a class="btn btn-success" href="%s/%s/familiar" role="button">熟悉</a> <a class="btn btn-warning" href="%s/%s/unfamiliar" role="button">不熟悉</a> <a class="btn btn-danger" href="%s/%s/del" role="button">删除</a> </p>\n' % (youdao_link(word), word, '; '.join(d[word]), freq,username, word,username,word, username,word)
|
||||
elif isinstance(d[word], int): # d[word] is a frequency. to migrate from old format.
|
||||
page += '<a href="%s">%s</a>%d\n' % (youdao_link(word), word, freq)
|
||||
page += '<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>'
|
||||
page += '</div>'
|
||||
return page
|
||||
|
||||
### Sign-up, login, logout ###
|
||||
@app.route("/signup", methods=['GET', 'POST'])
|
||||
def signup():
|
||||
if request.method == 'GET':
|
||||
return render_template('signup.html')
|
||||
elif request.method == 'POST':
|
||||
username = request.form['username']
|
||||
password = request.form['password']
|
||||
|
||||
available = check_username_availability(username)
|
||||
if not available:
|
||||
flash('用户名 %s 已经被注册。' % (username))
|
||||
return render_template('signup.html')
|
||||
elif len(password.strip()) < 4:
|
||||
return '密码过于简单。'
|
||||
else:
|
||||
add_user(username, password)
|
||||
verified = verify_user(username, password)
|
||||
if verified:
|
||||
session['logged_in'] = True
|
||||
session[username] = username
|
||||
session['username'] = username
|
||||
session['expiry_date'] = get_expiry_date(username)
|
||||
session['articleID'] = None
|
||||
return '<p>恭喜,你已成功注册, 你的用户名是 <a href="%s">%s</a>。</p>\
|
||||
<p><a href="/%s">开始使用</a> <a href="/">返回首页</a><p/>' % (username, username, username)
|
||||
else:
|
||||
return '用户名密码验证失败。'
|
||||
|
||||
|
||||
@app.route("/login", methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'GET':
|
||||
if not session.get('logged_in'):
|
||||
return render_template('login.html')
|
||||
else:
|
||||
return '你已登录 <a href="/%s">%s</a>。 登出点击<a href="/logout">这里</a>。' % (session['username'], session['username'])
|
||||
elif request.method == 'POST':
|
||||
# check database and verify user
|
||||
username = request.form['username']
|
||||
password = request.form['password']
|
||||
verified = verify_user(username, password)
|
||||
if verified:
|
||||
session['logged_in'] = True
|
||||
session[username] = username
|
||||
session['username'] = username
|
||||
user_expiry_date = get_expiry_date(username)
|
||||
session['expiry_date'] = user_expiry_date
|
||||
session['articleID'] = None
|
||||
return redirect(url_for('userpage', username=username))
|
||||
else:
|
||||
return '无法通过验证。'
|
||||
|
||||
|
||||
@app.route("/logout", methods=['GET', 'POST'])
|
||||
def logout():
|
||||
session['logged_in'] = False
|
||||
return redirect(url_for('mainpage'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
#app.secret_key = os.urandom(16)
|
||||
#app.run(debug=False, port='6000')
|
||||
app.run(debug=True)
|
||||
#app.run(debug=True, port='6000')
|
||||
#app.run(host='0.0.0.0', debug=True, port='6000')
|
||||
#! /usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
###########################################################################
|
||||
# Copyright 2019 (C) Hui Lan <hui.lan@cantab.net>
|
||||
# Written permission must be obtained from the author for commercial uses.
|
||||
###########################################################################
|
||||
|
||||
from Login import *
|
||||
from Article import *
|
||||
import Yaml
|
||||
from user_service import userService
|
||||
from account_service import accountService
|
||||
app = Flask(__name__)
|
||||
app.secret_key = 'lunch.time!'
|
||||
|
||||
# 将蓝图注册到Lab app
|
||||
app.register_blueprint(userService)
|
||||
app.register_blueprint(accountService)
|
||||
|
||||
path_prefix = '/var/www/wordfreq/wordfreq/'
|
||||
path_prefix = './' # comment this line in deployment
|
||||
|
||||
|
||||
def get_random_image(path):
|
||||
'''
|
||||
返回随机图
|
||||
:param path: 图片文件(JPEG格式),不包含后缀名
|
||||
:return:
|
||||
'''
|
||||
img_path = random.choice(glob.glob(os.path.join(path, '*.jpg')))
|
||||
|
||||
return img_path[img_path.rfind('/static'):]
|
||||
|
||||
|
||||
def get_random_ads():
|
||||
'''
|
||||
返回随机广告
|
||||
:return: 一个广告(包含HTML标签)
|
||||
'''
|
||||
ads = random.choice(['个性化分析精准提升', '你的专有单词本', '智能捕捉阅读弱点,针对性提高你的阅读水平'])
|
||||
return ads + '。 <a href="/signup">试试</a>吧!'
|
||||
|
||||
|
||||
def appears_in_test(word, d):
|
||||
'''
|
||||
如果字符串里没有指定的单词,则返回逗号加单词
|
||||
:param word: 指定单词
|
||||
:param d: 字符串
|
||||
:return: 逗号加单词
|
||||
'''
|
||||
if not word in d:
|
||||
return ''
|
||||
else:
|
||||
return ','.join(d[word])
|
||||
|
||||
|
||||
@app.route("/mark", methods=['GET', 'POST'])
|
||||
def mark_word():
|
||||
'''
|
||||
标记单词
|
||||
:return: 重定位到主界面
|
||||
'''
|
||||
if request.method == 'POST':
|
||||
d = load_freq_history(path_prefix + 'static/frequency/frequency.p')
|
||||
lst_history = pickle_idea.dict2lst(d)
|
||||
lst = []
|
||||
for word in request.form.getlist('marked'):
|
||||
lst.append((word, 1))
|
||||
d = pickle_idea.merge_frequency(lst, lst_history)
|
||||
pickle_idea.save_frequency_to_pickle(d, path_prefix + 'static/frequency/frequency.p')
|
||||
return redirect(url_for('mainpage'))
|
||||
else: # 不回应GET请求
|
||||
return 'Under construction'
|
||||
|
||||
|
||||
@app.route("/", methods=['GET', 'POST'])
|
||||
def mainpage():
|
||||
'''
|
||||
根据GET或POST方法来返回不同的主界面
|
||||
:return: 主界面
|
||||
'''
|
||||
if request.method == 'POST': # when we submit a form
|
||||
content = request.form['content']
|
||||
f = WordFreq(content)
|
||||
lst = f.get_freq()
|
||||
# save history
|
||||
d = load_freq_history(path_prefix + 'static/frequency/frequency.p')
|
||||
lst_history = pickle_idea.dict2lst(d)
|
||||
d = pickle_idea.merge_frequency(lst, lst_history)
|
||||
pickle_idea.save_frequency_to_pickle(d, path_prefix + 'static/frequency/frequency.p')
|
||||
return render_template('mainpage_post.html', lst=lst, yml=Yaml.yml)
|
||||
|
||||
elif 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')
|
||||
d_len = len(d)
|
||||
lst = sort_in_descending_order(pickle_idea.dict2lst(d))
|
||||
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__':
|
||||
'''
|
||||
运行程序
|
||||
'''
|
||||
# app.secret_key = os.urandom(16)
|
||||
# app.run(debug=False, port='6000')
|
||||
app.run(debug=True)
|
||||
# app.run(debug=True, port='6000')
|
||||
# app.run(host='0.0.0.0', debug=True, port='6000')
|
||||
# print(mod5('123'))
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# 全局引入的css文件地址
|
||||
css:
|
||||
item:
|
||||
- static/css/bootstrap.css
|
||||
|
||||
# 全局引入的js文件地址
|
||||
js:
|
||||
head: # 在页面加载之前加载
|
||||
# - static/js/APlayer.js
|
||||
# - static/js/Meting.js
|
||||
bottom: # 在页面加载完之后加载
|
||||
- static/js/fillword.js
|
||||
- static/js/highlight.js
|
||||
|
||||
# 高亮样式,目前仅支持修改颜色
|
||||
highlight:
|
||||
color: ff0000
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,29 @@
|
|||
isRead = true;
|
||||
isChoose = true;
|
||||
var reader = window.speechSynthesis; // 全局定义朗读者,以便朗读和暂停
|
||||
|
||||
function getWord(){
|
||||
var word = window.getSelection?window.getSelection():document.selection.createRange().text;
|
||||
return word;
|
||||
}
|
||||
function fillinWord(){
|
||||
var word = getWord();
|
||||
if (isRead) read(word);
|
||||
if (!isChoose) return;
|
||||
var element = document.getElementById("selected-words");
|
||||
element.value = element.value + " " + word;
|
||||
}
|
||||
document.getElementById("text-content").addEventListener("click", fillinWord, false);
|
||||
function read(s){
|
||||
var msg = new SpeechSynthesisUtterance(s);
|
||||
reader.speak(msg);
|
||||
}
|
||||
function onReadClick(){
|
||||
isRead = !isRead;
|
||||
if(!isRead){
|
||||
reader.cancel();
|
||||
}
|
||||
}
|
||||
function onChooseClick(){
|
||||
isChoose = !isChoose;
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
var isHighlight = true;
|
||||
|
||||
function cancelBtnHandler() {
|
||||
cancel_highLight();
|
||||
document.getElementById("text-content").removeEventListener("click", fillinWord, 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() {
|
||||
document.getElementById("text-content").removeEventListener("click", fillinWord2, false);
|
||||
document.getElementById("text-content").removeEventListener("touchstart", fillinWord2, false);
|
||||
document.getElementById("text-content").addEventListener("click", fillinWord, false);
|
||||
document.getElementById("text-content").addEventListener("touchstart", fillinWord, false);
|
||||
highLight();
|
||||
}
|
||||
|
||||
function getWord() {
|
||||
var word = window.getSelection ? window.getSelection() : document.selection.createRange().text;
|
||||
return word;
|
||||
}
|
||||
|
||||
function highLight() {
|
||||
if(!isHighlight) return;
|
||||
var txt = document.getElementById("article").innerText;
|
||||
var sel_word1 = document.getElementById("selected-words");
|
||||
var sel_word2 = document.getElementById("selected-words2");
|
||||
if (sel_word1 != null) {
|
||||
var list = sel_word1.value.split(" ");
|
||||
for (var i = 0; i < list.length; ++i) {
|
||||
list[i] = list[i].replace(/(^\s*)|(\s*$)/g, "");
|
||||
if (list[i] != "" && "<mark>".indexOf(list[i]) == -1 && "</mark>".indexOf(list[i]) == -1) {
|
||||
txt = txt.replace(new RegExp(list[i], "g"), "<mark>" + list[i] + "</mark>");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sel_word2 != null) {
|
||||
var list2 = sel_word2.value.split(" ");
|
||||
for (var i = 0; i < list2.length; ++i) {
|
||||
list2[i] = list2[i].replace(/(^\s*)|(\s*$)/g, "");
|
||||
if (list2[i] != "" && "<mark>".indexOf(list2[i]) == -1 && "</mark>".indexOf(list2[i]) == -1) {
|
||||
txt = txt.replace(new RegExp(list2[i], "g"), "<mark>" + list2[i] + "</mark>");
|
||||
}
|
||||
}
|
||||
}
|
||||
document.getElementById("article").innerHTML = txt;
|
||||
}
|
||||
|
||||
function cancel_highLight() {
|
||||
var txt = document.getElementById("article").innerText;
|
||||
var sel_word1 = document.getElementById("selected-words");
|
||||
var sel_word2 = document.getElementById("selected-words2");
|
||||
if (sel_word1 != null) {
|
||||
var list = sel_word1.value.split(" ");
|
||||
for (var i = 0; i < list.length; ++i) {
|
||||
list[i] = list[i].replace(/(^\s*)|(\s*$)/g, "");
|
||||
if (list[i] != "") {
|
||||
txt = txt.replace("<mark>" + list[i] + "</mark>", "list[i]");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sel_word2 != null) {
|
||||
var list2 = sel_word1.value.split(" ");
|
||||
for (var i = 0; i < list2.length; ++i) {
|
||||
var list2 = sel_word2.value.split(" ");
|
||||
list2[i] = list2[i].replace(/(^\s*)|(\s*$)/g, "");
|
||||
if (list2[i] != "") {
|
||||
txt = txt.replace("<mark>" + list[i] + "</mark>", "list[i]");
|
||||
}
|
||||
}
|
||||
}
|
||||
document.getElementById("article").innerHTML = txt;
|
||||
}
|
||||
|
||||
function fillinWord() {
|
||||
highLight();
|
||||
}
|
||||
|
||||
function fillinWord2() {
|
||||
cancel_highLight();
|
||||
}
|
||||
|
||||
function ChangeHighlight() {
|
||||
if (isHighlight) {
|
||||
isHighlight = false;
|
||||
cancel_highLight();
|
||||
} else {
|
||||
isHighlight = true;
|
||||
highLight();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
showBtnHandler();
|
|
@ -1,86 +1,86 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh"><head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />
|
||||
<meta charset="utf-8">
|
||||
<title>怎么用English Pal</title>
|
||||
<style>
|
||||
li { margin: 3px 0; }
|
||||
ul {list-style-type: none;}
|
||||
body {font-size: 100%;}
|
||||
* {font-family:SimSun !important;}
|
||||
|
||||
/* http://webtricksandtreats.com/table-style-css/ */
|
||||
table.table-style-one {
|
||||
font-family: verdana,arial,sans-serif;
|
||||
font-size:14px;
|
||||
color:#333333;
|
||||
border-width: 1px;
|
||||
border-color: #3A3A3A;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.table-style-one th {
|
||||
border-width: 1px;
|
||||
padding: 8px;
|
||||
border-style: solid;
|
||||
border-color: #3A3A3A;
|
||||
background-color: #F0F8FF;
|
||||
}
|
||||
table.table-style-one td {
|
||||
border-width: 1px;
|
||||
padding: 8px;
|
||||
border-style: solid;
|
||||
border-color: #3A3A3A;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p>本软件的宗旨是:珍惜你的时间, 提高你的获取英文信息的速度与准确度。 不管你是英语爱好者,备考的学生,还是英语老师,都能从 English Pal 中发现用处。</p>
|
||||
|
||||
<p>如果你教英语,English Pal 可以帮你掌握题目词汇规律, 提高教学质量。</p>
|
||||
|
||||
<p>如果你学英语,English Pal 可以帮你迅速提高词汇, 轻松应对各种考试。 </p>
|
||||
|
||||
<p>1秒内闪电查词,告别字典。 私人定制的单词簿,永久相伴,记录奋斗岁月。 </p>
|
||||
|
||||
<p>现在就<a href="/signup">试试看</a>吧。 English Pal 期待你的捷报, English Pal 期待你的<a href="./20161113-christ-piece-cambridge.jpg">远航</a>。</p>
|
||||
|
||||
<h2>使用方法</h2>
|
||||
|
||||
<p>在EnglishPal主页点击<a href="http://118.25.96.118:90/signup">成为会员</a>链接。支付会员费后即可开始使用。</p>
|
||||
|
||||
<p>活跃会员还有机会获得英文阅读10分钟一对一指导。</p>
|
||||
|
||||
<table class="table-style-one">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>截图</th>
|
||||
<th>说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="./EnglishPal-screenshot-01.jpg"><img src="./EnglishPal-screenshot-01.jpg" width="300px"/></a></td>
|
||||
<td>精选短文,让你窥见世界。 统计词频,让你掌握规律。</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><a href="./EnglishPal-screenshot-02.jpg"><img src="./EnglishPal-screenshot-02.jpg" width="300px"/></a></td>
|
||||
<td>时间无价,个性化的生词簿,为你节省记背单词时间。</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><a href="./EnglishPal-screenshot-03.jpg"><img src="./EnglishPal-screenshot-03.jpg" width="300px"/></a></td>
|
||||
<td>考试人人都怕,单词的考试分类,让你目的明确。</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh"><head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />
|
||||
<meta charset="utf-8">
|
||||
<title>怎么用English Pal</title>
|
||||
<style>
|
||||
li { margin: 3px 0; }
|
||||
ul {list-style-type: none;}
|
||||
body {font-size: 100%;}
|
||||
* {font-family:SimSun !important;}
|
||||
|
||||
/* http://webtricksandtreats.com/table-style-css/ */
|
||||
table.table-style-one {
|
||||
font-family: verdana,arial,sans-serif;
|
||||
font-size:14px;
|
||||
color:#333333;
|
||||
border-width: 1px;
|
||||
border-color: #3A3A3A;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.table-style-one th {
|
||||
border-width: 1px;
|
||||
padding: 8px;
|
||||
border-style: solid;
|
||||
border-color: #3A3A3A;
|
||||
background-color: #F0F8FF;
|
||||
}
|
||||
table.table-style-one td {
|
||||
border-width: 1px;
|
||||
padding: 8px;
|
||||
border-style: solid;
|
||||
border-color: #3A3A3A;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p>本软件的宗旨是:珍惜你的时间, 提高你的获取英文信息的速度与准确度。 不管你是英语爱好者,备考的学生,还是英语老师,都能从 English Pal 中发现用处。</p>
|
||||
|
||||
<p>如果你教英语,English Pal 可以帮你掌握题目词汇规律, 提高教学质量。</p>
|
||||
|
||||
<p>如果你学英语,English Pal 可以帮你迅速提高词汇, 轻松应对各种考试。 </p>
|
||||
|
||||
<p>1秒内闪电查词,告别字典。 私人定制的单词簿,永久相伴,记录奋斗岁月。 </p>
|
||||
|
||||
<p>现在就<a href="/signup">试试看</a>吧。 English Pal 期待你的捷报, English Pal 期待你的<a href="./20161113-christ-piece-cambridge.jpg">远航</a>。</p>
|
||||
|
||||
<h2>使用方法</h2>
|
||||
|
||||
<p>在EnglishPal主页点击<a href="http://118.25.96.118:90/signup">成为会员</a>链接。支付会员费后即可开始使用。</p>
|
||||
|
||||
<p>活跃会员还有机会获得英文阅读10分钟一对一指导。</p>
|
||||
|
||||
<table class="table-style-one">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>截图</th>
|
||||
<th>说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="./EnglishPal-screenshot-01.jpg"><img src="./EnglishPal-screenshot-01.jpg" width="300px"/></a></td>
|
||||
<td>精选短文,让你窥见世界。 统计词频,让你掌握规律。</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><a href="./EnglishPal-screenshot-02.jpg"><img src="./EnglishPal-screenshot-02.jpg" width="300px"/></a></td>
|
||||
<td>时间无价,个性化的生词簿,为你节省记背单词时间。</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><a href="./EnglishPal-screenshot-03.jpg"><img src="./EnglishPal-screenshot-03.jpg" width="300px"/></a></td>
|
||||
<td>考试人人都怕,单词的考试分类,让你目的明确。</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>账号过期</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>您的账号{{ username }}过期。</p>
|
||||
<p>为了提高服务质量,English Pal 收取会员费用, 每天1元。</p>
|
||||
<p>请决定你要试用的时间长度,扫描下面支付宝二维码支付。 支付时请注明<i>English Pal Membership Fee</i>。 我们会于12小时内激活账号。</p>
|
||||
<p><img src="static/donate-the-author-hidden.jpg" width="120px" alt="支付宝二维码" /></p>
|
||||
<p>如果有问题,请联系开发者 dev@qq.com。</p>
|
||||
<p><a href="/logout">登出</a></p>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,15 +1,21 @@
|
|||
{% block body %}
|
||||
{% if session['logged_in'] %}
|
||||
|
||||
You're logged in already!
|
||||
|
||||
{% else %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />
|
||||
<form action="/login" method="POST">
|
||||
<p><input type="username" name="username" placeholder="邮箱地址、电话号码"></p>
|
||||
<p><input type="password" name="password" placeholder="密码"></p>
|
||||
<p><input type="submit" value="登录"></p>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% if session['logged_in'] %}
|
||||
|
||||
You're logged in already!
|
||||
|
||||
{% else %}
|
||||
<meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />
|
||||
<form action="/login" method="POST">
|
||||
<p>
|
||||
<input type="username" name="username" placeholder="邮箱地址、电话号码">
|
||||
</p>
|
||||
<p>
|
||||
<input type="password" name="password" placeholder="密码">
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="登录">
|
||||
</p>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />
|
||||
|
||||
{{ yml['header'] | safe }}
|
||||
{% if yml['css']['item'] %}
|
||||
{% for css in yml['css']['item'] %}
|
||||
<link href="{{ css }}" rel="stylesheet">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if yml['js']['head'] %}
|
||||
{% for js in yml['js']['head'] %}
|
||||
<script src="{{ js }}" ></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<title>EnglishPal 英文单词高效记</title>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<p><b><font size="+3" color="red">English Pal - Learn English smartly!</font></b></p>
|
||||
{% if session['logged_in'] %}
|
||||
<a href="/{{session['username']}}">{{session['username']}}</a></p>
|
||||
{% else %}
|
||||
<p><a href="/login">登录</a> <a href="/signup">成为会员</a> <a href="/static/usr/instructions.html">使用说明</a></p >
|
||||
<p><b>{{random_ads|safe}}</b></p>
|
||||
{% endif %}
|
||||
<div class="alert alert-success" role="alert">共有文章 <span class="badge bg-success"> {{number_of_essays}} </span> 篇</div>
|
||||
<p>粘帖1篇文章 (English only)</p>
|
||||
<form method="post" action="/">
|
||||
<textarea name="content" rows="10" cols="120"></textarea><br/>
|
||||
<input type="submit" value="get文章中的词频"/>
|
||||
<input type="reset" value="清除"/>
|
||||
</form>
|
||||
{% if d_len > 0 %}
|
||||
<p><b>最常见的词</b></p>
|
||||
{% for x in lst if x[1]>99 %}
|
||||
<a href="http://youdao.com/w/eng/{{x[0]}}/#keyfrom=dict2.index">{{x[0]}}</a> {{x[1]}}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
</div>
|
||||
{{ yml['footer'] | safe }}
|
||||
{% if yml['js']['bottom'] %}
|
||||
{% for js in yml['js']['bottom'] %}
|
||||
<script src="{{ js }}" ></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,40 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
|
||||
{{ yml['header'] | safe }}
|
||||
{% if yml['css']['item'] %}
|
||||
{% for css in yml['css']['item'] %}
|
||||
<link href="{{ css }}" rel="stylesheet">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if yml['js']['head'] %}
|
||||
{% for js in yml['js']['head'] %}
|
||||
<script src="{{ js }}" ></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<form method="post" action="/mark">
|
||||
{% for x in lst %}
|
||||
<p>
|
||||
<font color="grey">{{loop.index}}</font>
|
||||
:
|
||||
<a href="http://youdao.com/w/eng/{{x[0]}}/#keyfrom=dict2.index">{{x[0]}}</a>
|
||||
({{x[1]}})
|
||||
<input type="checkbox" name="marked" value="{{x[0]}}">
|
||||
</p>
|
||||
{% endfor %}
|
||||
<input type="submit" value="确定并返回"/>
|
||||
</form>
|
||||
{{ yml['footer'] | safe }}
|
||||
{% if yml['js']['bottom'] %}
|
||||
{% for js in yml['js']['bottom'] %}
|
||||
<script src="{{ js }}" ></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,5 @@
|
|||
{% block body %}
|
||||
{% if not session['logged_in'] %}
|
||||
<p>请先<a href="/login">登录</a></p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,14 @@
|
|||
<html>
|
||||
<body>
|
||||
<form action="/reset" method='POST'>
|
||||
旧密码:
|
||||
<input type="password" name="old-psd" />
|
||||
<br/>
|
||||
新密码:
|
||||
<input type="password" name="new-psd" />
|
||||
<br/>
|
||||
<input type="submit" name="submit" value="提交" />
|
||||
<input type="button" name="submit" value="放弃修改" onclick="window.location.href='/{{ username }}'"/>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -1,19 +1,19 @@
|
|||
{% block body %}
|
||||
{% if session['logged_in'] %}
|
||||
|
||||
You're logged in already! <a href="/logout">Logout</a>.
|
||||
|
||||
{% else %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />
|
||||
<p>{{ get_flashed_messages()[0] | safe }}</p>
|
||||
|
||||
<p>Sign up here.</p>
|
||||
|
||||
<form action="/signup" method="POST">
|
||||
<p><input type="username" name="username" placeholder="邮箱地址、电话号码"></p>
|
||||
<p><input type="password" name="password" placeholder="密码"></p>
|
||||
<p><input type="submit" value="注册"></p>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% if session['logged_in'] %}
|
||||
|
||||
You're logged in already! <a href="/logout">Logout</a>.
|
||||
|
||||
{% else %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />
|
||||
<p>{{ get_flashed_messages()[0] | safe }}</p>
|
||||
|
||||
<p>Sign up here.</p>
|
||||
|
||||
<form action="/signup" method="POST">
|
||||
<p><input type="username" name="username" placeholder="邮箱地址、电话号码"></p>
|
||||
<p><input type="password" name="password" placeholder="密码"></p>
|
||||
<p><input type="submit" value="注册"></p>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes"/>
|
||||
<meta name="format-detection" content="telephone=no"/>
|
||||
|
||||
{{ yml['header'] | safe }}
|
||||
{% if yml['css']['item'] %}
|
||||
{% for css in yml['css']['item'] %}
|
||||
<link href="{{ css }}" rel="stylesheet">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if yml['js']['head'] %}
|
||||
{% for js in yml['js']['head'] %}
|
||||
<script src="{{ js }}"></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<title>EnglishPal Study Room for {{ username }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<p><b>English Pal for <font color="red">{{ username }}</font></b>
|
||||
<a class="btn btn-secondary" href="/logout" role="button">登出</a>
|
||||
<a class="btn btn-secondary" href="/reset" role="button">重设密码</a>
|
||||
</p>
|
||||
{{ flashed_messages|safe }}
|
||||
|
||||
<p><a class="btn btn-success" href="/{{ username }}/reset" role="button"> 下一篇 Next Article </a></p>
|
||||
<p><b>阅读文章并回答问题</b></p>
|
||||
<div id="text-content">{{ today_article|safe }}</div>
|
||||
|
||||
<input type="checkbox" onclick="ChangeHighlight()" checked/>生词高亮
|
||||
<input type="checkbox" onclick="onReadClick()" checked/>大声朗读
|
||||
<input type="checkbox" onclick="onChooseClick()" checked/>划词入库
|
||||
|
||||
<p><b>收集生词吧</b> (可以在正文中划词,也可以复制黏贴)</p>
|
||||
<form method="post" action="/{{ username }}">
|
||||
<textarea name="content" id="selected-words" rows="10" cols="120"></textarea><br/>
|
||||
<input type="submit" value="把生词加入我的生词库"/>
|
||||
<input type="reset" value="清除"/>
|
||||
</form>
|
||||
{% if session.get['thisWord'] %}
|
||||
<script type="text/javascript">
|
||||
//point to the anchor in the page whose id is aaa if it exists
|
||||
window.onload = function () {
|
||||
var element = document.getElementsByName("aaa");
|
||||
if (element != null)
|
||||
document.getElementsByName("aaa")[0].scrollIntoView(true);
|
||||
}
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
{% if d_len > 0 %}
|
||||
<p><b>我的生词簿</b></p>
|
||||
{% for x in lst3 %}
|
||||
{% set word = x[0] %}
|
||||
|
||||
{% set freq = x[1] %}
|
||||
{% if session.get('thisWord') == x[0] and session.get('time') == 1 %}
|
||||
<a name="aaa"></a>
|
||||
{% endif %}
|
||||
{% if freq > 1 %}
|
||||
<p class="new-word">
|
||||
<a class="btn btn-light" href='http://youdao.com/w/eng/{{ word }}/#keyfrom=dict2.index'
|
||||
role="button">{{ word }}</a>
|
||||
(
|
||||
<a title="{{ word }}">{{ freq }}</a>
|
||||
)
|
||||
|
||||
<a class="btn btn-success" href={{ username }}/{{ word }}/familiar role="button">熟悉</a>
|
||||
<a class="btn btn-warning" href={{ username }}/{{ word }}/unfamiliar role="button">不熟悉</a>
|
||||
<a class="btn btn-danger" href={{ username }}/{{ word }}/del role="button">删除</a>
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="new-word">
|
||||
<a class="btn btn-light" href='http://youdao.com/w/eng/{{ word }}/#keyfrom=dict2.index'
|
||||
role="button">{{ word }}</a>
|
||||
(
|
||||
<a title="{{ word }}">{{ freq }}</a>
|
||||
)
|
||||
<a class="btn btn-success" href={{ username }}/{{ word }}/familiar role="button">熟悉</a>
|
||||
<a class="btn btn-warning" href={{ username }}/{{ word }}/unfamiliar role="button">不熟悉</a>
|
||||
<a class="btn btn-danger" href={{ username }}/{{ word }}/del role="button">删除</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<a href='http://youdao.com/w/eng/{{ word }}/#keyfrom=dict2.index'>{{ word }}</a>{{ freq }}
|
||||
{% endfor %}
|
||||
<input id="selected-words2" type="hidden" value="{{ words }}">
|
||||
{% endif %}
|
||||
</div>
|
||||
{{ yml['footer'] | safe }}
|
||||
{% if yml['js']['bottom'] %}
|
||||
{% for js in yml['js']['bottom'] %}
|
||||
<script src="{{ js }}"></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</body>
|
||||
<style>
|
||||
mark {
|
||||
color: #{{ yml['highlight']['color'] }};
|
||||
background-color: rgba(0,0,0,0);
|
||||
}
|
||||
</style>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
|
||||
{{ yml['header'] | safe }}
|
||||
{% if yml['css']['item'] %}
|
||||
{% for css in yml['css']['item'] %}
|
||||
<link href="{{ css }}" rel="stylesheet">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if yml['js']['head'] %}
|
||||
{% for js in yml['js']['head'] %}
|
||||
<script src="{{ js }}" ></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<title>EnglishPal Study Room for {{username}}</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>勾选不认识的单词</p>
|
||||
<form method="post" action="/{{username}}/mark">
|
||||
<input type="submit" name="add-btn" value="加入我的生词簿"/>
|
||||
{% for x in lst %}
|
||||
{% set word = x[0]%}
|
||||
<p>
|
||||
<font color="grey">{{loop.index}}</font>
|
||||
:
|
||||
<a href='http://youdao.com/w/eng/{{word}}/#keyfrom=dict2.index' title={{word}}>{{word}}</a>
|
||||
({{x[1]}})
|
||||
<input type="checkbox" name="marked" value={{word}}>
|
||||
</p>
|
||||
|
||||
{% endfor %}
|
||||
</form>
|
||||
{{ yml['footer'] | safe }}
|
||||
{% if yml['js']['bottom'] %}
|
||||
{% for js in yml['js']['bottom'] %}
|
||||
<script src="{{ js }}" ></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
|
@ -6,18 +6,19 @@ from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
|||
|
||||
import random, time
|
||||
import string
|
||||
import pytest
|
||||
|
||||
#driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
#driver.implicitly_wait(10)
|
||||
driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
driver.implicitly_wait(10)
|
||||
|
||||
HOME_PAGE = 'http://121.4.94.30:91/'
|
||||
|
||||
|
||||
def has_punctuation(s):
|
||||
return [c for c in s if c in string.punctuation] != []
|
||||
|
||||
@pytest.mark.usefixtures
|
||||
def test_add_word(URL, driver):
|
||||
|
||||
def test_add_word():
|
||||
try:
|
||||
driver.get(URL)
|
||||
driver.get(HOME_PAGE)
|
||||
assert 'English Pal -' in driver.page_source
|
||||
|
||||
# login
|
||||
|
|
|
@ -3,21 +3,22 @@
|
|||
# docker run -d -p 4444:4444 selenium/standalone-chrome
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
import pytest
|
||||
|
||||
import random, time
|
||||
import string
|
||||
|
||||
#driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
#driver.implicitly_wait(10)
|
||||
driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
driver.implicitly_wait(10)
|
||||
|
||||
HOME_PAGE = 'http://121.4.94.30:91/'
|
||||
|
||||
|
||||
def has_punctuation(s):
|
||||
return [c for c in s if c in string.punctuation] != []
|
||||
|
||||
@pytest.mark.usefixtures
|
||||
def test_add_word_and_essay_does_not_change(URL, driver):
|
||||
|
||||
def test_add_word_and_essay_does_not_change():
|
||||
try:
|
||||
driver.get(URL)
|
||||
driver.get(HOME_PAGE)
|
||||
assert 'English Pal -' in driver.page_source
|
||||
|
||||
# login
|
||||
|
|
|
@ -1,57 +1,61 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Run the docker image using the following command:
|
||||
# docker run -d -p 4444:4444 selenium/standalone-chrome
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
import pytest
|
||||
import random, time
|
||||
import string
|
||||
|
||||
# 调用本地chromedriver
|
||||
# driver = webdriver.Chrome(executable_path="D:\ChromeDriver\chromedriver.exe")
|
||||
# driver.get("http://127.0.0.1:5000/")
|
||||
#driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
#driver.implicitly_wait(10)
|
||||
# driver.maximize_window()
|
||||
# HOME_PAGE = "http://127.0.0.1:5000/"
|
||||
|
||||
@pytest.mark.usefixtures
|
||||
def test_delete_word():
|
||||
try:
|
||||
driver.get(URL)
|
||||
assert 'English Pal -' in driver.page_source
|
||||
# login
|
||||
elem = driver.find_element_by_link_text('登录')
|
||||
elem.click()
|
||||
|
||||
uname = 'lanhui'
|
||||
password = 'l0ve1t'
|
||||
elem = driver.find_element_by_name('username')
|
||||
elem.send_keys(uname)
|
||||
|
||||
elem = driver.find_element_by_name('password')
|
||||
elem.send_keys(password)
|
||||
|
||||
elem = driver.find_element_by_xpath('//form[1]/p[3]/input[1]') # 找到登录按钮
|
||||
elem.click()
|
||||
|
||||
assert 'EnglishPal Study Room for ' + uname in driver.title
|
||||
|
||||
# delete
|
||||
elems = driver.find_element_by_class_name('new-word')
|
||||
# 移动到元素elems对象的“顶端”与当前窗口的“顶部”对齐
|
||||
driver.execute_script("arguments[0].scrollIntoView();", elems)
|
||||
driver.save_screenshot('test_delete_pic1.png')
|
||||
current_2 = elems.text
|
||||
elem = driver.find_element_by_link_text("删除")
|
||||
elem.click()
|
||||
elems = driver.find_element_by_class_name('new-word')
|
||||
driver.execute_script("arguments[0].scrollIntoView();", elems)
|
||||
driver.save_screenshot('test_delete_pic2.png')
|
||||
now_2 = elems.text
|
||||
|
||||
assert current_2 != now_2
|
||||
|
||||
finally:
|
||||
driver.quit()
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
# Run the docker image using the following command:
|
||||
# docker run -d -p 4444:4444 selenium/standalone-chrome
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
|
||||
import random, time
|
||||
import string
|
||||
|
||||
# 调用本地chromedriver
|
||||
# driver = webdriver.Chrome(executable_path="D:\ChromeDriver\chromedriver.exe")
|
||||
# driver.get("http://127.0.0.1:5000/")
|
||||
driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
driver.implicitly_wait(10)
|
||||
# driver.maximize_window()
|
||||
# HOME_PAGE = "http://127.0.0.1:5000/"
|
||||
|
||||
|
||||
HOME_PAGE = 'http://121.4.94.30:91/'
|
||||
|
||||
|
||||
def test_delete_word():
|
||||
try:
|
||||
driver.get(HOME_PAGE)
|
||||
assert 'English Pal -' in driver.page_source
|
||||
# login
|
||||
elem = driver.find_element_by_link_text('登录')
|
||||
elem.click()
|
||||
|
||||
uname = 'lanhui'
|
||||
password = 'l0ve1t'
|
||||
elem = driver.find_element_by_name('username')
|
||||
elem.send_keys(uname)
|
||||
|
||||
elem = driver.find_element_by_name('password')
|
||||
elem.send_keys(password)
|
||||
|
||||
elem = driver.find_element_by_xpath('//form[1]/p[3]/input[1]') # 找到登录按钮
|
||||
elem.click()
|
||||
|
||||
assert 'EnglishPal Study Room for ' + uname in driver.title
|
||||
|
||||
# delete
|
||||
elems = driver.find_element_by_class_name('new-word')
|
||||
# 移动到元素elems对象的“顶端”与当前窗口的“顶部”对齐
|
||||
driver.execute_script("arguments[0].scrollIntoView();", elems)
|
||||
driver.save_screenshot('test_delete_pic1.png')
|
||||
current_2 = elems.text
|
||||
elem = driver.find_element_by_link_text("删除")
|
||||
elem.click()
|
||||
elems = driver.find_element_by_class_name('new-word')
|
||||
driver.execute_script("arguments[0].scrollIntoView();", elems)
|
||||
driver.save_screenshot('test_delete_pic2.png')
|
||||
now_2 = elems.text
|
||||
|
||||
assert current_2 != now_2
|
||||
|
||||
finally:
|
||||
driver.quit()
|
||||
|
||||
# test_delete_word()
|
||||
|
|
|
@ -3,17 +3,19 @@
|
|||
# docker run -d -p 4444:4444 selenium/standalone-chrome
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
import pytest
|
||||
|
||||
import random, string
|
||||
|
||||
#driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
#driver.implicitly_wait(10)
|
||||
driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
driver.implicitly_wait(10)
|
||||
|
||||
HOME_PAGE = 'http://121.4.94.30:91/'
|
||||
|
||||
|
||||
@pytest.mark.usefixtures
|
||||
def test_login(URL, driver):
|
||||
|
||||
def test_login():
|
||||
try:
|
||||
driver.get(URL)
|
||||
driver.get(HOME_PAGE)
|
||||
driver.save_screenshot('./app/test/test_login_pic0.png')
|
||||
|
||||
assert 'English Pal -' in driver.page_source
|
||||
|
@ -39,7 +41,7 @@ def test_login(URL, driver):
|
|||
assert uname in driver.page_source
|
||||
|
||||
# logout
|
||||
driver.get(URL + 'logout')
|
||||
driver.get(HOME_PAGE + 'logout')
|
||||
driver.save_screenshot('./app/test/test_login_pic3.png')
|
||||
|
||||
# login
|
||||
|
|
|
@ -3,16 +3,17 @@
|
|||
# docker run -d -p 4444:4444 selenium/standalone-chrome
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
import pytest
|
||||
|
||||
import random, string
|
||||
|
||||
#driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
#driver.implicitly_wait(10)
|
||||
driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
driver.implicitly_wait(10)
|
||||
|
||||
HOME_PAGE = 'http://121.4.94.30:91/'
|
||||
|
||||
@pytest.mark.usefixtures
|
||||
def test_login_security_fix():
|
||||
try:
|
||||
driver.get(URL)
|
||||
driver.get(HOME_PAGE)
|
||||
|
||||
elem = driver.find_element_by_link_text('登录')
|
||||
elem.click()
|
||||
|
|
|
@ -3,16 +3,19 @@
|
|||
# docker run -d -p 4444:4444 selenium/standalone-chrome
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
import pytest
|
||||
|
||||
import random, string, time
|
||||
|
||||
#driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
#driver.implicitly_wait(10)
|
||||
driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
driver.implicitly_wait(10)
|
||||
|
||||
@pytest.mark.usefixtures
|
||||
def test_next(URL, driver):
|
||||
HOME_PAGE = 'http://121.4.94.30:91/'
|
||||
|
||||
|
||||
|
||||
def test_next():
|
||||
try:
|
||||
driver.get(URL)
|
||||
driver.get(HOME_PAGE)
|
||||
assert 'English Pal -' in driver.page_source
|
||||
|
||||
# login
|
||||
|
|
|
@ -6,10 +6,15 @@ Click the Familiar or Unfamiliar button (current word frequency is 1), and the p
|
|||
from random import randint
|
||||
|
||||
from selenium import webdriver
|
||||
import pytest
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
|
||||
driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
driver.implicitly_wait(10)
|
||||
|
||||
HOME_PAGE = 'http://121.4.94.30:91/'
|
||||
|
||||
|
||||
def click_by_random(text):
|
||||
elements = driver.find_elements_by_link_text(text) # 点击单词表中的第一个单词的熟悉按钮
|
||||
elements[randint(0, len(elements) - 1)].click()
|
||||
|
@ -28,10 +33,10 @@ def get_scrollTop():
|
|||
roll_height = driver.execute_script(js)
|
||||
return roll_height
|
||||
|
||||
@pytest.mark.usefixtures
|
||||
def test_page_position(URL, driver):
|
||||
|
||||
def test_page_position():
|
||||
try:
|
||||
driver.get(URL)
|
||||
driver.get(HOME_PAGE)
|
||||
# login
|
||||
driver.find_element_by_link_text('登录').click()
|
||||
|
||||
|
|
|
@ -3,19 +3,19 @@
|
|||
# docker run -d -p 4444:4444 selenium/standalone-chrome
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
import pytest
|
||||
|
||||
import random, string
|
||||
|
||||
#driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
#driver.implicitly_wait(10)
|
||||
driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
driver.implicitly_wait(10)
|
||||
|
||||
HOME_PAGE = 'http://121.4.94.30:91/'
|
||||
|
||||
|
||||
|
||||
|
||||
@pytest.mark.usefixtures
|
||||
def test_signup(URL, driver):
|
||||
def test_signup():
|
||||
try:
|
||||
driver.get(URL)
|
||||
driver.get(HOME_PAGE)
|
||||
driver.save_screenshot('test_signup_pic0.png')
|
||||
|
||||
assert 'English Pal -' in driver.page_source
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
from datetime import datetime
|
||||
|
||||
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
|
||||
|
||||
# 初始化蓝图
|
||||
userService = Blueprint("user_bp", __name__)
|
||||
|
||||
path_prefix = '/var/www/wordfreq/wordfreq/'
|
||||
path_prefix = './' # comment this line in deployment
|
||||
|
||||
|
||||
@userService.route("/<username>/reset", methods=['GET', 'POST'])
|
||||
def user_reset(username):
|
||||
'''
|
||||
用户界面
|
||||
:param username: 用户名
|
||||
:return: 返回页面内容
|
||||
'''
|
||||
if request.method == 'GET':
|
||||
session['articleID'] = None
|
||||
return redirect(url_for('user_bp.userpage', username=username))
|
||||
else:
|
||||
return 'Under construction'
|
||||
|
||||
|
||||
@userService.route("/<username>/<word>/unfamiliar", methods=['GET', 'POST'])
|
||||
def unfamiliar(username, word):
|
||||
'''
|
||||
|
||||
:param username:
|
||||
:param word:
|
||||
:return:
|
||||
'''
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
pickle_idea.unfamiliar(user_freq_record, word)
|
||||
session['thisWord'] = word # 1. put a word into session
|
||||
session['time'] = 1
|
||||
return redirect(url_for('user_bp.userpage', username=username))
|
||||
|
||||
|
||||
@userService.route("/<username>/<word>/familiar", methods=['GET', 'POST'])
|
||||
def familiar(username, word):
|
||||
'''
|
||||
|
||||
:param username:
|
||||
:param word:
|
||||
:return:
|
||||
'''
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
pickle_idea.familiar(user_freq_record, word)
|
||||
session['thisWord'] = word # 1. put a word into session
|
||||
session['time'] = 1
|
||||
return redirect(url_for('user_bp.userpage', username=username))
|
||||
|
||||
|
||||
@userService.route("/<username>/<word>/del", methods=['GET', 'POST'])
|
||||
def deleteword(username, word):
|
||||
'''
|
||||
删除单词
|
||||
:param username: 用户名
|
||||
:param word: 单词
|
||||
:return: 重定位到用户界面
|
||||
'''
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
pickle_idea2.deleteRecord(user_freq_record, word)
|
||||
flash(f'<strong>{word}</strong> is no longer in your word list.')
|
||||
return redirect(url_for('user_bp.userpage', username=username))
|
||||
|
||||
|
||||
@userService.route("/<username>", methods=['GET', 'POST'])
|
||||
def userpage(username):
|
||||
'''
|
||||
用户界面
|
||||
:param username: 用户名
|
||||
:return: 返回用户界面
|
||||
'''
|
||||
# 未登录,跳转到未登录界面
|
||||
if not session.get('logged_in'):
|
||||
return render_template('not_login.html')
|
||||
|
||||
# 用户过期
|
||||
user_expiry_date = session.get('expiry_date')
|
||||
if datetime.now().strftime('%Y%m%d') > user_expiry_date:
|
||||
return render_template('expiry.html')
|
||||
|
||||
# 获取session里的用户名
|
||||
username = session.get('username')
|
||||
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
|
||||
if request.method == 'POST': # when we submit a form
|
||||
content = request.form['content']
|
||||
f = WordFreq(content)
|
||||
lst = f.get_freq()
|
||||
return render_template('userpage_post.html',username=username,lst = lst, yml=Yaml.yml)
|
||||
|
||||
elif request.method == 'GET': # when we load a html page
|
||||
d = load_freq_history(user_freq_record)
|
||||
lst = pickle_idea2.dict2lst(d)
|
||||
lst2 = []
|
||||
for t in lst:
|
||||
lst2.append((t[0], len(t[1])))
|
||||
lst3 = sort_in_descending_order(lst2)
|
||||
words = ''
|
||||
for x in lst3:
|
||||
words += x[0] + ' '
|
||||
return render_template('userpage_get.html',
|
||||
username=username,
|
||||
session=session,
|
||||
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("/<username>/mark", methods=['GET', 'POST'])
|
||||
def user_mark_word(username):
|
||||
'''
|
||||
标记单词
|
||||
:param username: 用户名
|
||||
:return: 重定位到用户界面
|
||||
'''
|
||||
username = session[username]
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
if request.method == 'POST':
|
||||
# 提交标记的单词
|
||||
d = load_freq_history(user_freq_record)
|
||||
lst_history = pickle_idea2.dict2lst(d)
|
||||
lst = []
|
||||
for word in request.form.getlist('marked'):
|
||||
lst.append((word, [get_time()]))
|
||||
d = pickle_idea2.merge_frequency(lst, lst_history)
|
||||
pickle_idea2.save_frequency_to_pickle(d, user_freq_record)
|
||||
return redirect(url_for('user_bp.userpage', username=username))
|
||||
else:
|
||||
return 'Under construction'
|
||||
|
||||
def get_time():
|
||||
'''
|
||||
获取当前时间
|
||||
:return: 当前时间
|
||||
'''
|
||||
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 += '<div class="alert alert-warning" role="alert">'
|
||||
s += f'Congratulations! {message}'
|
||||
s += '</div>'
|
||||
return s
|
|
@ -1,2 +1,3 @@
|
|||
Flask==1.1.2
|
||||
selenium==3.141.0
|
||||
PyYAML~=6.0
|
||||
|
|
Loading…
Reference in New Issue