forked from mrlan/EnglishPal
				
			Merge branch 'master' of http://121.4.94.30:3000/mrlan/EnglishPal into Lanhui-update-README
						commit
						84e98d8bd9
					
				|  | @ -55,11 +55,17 @@ def get_difficulty_level(d1, d2): | ||||||
| 
 | 
 | ||||||
| def revert_dict(d): | def revert_dict(d): | ||||||
|     ''' |     ''' | ||||||
|     In d2, time is the key, and the value is a list of words picked at that time. |     In d, word is the key, and value is a list of dates. | ||||||
|  |     In d2 (the returned value of this function), time is the key, and the value is a list of words picked at that time. | ||||||
|     ''' |     ''' | ||||||
|     d2 = {} |     d2 = {} | ||||||
|     for k in d: |     for k in d: | ||||||
|  |         if type(d[k]) is list:  # d[k] is a list of dates. | ||||||
|             lst = d[k] |             lst = d[k] | ||||||
|  |         elif type(d[k]) is int: # for backward compatibility.  d was sth like {'word':1}.  The value d[k] is not a list of dates, but a number representing how frequent this word had been added to the new word book.  | ||||||
|  |             freq = d[k] | ||||||
|  |             lst = freq*['2021082019'] # why choose this date?  No particular reasons.  I fix the bug in this date. | ||||||
|  | 
 | ||||||
|         for time_info in lst: |         for time_info in lst: | ||||||
|             date = time_info[:10] # until hour |             date = time_info[:10] # until hour | ||||||
|             if not date in d2: |             if not date in d2: | ||||||
|  |  | ||||||
							
								
								
									
										74
									
								
								app/main.py
								
								
								
								
							
							
						
						
									
										74
									
								
								app/main.py
								
								
								
								
							|  | @ -13,7 +13,7 @@ import pickle_idea, pickle_idea2 | ||||||
| import os | import os | ||||||
| import random, glob | import random, glob | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
| from flask import Flask, request, redirect, render_template, url_for, session, abort, flash | from flask import Flask, request, redirect, render_template, url_for, session, abort, flash, get_flashed_messages | ||||||
| from difficulty import get_difficulty_level, text_difficulty_level, user_difficulty_level | from difficulty import get_difficulty_level, text_difficulty_level, user_difficulty_level | ||||||
| 
 | 
 | ||||||
| app = Flask(__name__) | app = Flask(__name__) | ||||||
|  | @ -81,6 +81,15 @@ def within_range(x, y, r): | ||||||
|     return x > y and abs(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): | def get_today_article(user_word_list, articleID): | ||||||
| 
 | 
 | ||||||
|     rq = RecordQuery(path_prefix + 'static/wordfreqapp.db') |     rq = RecordQuery(path_prefix + 'static/wordfreqapp.db') | ||||||
|  | @ -90,6 +99,7 @@ def get_today_article(user_word_list, articleID): | ||||||
|         rq.instructions('SELECT * FROM article WHERE article_id=%d' % (articleID)) |         rq.instructions('SELECT * FROM article WHERE article_id=%d' % (articleID)) | ||||||
|     rq.do() |     rq.do() | ||||||
|     result = rq.get_results() |     result = rq.get_results() | ||||||
|  |     random.shuffle(result) | ||||||
|      |      | ||||||
|     # Choose article according to reader's level |     # Choose article according to reader's level | ||||||
|     d1 = load_freq_history(path_prefix + 'static/frequency/frequency.p') |     d1 = load_freq_history(path_prefix + 'static/frequency/frequency.p') | ||||||
|  | @ -105,18 +115,23 @@ def get_today_article(user_word_list, articleID): | ||||||
|     if articleID == None: |     if articleID == None: | ||||||
|         for reading in result: |         for reading in result: | ||||||
|             text_level = text_difficulty_level(reading['text'], d3) |             text_level = text_difficulty_level(reading['text'], d3) | ||||||
|             #print('TEXT_LEVEL %4.2f' % (text_level)) |             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, 0.5): |             if within_range(text_level, user_level, (8.0 - user_level)*factor): | ||||||
|                 d = reading |                 d = reading | ||||||
|                 break |                 break | ||||||
|              |              | ||||||
|     s = '<p><i>According to your word list, your level is <b>%4.2f</b> and we have chosen an article with a difficulty level of <b>%4.2f</b> for you.</i></p>' % (user_level, text_level) |     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><b>%s</b></p>' % (d['date']) |     s += '<p class="text-muted">Article added on: %s</p>' % (d['date']) | ||||||
|     s += '<p><font size=+2>%s</font></p>' % (d['text']) |     s += '<div class="p-3 mb-2 bg-light text-dark">' | ||||||
|     s += '<p><i>%s</i></p>' % (d['source']) |     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 += '<p><b>%s</b></p>' % (get_question_part(d['question'])) | ||||||
|     s = s.replace('\n', '<br/>')     |     s = s.replace('\n', '<br/>')     | ||||||
|     s += '%s' % (get_answer_part(d['question'])) |     s += '%s' % (get_answer_part(d['question'])) | ||||||
|  |     s += '</div>' | ||||||
|     session['articleID'] = d['article_id'] |     session['articleID'] = d['article_id'] | ||||||
|     return s |     return s | ||||||
| 
 | 
 | ||||||
|  | @ -178,6 +193,15 @@ def get_answer_part(s): | ||||||
|     return html_code |     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']) | @app.route("/<username>/reset", methods=['GET', 'POST']) | ||||||
| def user_reset(username): | def user_reset(username): | ||||||
|  | @ -230,11 +254,14 @@ def mainpage(): | ||||||
|                <head> |                <head> | ||||||
|                <meta charset="utf-8"> |                <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="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> |                  <title>EnglishPal 英文单词高效记</title> | ||||||
| 
 | 
 | ||||||
|                </head> |                </head> | ||||||
|                <body> |                <body> | ||||||
|         ''' |         ''' | ||||||
|  |         page += '<div class="container-fluid">' | ||||||
|         page += '<p><b><font size="+3" color="red">English Pal - Learn English smartly!</font></b></p>' |         page += '<p><b><font size="+3" color="red">English Pal - Learn English smartly!</font></b></p>' | ||||||
|         if session.get('logged_in'): |         if session.get('logged_in'): | ||||||
|             page += ' <a href="%s">%s</a></p>\n' % (session['username'], session['username']) |             page += ' <a href="%s">%s</a></p>\n' % (session['username'], session['username']) | ||||||
|  | @ -242,7 +269,7 @@ def mainpage(): | ||||||
|             page += '<p><a href="/login">登录</a>  <a href="/signup">成为会员</a> <a href="/static/usr/instructions.html">使用说明</a></p>\n' |             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><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 += '<p><b>%s</b></p>' % (get_random_ads()) | ||||||
|         page += '<p>共有文章%d篇</b>' % (total_number_of_essays()) |         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 += '<p>粘帖1篇文章 (English only)</p>' | ||||||
|         page += '<form method="post" action="/">' |         page += '<form method="post" action="/">' | ||||||
|         page += ' <textarea name="content" rows="10" cols="120"></textarea><br/>' |         page += ' <textarea name="content" rows="10" cols="120"></textarea><br/>' | ||||||
|  | @ -257,6 +284,8 @@ def mainpage(): | ||||||
|                     break |                     break | ||||||
|                 page += '<a href="%s">%s</a> %d\n' % (youdao_link(x[0]), x[0], x[1]) |                 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>' |         page += '</body></html>' | ||||||
|         return page |         return page | ||||||
| 
 | 
 | ||||||
|  | @ -283,6 +312,7 @@ def unfamiliar(username,word): | ||||||
|     user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username) |     user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username) | ||||||
|     pickle_idea.unfamiliar(user_freq_record,word) |     pickle_idea.unfamiliar(user_freq_record,word) | ||||||
|     session['thisWord'] = word  # 1. put a word into session |     session['thisWord'] = word  # 1. put a word into session | ||||||
|  |     session['time'] = 1 | ||||||
|     return redirect(url_for('userpage', username=username)) |     return redirect(url_for('userpage', username=username)) | ||||||
| 
 | 
 | ||||||
| @app.route("/<username>/<word>/familiar", methods=['GET', 'POST']) | @app.route("/<username>/<word>/familiar", methods=['GET', 'POST']) | ||||||
|  | @ -290,12 +320,14 @@ def familiar(username,word): | ||||||
|     user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username) |     user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username) | ||||||
|     pickle_idea.familiar(user_freq_record,word) |     pickle_idea.familiar(user_freq_record,word) | ||||||
|     session['thisWord'] = word  # 1. put a word into session |     session['thisWord'] = word  # 1. put a word into session | ||||||
|  |     session['time'] = 1 | ||||||
|     return redirect(url_for('userpage', username=username)) |     return redirect(url_for('userpage', username=username)) | ||||||
| 
 | 
 | ||||||
| @app.route("/<username>/<word>/del", methods=['GET', 'POST']) | @app.route("/<username>/<word>/del", methods=['GET', 'POST']) | ||||||
| def deleteword(username,word): | def deleteword(username,word): | ||||||
|     user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username) |     user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username) | ||||||
|     pickle_idea2.deleteRecord(user_freq_record,word) |     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)) |     return redirect(url_for('userpage', username=username)) | ||||||
| 
 | 
 | ||||||
| @app.route("/<username>", methods=['GET', 'POST']) | @app.route("/<username>", methods=['GET', 'POST']) | ||||||
|  | @ -334,10 +366,13 @@ def userpage(username): | ||||||
|         page = '<meta charset="UTF8">\n' |         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="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 += '<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 += '<title>EnglishPal Study Room for %s</title>' % (username) | ||||||
|         page += '<p><b>English Pal for <font color="red">%s</font></b> <a href="/logout">登出</a></p>' % (username) |         page += '<div class="container-fluid">' | ||||||
|         page += '<p><a href="/%s/reset">下一篇</a></p>' % (username) |         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><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 += '<div id="text-content">%s</div>'  % (get_today_article(user_freq_record, session['articleID'])) | ||||||
|         page += '<p><b>收集生词吧</b> (可以在正文中划词,也可以复制黏贴)</p>' |         page += '<p><b>收集生词吧</b> (可以在正文中划词,也可以复制黏贴)</p>' | ||||||
|         page += '<form method="post" action="/%s">' % (username) |         page += '<form method="post" action="/%s">' % (username) | ||||||
|  | @ -362,7 +397,12 @@ def userpage(username): | ||||||
|         if session.get('thisWord'): |         if session.get('thisWord'): | ||||||
|             page += ''' |             page += ''' | ||||||
|                    <script type="text/javascript"> |                    <script type="text/javascript"> | ||||||
|                        location.href = "#aaa"  // 2. define a anchor URL and point to the anchor in the page whose id is aaa |                         //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>  |                    </script>  | ||||||
|                    ''' |                    ''' | ||||||
| 
 | 
 | ||||||
|  | @ -376,15 +416,18 @@ def userpage(username): | ||||||
|             for x in sort_in_descending_order(lst2): |             for x in sort_in_descending_order(lst2): | ||||||
|                 word = x[0] |                 word = x[0] | ||||||
|                 freq = x[1] |                 freq = x[1] | ||||||
|                 if session.get('thisWord') == x[0]: |                 if session.get('thisWord') == x[0] and session.get('time') == 1: | ||||||
|                     page += '<a name="aaa"></a>'    # 3. anchor |                     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 isinstance(d[word], list): # d[word] is a list of dates | ||||||
|                     if freq > 1: |                     if freq > 1: | ||||||
|                         page += '<p class="new-word"> <a href="%s">%s</a>(<a title="%s">%d</a>) <a href="%s/%s/familiar">熟悉</a> <a href="%s/%s/unfamiliar">不熟悉</a>  <a href="%s/%s/del">删除</a> </p>\n' % (youdao_link(word), word, '; '.join(d[word]), freq,username, word,username,word, username,word) |                         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: |                     else: | ||||||
|                         page += '<p class="new-word"> <a href="%s">%s</a>(<a title="%s">%d</a>) <a href="%s/%s/familiar">熟悉</a> <a href="%s/%s/unfamiliar">不熟悉</a>  <a href="%s/%s/del" >删除</a> </p>\n' % (youdao_link(word), word, '; '.join(d[word]), freq,username, word,username,word, username,word) |                         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. |                 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 += '<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 |         return page | ||||||
| 
 | 
 | ||||||
| ### Sign-up, login, logout ### | ### Sign-up, login, logout ### | ||||||
|  | @ -409,6 +452,8 @@ def signup(): | ||||||
|                 session['logged_in'] = True |                 session['logged_in'] = True | ||||||
|                 session[username] = username |                 session[username] = username | ||||||
|                 session['username'] = username |                 session['username'] = username | ||||||
|  |                 session['expiry_date'] = get_expiry_date(username) | ||||||
|  |                 session['articleID'] = None | ||||||
|                 return '<p>恭喜,你已成功注册, 你的用户名是 <a href="%s">%s</a>。</p>\ |                 return '<p>恭喜,你已成功注册, 你的用户名是 <a href="%s">%s</a>。</p>\ | ||||||
|                 <p><a href="/%s">开始使用</a> <a href="/">返回首页</a><p/>' % (username, username, username) |                 <p><a href="/%s">开始使用</a> <a href="/">返回首页</a><p/>' % (username, username, username) | ||||||
|             else: |             else: | ||||||
|  | @ -451,4 +496,3 @@ if __name__ == '__main__': | ||||||
|     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') | ||||||
| 
 |  | ||||||
|  |  | ||||||
							
								
								
									
										3
									
								
								build.sh
								
								
								
								
							
							
						
						
									
										3
									
								
								build.sh
								
								
								
								
							|  | @ -4,12 +4,13 @@ cd /home/lanhui/englishpal | ||||||
| 
 | 
 | ||||||
| # Stop service | # Stop service | ||||||
| sudo docker stop EnglishPal | sudo docker stop EnglishPal | ||||||
|  | sudo docker rm EnglishPal | ||||||
| 
 | 
 | ||||||
| # Rebuild container. Run this after modifying the source code. | # Rebuild container. Run this after modifying the source code. | ||||||
| sudo docker build -t englishpal . | sudo docker build -t englishpal . | ||||||
| 
 | 
 | ||||||
| # Run the application | # Run the application | ||||||
| sudo docker run -d --name EnglishPal -p 90:80 -v /home/lanhui/englishpal/app/static/frequency:/app/static/frequency -t englishpal  # for permanently saving data | sudo docker run -d --name EnglishPal -p 90:80 -v /home/lanhui/englishpal/app/static/frequency:/app/static/frequency -v /home/lanhui/englishpal/app/static/:/app/static/ -t englishpal  # for permanently saving data | ||||||
| 
 | 
 | ||||||
| # Save space.  Run it after sudo docker run | # Save space.  Run it after sudo docker run | ||||||
| sudo docker system prune -a -f | sudo docker system prune -a -f | ||||||
|  |  | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | Flask==1.1.2 | ||||||
|  | selenium==3.141.0 | ||||||
		Loading…
	
		Reference in New Issue