1
0
Fork 0

Compare commits

..

18 Commits

Author SHA1 Message Date
Lan Hui c64af4a20a wordfreqapp.db should be placed under app/db (not app/static) to protect data 2026-03-12 16:04:03 +08:00
Lan Hui 6285581bb5 Fix the webpage CRASH problem when the database has no article or when something bad happened on loading articles. 2026-03-12 16:00:53 +08:00
Lan Hui c9bbf6b6a3 Remove ambiguities: The Oxford word coverage is X% -> X% of the words in this article are in Oxford Word 5000. 2026-03-12 15:50:59 +08:00
Lan Hui 68e4ba33c5 mainpage_get.html: 牛津单词占比率(文章中有多少单词来自 oxford 5000),而不是覆盖率(文章覆盖了多少 oxford 5000 单词) 2025-09-29 15:28:41 +08:00
Lan Hui d9512c929b Add dependency for api_service.py 2025-04-21 15:19:06 +08:00
mrlan ce58f14406 Merge pull request 'Fix bug 580' (#188) from Bug580-Hui into Alpha-snapshot20240618
Reviewed-on: mrlan/EnglishPal#188
2024-09-08 12:19:02 +08:00
Lan Hui 05dc0ecbb7 Fix bug 580 2024-09-08 12:18:07 +08:00
mrlan 8859827eac Merge pull request 'Fix bug 565' (#187) from Bug565-Hui into Alpha-snapshot20240618
Reviewed-on: mrlan/EnglishPal#187
2024-09-07 09:04:35 +08:00
Lan Hui b6a36b1d1a Fix bug 565 2024-09-07 09:03:59 +08:00
mrlan 12752341db Merge pull request 'Fix bug 394' (#185) from Bug394-Hui into Alpha-snapshot20240618
Reviewed-on: mrlan/EnglishPal#185
2024-09-06 08:55:35 +08:00
Lan Hui 3abebdfb21 Fix bug 394 2024-09-06 08:54:11 +08:00
mrlan 92331ca7a0 Merge pull request 'Fix bug 478' (#184) from Bug478-Hui into Alpha-snapshot20240618
Reviewed-on: mrlan/EnglishPal#184
2024-09-05 10:12:21 +08:00
Lan Hui 1166d3499f Fix bug 478 2024-09-05 10:11:20 +08:00
mrlan fe1d4c29ff Merge pull request 'Fix Bug 501. The key fix is the first line in funciton wordTemplate() from word_operation.js, i.e., using double quotes instead of single quotes.' (#183) from Bug501-Hui into Alpha-snapshot20240618
Reviewed-on: mrlan/EnglishPal#183
2024-09-04 15:17:23 +08:00
Lan Hui aaf2db8657 Fix Bug 501. The key fix is the first line in funciton wordTemplate() from word_operation.js, i.e., using double quotes instead of single quotes. 2024-09-04 15:15:03 +08:00
mrlan e478217343 Merge pull request 'main.py: fix Bug 511' (#182) from Bug511-Hui into Alpha-snapshot20240618
Reviewed-on: mrlan/EnglishPal#182
2024-09-04 10:22:56 +08:00
Lan Hui 4f95935037 main.py: fix Bug 511 2024-09-04 10:21:55 +08:00
mrlan 370a215d08 Merge pull request 'pickle_idea.py: Make funciton unfamiliar() more robust' (#181) from Bug518-Hui into Alpha-snapshot20240618
Reviewed-on: mrlan/EnglishPal#181
2024-09-03 11:43:10 +08:00
12 changed files with 88 additions and 46 deletions

View File

@ -18,7 +18,7 @@ picked from articles selected for him to read according his vocabulary level. E
`python3 main.py` `python3 main.py`
Make sure you have put the SQLite database file in the path `app/static` (see below). Make sure you have put the SQLite database file in the path `app/db` (see below).
## Run it as a Docker container ## Run it as a Docker container
@ -214,5 +214,5 @@ Bug report: http://118.25.96.118/bugzilla/show_bug.cgi?id=215
Bug report: http://118.25.96.118/bugzilla/show_bug.cgi?id=489 Bug report: http://118.25.96.118/bugzilla/show_bug.cgi?id=489
*Last modified on 2023-01-30* *Last modified on 2026-03-12*

View File

@ -106,7 +106,7 @@ def get_today_article(user_word_list, visited_articles):
text_level = text_difficulty_level(d['text'], d3) text_level = text_difficulty_level(d['text'], d3)
result_of_generate_article = "found" result_of_generate_article = "found"
today_article = None today_article = {}
if d: if d:
oxford_words = load_oxford_words(oxford_words_path) oxford_words = load_oxford_words(oxford_words_path)
oxford_word_count, total_words = count_oxford_words(d['text'],oxford_words) oxford_word_count, total_words = count_oxford_words(d['text'],oxford_words)

View File

@ -21,11 +21,6 @@ path_prefix = '/var/www/wordfreq/wordfreq/'
path_prefix = './' # comment this line in deployment path_prefix = './' # comment this line in deployment
def verify_pass(newpass, oldpass):
if (newpass == oldpass):
return True
def verify_user(username, password): def verify_user(username, password):
user = get_user_by_username(username) user = get_user_by_username(username)
encoded_password = md5(username + password) encoded_password = md5(username + password)
@ -54,12 +49,12 @@ def change_password(username, old_password, new_password):
:return: 修改成功:True 否则:False :return: 修改成功:True 否则:False
''' '''
if not verify_user(username, old_password): # 旧密码错误 if not verify_user(username, old_password): # 旧密码错误
return False return {'error':'Old password is wrong.', 'username':username}
# 将用户名和密码一起加密,以免暴露不同用户的相同密码 # 将用户名和密码一起加密,以免暴露不同用户的相同密码
if verify_pass(new_password, old_password): #新旧密码一致 if new_password == old_password: #新旧密码一致
return False return {'error':'New password cannot be the same as the old password.', 'username':username}
update_password_by_username(username, new_password) update_password_by_username(username, new_password)
return True return {'success':'Password changed', 'username':username}
def get_expiry_date(username): def get_expiry_date(username):

View File

@ -133,10 +133,7 @@ def reset():
# POST请求用于提交修改后信息 # POST请求用于提交修改后信息
old_password = escape(request.form['old-password']) old_password = escape(request.form['old-password'])
new_password = escape(request.form['new-password']) new_password = escape(request.form['new-password'])
flag = change_password(username, old_password, new_password) # flag表示是否修改成功 result = change_password(username, old_password, new_password)
if flag: return jsonify(result)
session['logged_in'] = False
return jsonify({'status':'1'}) # 修改成功
else:
return jsonify({'status':'2'}) # 修改失败

View File

@ -4,6 +4,7 @@
########################################################################### ###########################################################################
from flask import abort, jsonify from flask import abort, jsonify
from markupsafe import escape from markupsafe import escape
from collections import Counter
from Login import * from Login import *
from Article import * from Article import *
import Yaml import Yaml
@ -58,6 +59,12 @@ def appears_in_test(word, d):
else: else:
return ','.join(d[word]) return ','.join(d[word])
def good_word(word):
return len(word) < len('Pneumonoultramicroscopicsilicovolcanoconiosis') \
and Counter(word).most_common(1)[0][1] <= 4
@app.route("/mark", methods=['GET', 'POST']) @app.route("/mark", methods=['GET', 'POST'])
def mark_word(): def mark_word():
''' '''
@ -99,7 +106,7 @@ def mainpage():
if request.method == 'POST': # when we submit a form if request.method == 'POST': # when we submit a form
content = escape(request.form['content']) content = escape(request.form['content'])
f = WordFreq(content) f = WordFreq(content)
lst = f.get_freq() lst = [ t for t in f.get_freq() if good_word(t[0]) ] # only keep normal words
# save history # save history
d = load_freq_history(path_prefix + 'static/frequency/frequency.p') d = load_freq_history(path_prefix + 'static/frequency/frequency.p')
lst_history = pickle_idea.dict2lst(d) lst_history = pickle_idea.dict2lst(d)
@ -137,8 +144,8 @@ if __name__ == '__main__':
运行程序 运行程序
''' '''
# app.secret_key = os.urandom(16) # app.secret_key = os.urandom(16)
# app.run(debug=False, port='6000') app.run(debug=True, port=5000)
app.run(debug=True) # app.run(debug=True)
# app.run(debug=True, port='6000') # app.run(debug=True, port='6000')
# app.run(host='0.0.0.0', debug=True, port='6000') # app.run(host='0.0.0.0', debug=True, port='6000')
# print(mod5('123')) # print(mod5('123'))

20
app/static/js/password.js Normal file
View File

@ -0,0 +1,20 @@
function containsDigitsLettersSpecialCharacters(s) {
let resultD = 0, resultL = 0, resultS = 0;
// Digit test
'0123456789'.split('').forEach((x) => {
if (s.includes(x))
resultD = 1;
});
// Letter test
resultL = /[a-z]/i.test(s);
// Special charater test
'+-*/,.:;/\[]<>$%&()!?^~'.split('').forEach((x) => {
if (s.includes(x))
resultS = 1;
});
return resultD + resultL + resultS == 3;
}

View File

@ -1,7 +1,9 @@
function familiar(theWord) { function familiar(theWord) {
let username = $("#username").text(); let username = $("#username").text();
let word = $("#word_" + theWord).text(); let word = document.getElementById(`word_${theWord}`).innerText;
let freq = $("#freq_" + theWord).text(); let freq = document.getElementById(`freq_${theWord}`).innerText;
console.log(theWord);
console.log(word);
$.ajax({ $.ajax({
type:"GET", type:"GET",
url:"/" + username + "/" + word + "/familiar", url:"/" + username + "/" + word + "/familiar",
@ -27,8 +29,10 @@ function familiar(theWord) {
function unfamiliar(theWord) { function unfamiliar(theWord) {
let username = $("#username").text(); let username = $("#username").text();
let word = $("#word_" + theWord).text(); let word = document.getElementById(`word_${theWord}`).innerText;
let freq = $("#freq_" + theWord).text(); let freq = document.getElementById(`freq_${theWord}`).innerText;
console.log(theWord);
console.log(word);
$.ajax({ $.ajax({
type:"GET", type:"GET",
url:"/" + username + "/" + word + "/unfamiliar", url:"/" + username + "/" + word + "/unfamiliar",
@ -57,6 +61,12 @@ function delete_word(theWord) {
} else { } else {
$("#p_" + theWord).remove(); $("#p_" + theWord).remove();
} }
// remove highlighting for the word
let highlightedWords = document.querySelectorAll('.highlighted');
for (let x of highlightedWords) {
if (x.innerHTML == word)
x.replaceWith(x.innerHTML);
}
} }
}); });
} }
@ -83,7 +93,9 @@ function parseWord(element) {
const word = element const word = element
.querySelector("a.btn.btn-light[role=button]") // 获取当前词频元素的词汇元素 .querySelector("a.btn.btn-light[role=button]") // 获取当前词频元素的词汇元素
.innerText // 获取词汇值; .innerText // 获取词汇值;
const freq = Number.parseInt(element.querySelector(`#freq_${word}`).innerText); // 获取词汇的数量 let freqId = `freq_${word}`;
freqId = CSS.escape(freqId); // for fixing bug 580, escape the apostrophe in the word
const freq = Number.parseInt(element.querySelector("#"+freqId).innerText); // 获取词汇的数量
return { return {
word, word,
freq freq
@ -95,14 +107,14 @@ function parseWord(element) {
*/ */
function wordTemplate(word) { function wordTemplate(word) {
// 这个模板应当与 templates/userpage_get.html 中的 <p id='p_${word.word}' class="new-word" > ... </p> 保持一致 // 这个模板应当与 templates/userpage_get.html 中的 <p id='p_${word.word}' class="new-word" > ... </p> 保持一致
return `<p id='p_${word.word}' class="new-word" > return `<p id="p_${word.word}" class="new-word" >
<a id="word_${word.word}" class="btn btn-light" href='http://youdao.com/w/eng/${word.word}/#keyfrom=dict2.index' <a id="word_${word.word}" class="btn btn-light" href='http://youdao.com/w/eng/${word.word}/#keyfrom=dict2.index'
role="button">${word.word}</a> role="button">${word.word}</a>
( <a id="freq_${word.word}" title="${word.word}">${word.freq}</a> ) ( <a id="freq_${word.word}" title="${word.word}">${word.freq}</a> )
<a class="btn btn-success" onclick="familiar('${word.word}')" role="button">熟悉</a> <a class="btn btn-success" onclick=familiar("${word.word}") role="button">熟悉</a>
<a class="btn btn-warning" onclick="unfamiliar('${word.word}')" role="button">不熟悉</a> <a class="btn btn-warning" onclick=unfamiliar("${word.word}") role="button">不熟悉</a>
<a class="btn btn-danger" onclick="delete_word('${word.word}')" role="button">删除</a> <a class="btn btn-danger" onclick=delete_word("${word.word}") role="button">删除</a>
<a class="btn btn-info" onclick="read_word('${word.word}')" role="button">朗读</a> <a class="btn btn-info" onclick=read_word("${word.word}") role="button">朗读</a>
<a class="btn btn-primary" onclick="addNote('{{ word }}'); saveNote('{{ word }}')" role="button">笔记</a> <!-- Modify to call addNote and then saveNote --> <a class="btn btn-primary" onclick="addNote('{{ word }}'); saveNote('{{ word }}')" role="button">笔记</a> <!-- Modify to call addNote and then saveNote -->
<input type="text" id="note_{{ word }}" class="note-input" placeholder="输入笔记内容" style="display:none;" oninput="saveNote('{{ word }}')"> <!-- Added oninput event --> <input type="text" id="note_{{ word }}" class="note-input" placeholder="输入笔记内容" style="display:none;" oninput="saveNote('{{ word }}')"> <!-- Added oninput event -->
</p>`; </p>`;

View File

@ -31,7 +31,7 @@
<p><a href="/login">登录</a> <a href="/signup">注册</a> <a href="/static/usr/instructions.html">使用说明</a></p > <p><a href="/login">登录</a> <a href="/signup">注册</a> <a href="/static/usr/instructions.html">使用说明</a></p >
<p><b> {{ random_ads }}。 <a href="/signup">试试</a>吧!</b></p> <p><b> {{ random_ads }}。 <a href="/signup">试试</a>吧!</b></p>
{% endif %} {% endif %}
<div class="alert alert-success" role="alert">共有文章 <span class="badge bg-success"> {{ number_of_essays }} </span> 篇,覆盖 <span class="badge bg-success"> {{ (ratio * 100) | int }}% </span> 的 Oxford5000 单词</div> <div class="alert alert-success" role="alert">共有文章 <span class="badge bg-success"> {{ number_of_essays }} </span> 篇,Oxford 5000 单词占比 <span class="badge bg-success"> {{ (ratio * 100) | int }}% </span> </div>
<p>粘贴1篇文章 (English only)</p> <p>粘贴1篇文章 (English only)</p>
<form method="post" action="/"> <form method="post" action="/">
<textarea name="content" id="article" rows="10" cols="120"></textarea><br/> <textarea name="content" id="article" rows="10" cols="120"></textarea><br/>

View File

@ -3,6 +3,7 @@
content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes"/> content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes"/>
<link rel="stylesheet" href="static/css/login_service.css"> <link rel="stylesheet" href="static/css/login_service.css">
<script src="static/js/jquery.js"></script> <script src="static/js/jquery.js"></script>
<script src="static/js/password.js"></script>
<script> <script>
function reset() { function reset() {
let old_password = $("#old-password").val(); let old_password = $("#old-password").val();
@ -24,15 +25,19 @@
alert('密码过于简单。(密码长度至少4位)'); alert('密码过于简单。(密码长度至少4位)');
return false; return false;
} }
if (!containsDigitsLettersSpecialCharacters(new_password)) {
alert('密码过于简单。(密码要包括数字,字母,特殊符号)');
return false;
}
$.post("/reset", {'old-password': old_password, 'new-password': new_password}, $.post("/reset", {'old-password': old_password, 'new-password': new_password},
function (response) { function (response) {
if (response.status === '1') { console.log(response);
alert('密码修改成功,请重新登录。'); if ('success' in response) {
window.location.href = "/login"; alert('密码修改成功。');
} else if (response.status === '2') { } else if ('error' in response) {
alert('密码修改失败'); alert(`密码修改失败 ${response.error}`);
window.location.href = "/reset";
} }
window.location.href = `/${response.username}/userpage`;
} }
) )
return false; return false;

View File

@ -7,6 +7,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE-edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE-edge,chrome=1">
<link href="static/css/slide-unlock.css" rel="stylesheet"> <link href="static/css/slide-unlock.css" rel="stylesheet">
<script src="static/js/password.js"></script>
<script src="static/js/jquery.js"></script> <script src="static/js/jquery.js"></script>
<script src="static/js/jquery.slideunlock.js"></script> <script src="static/js/jquery.slideunlock.js"></script>
<script> <script>
@ -44,6 +45,10 @@
alert('密码过于简单。(密码长度至少4位)'); alert('密码过于简单。(密码长度至少4位)');
return false; return false;
} }
if (!containsDigitsLettersSpecialCharacters(password)) {
alert('密码过于简单。(密码要包括数字,字母,特殊符号)');
return false;
}
is_ok = slider.getIsOk(); is_ok = slider.getIsOk();
if(!is_ok){ if(!is_ok){
alert('没有滑动验证'); alert('没有滑动验证');

View File

@ -87,7 +87,7 @@
<div id="text-content"> <div id="text-content">
<div id="found"> <div id="found">
<div class="alert alert-success" role="alert">According to your word list, your level is <span class="text-decoration-underline" id="user_level">{{ today_article["user_level"] }}</span> and we have chosen an article with a difficulty level of <span class="text-decoration-underline" id="text_level">{{ today_article["text_level"] }}</span> for you. The Oxford word coverage is <span class="text-decoration-underline" id="ratio">{{ (today_article["ratio"] * 100) | int }}%.</span></div> <div class="alert alert-success" role="alert">According to your word list, your level is <span class="text-decoration-underline" id="user_level">{{ today_article["user_level"] }}</span> and we have chosen an article with a difficulty level of <span class="text-decoration-underline" id="text_level">{{ today_article["text_level"] }}</span> for you. <span class="text-decoration-underline" id="ratio">{{ (today_article["ratio"] * 100) | int }}%</span> of the words in this article are in Oxford Word 5000.</div>
<p class="text-muted" id="date">Article added on: {{ today_article["date"] }}</p><br/> <p class="text-muted" id="date">Article added on: {{ today_article["date"] }}</p><br/>
<button onclick="saveArticle()" >标记文章</button> <button onclick="saveArticle()" >标记文章</button>
@ -179,10 +179,10 @@
<a id="word_{{ word }}" class="btn btn-light" href='http://youdao.com/w/eng/{{ word }}/#keyfrom=dict2.index' <a id="word_{{ word }}" class="btn btn-light" href='http://youdao.com/w/eng/{{ word }}/#keyfrom=dict2.index'
role="button">{{ word }}</a> role="button">{{ word }}</a>
( <a id="freq_{{ word }}" title="{{ word }}">{{ freq }}</a> ) ( <a id="freq_{{ word }}" title="{{ word }}">{{ freq }}</a> )
<a class="btn btn-success" onclick="familiar('{{ word }}')" role="button">熟悉</a> <a class="btn btn-success" onclick=familiar("{{ word }}") role="button">熟悉</a>
<a class="btn btn-warning" onclick="unfamiliar('{{ word }}')" role="button">不熟悉</a> <a class="btn btn-warning" onclick=unfamiliar("{{ word }}") role="button">不熟悉</a>
<a class="btn btn-danger" onclick="delete_word('{{ word }}')" role="button">删除</a> <a class="btn btn-danger" onclick=delete_word("{{ word }}") role="button">删除</a>
<a class="btn btn-info" onclick="read_word('{{ word }}')" role="button">朗读</a> <a class="btn btn-info" onclick=read_word("{{ word }}") role="button">朗读</a>
<a class="btn btn-primary" onclick="addNote('{{ word }}'); saveNote('{{ word }}')" role="button">笔记</a> <!-- Modify to call addNote and then saveNote --> <a class="btn btn-primary" onclick="addNote('{{ word }}'); saveNote('{{ word }}')" role="button">笔记</a> <!-- Modify to call addNote and then saveNote -->
<input type="text" id="note_{{ word }}" class="note-input" placeholder="输入笔记内容" style="display:none;" oninput="saveNote('{{ word }}')"> <!-- Added oninput event --> <input type="text" id="note_{{ word }}" class="note-input" placeholder="输入笔记内容" style="display:none;" oninput="saveNote('{{ word }}')"> <!-- Added oninput event -->
</p> </p>

View File

@ -6,3 +6,4 @@ snowballstemmer==2.2.0
Werkzeug==2.2.2 Werkzeug==2.2.2
requests requests
pytest~=8.1.1 pytest~=8.1.1
Flask-HTTPAuth==4.4.0