Compare commits
	
		
			1 Commits 
		
	
	
		
			master
			...
			Alpha-snap
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 6210ca432a | 
|  | @ -8,6 +8,7 @@ import hashlib | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
| from flask import Flask, request, redirect, render_template, url_for, session, abort, flash, get_flashed_messages | from flask import Flask, request, redirect, render_template, url_for, session, abort, flash, get_flashed_messages | ||||||
| from difficulty import get_difficulty_level_for_user, text_difficulty_level, user_difficulty_level | from difficulty import get_difficulty_level_for_user, text_difficulty_level, user_difficulty_level | ||||||
|  | from MyPony import * | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| path_prefix = '/var/www/wordfreq/wordfreq/' | path_prefix = '/var/www/wordfreq/wordfreq/' | ||||||
|  | @ -32,22 +33,20 @@ def get_article_body(s): | ||||||
|     return '\n'.join(lst) |     return '\n'.join(lst) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @db_session | ||||||
| def get_today_article(user_word_list, visited_articles): | def get_today_article(user_word_list, visited_articles): | ||||||
|     rq = RecordQuery(path_prefix + 'static/wordfreqapp.db') |  | ||||||
|     if visited_articles is None: |     if visited_articles is None: | ||||||
|         visited_articles = { |         visited_articles = { | ||||||
|             "index" : 0,  # 为 article_ids 的索引 |             "index" : 0,  # 为 article_ids 的索引 | ||||||
|             "article_ids": []  # 之前显示文章的id列表,越后越新 |             "article_ids": []  # 之前显示文章的id列表,越后越新 | ||||||
|         } |         } | ||||||
|     if visited_articles["index"] > len(visited_articles["article_ids"])-1:  # 生成新的文章,因此查找所有的文章 |     if visited_articles["index"] > len(visited_articles["article_ids"])-1:  # 生成新的文章,因此查找所有的文章 | ||||||
|         rq.instructions("SELECT * FROM article") |         result = list(selectAllArticles()) | ||||||
|     else:  # 生成阅读过的文章,因此查询指定 article_id 的文章 |     else:  # 生成阅读过的文章,因此查询指定 article_id 的文章 | ||||||
|         if visited_articles["article_ids"][visited_articles["index"]] == 'null':  # 可能因为直接刷新页面导致直接去查询了'null',因此当刷新的页面的时候,需要直接进行“上一篇”操作 |         if visited_articles["article_ids"][visited_articles["index"]] == 'null':  # 可能因为直接刷新页面导致直接去查询了'null',因此当刷新的页面的时候,需要直接进行“上一篇”操作 | ||||||
|             visited_articles["index"] -= 1 |             visited_articles["index"] -= 1 | ||||||
|             visited_articles["article_ids"].pop() |             visited_articles["article_ids"].pop() | ||||||
|         rq.instructions('SELECT * FROM article WHERE article_id=%d' % (visited_articles["article_ids"][visited_articles["index"]])) |         result = list(selectArticlesById(visited_articles["article_ids"][visited_articles["index"]])) | ||||||
|     rq.do() |  | ||||||
|     result = rq.get_results() |  | ||||||
|     random.shuffle(result) |     random.shuffle(result) | ||||||
| 
 | 
 | ||||||
|     # Choose article according to reader's level |     # Choose article according to reader's level | ||||||
|  |  | ||||||
|  | @ -0,0 +1,55 @@ | ||||||
|  | from pony.orm import * | ||||||
|  | 
 | ||||||
|  | db = Database() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # 数据库实体 | ||||||
|  | class Article(db.Entity): | ||||||
|  |     _table_ = "article"  # table name | ||||||
|  |     article_id = PrimaryKey(int, auto=True) | ||||||
|  |     text = Optional(str) | ||||||
|  |     source = Optional(str) | ||||||
|  |     date = Optional(str) | ||||||
|  |     level = Optional(str) | ||||||
|  |     question = Optional(str) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | db.bind(provider='sqlite', filename='./static/wordfreqapp.db', create_db=True) | ||||||
|  | db.generate_mapping(create_tables=True) | ||||||
|  | # 打印SQL错误 | ||||||
|  | set_sql_debug(True) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @db_session | ||||||
|  | def selectAllArticles(): | ||||||
|  |     """ | ||||||
|  |     获取全部文章 | ||||||
|  |     """ | ||||||
|  |     articles = select(a for a in Article) | ||||||
|  |     articles_list = [] | ||||||
|  |     for i in articles: | ||||||
|  |         d = {"article_id": i.article_id, | ||||||
|  |              "text": i.text, | ||||||
|  |              "source": i.source, | ||||||
|  |              "date": i.date, | ||||||
|  |              "level": i.level, | ||||||
|  |              "question": i.question} | ||||||
|  |         articles_list.append(d) | ||||||
|  |     return articles_list | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @db_session | ||||||
|  | def selectArticlesById(id): | ||||||
|  |     """ | ||||||
|  |     根据文章id搜索 | ||||||
|  |     """ | ||||||
|  |     article = Article.get(article_id=id) | ||||||
|  |     li = [] | ||||||
|  |     d = {"article_id": article.article_id, | ||||||
|  |          "text": article.text, | ||||||
|  |          "source": article.source, | ||||||
|  |          "date": article.date, | ||||||
|  |          "level": article.level, | ||||||
|  |          "question": article.question} | ||||||
|  |     li.append(d) | ||||||
|  |     return li | ||||||
|  | @ -7,7 +7,6 @@ css: | ||||||
| js: | js: | ||||||
|   head: # 在页面加载之前加载 |   head: # 在页面加载之前加载 | ||||||
|     - ../static/js/jquery.js |     - ../static/js/jquery.js | ||||||
|     - ../static/js/read.js |  | ||||||
|     - ../static/js/word_operation.js |     - ../static/js/word_operation.js | ||||||
|   bottom: # 在页面加载完之后加载 |   bottom: # 在页面加载完之后加载 | ||||||
|     - ../static/js/fillword.js |     - ../static/js/fillword.js | ||||||
|  |  | ||||||
|  | @ -1,5 +1,9 @@ | ||||||
| let isRead = true; | let isRead = true; | ||||||
| let isChoose = true; | let isChoose = true; | ||||||
|  | let reader = window.speechSynthesis; // 全局定义朗读者,以便朗读和暂停
 | ||||||
|  | let current_position = 0; // 朗读文本的当前位置
 | ||||||
|  | let original_position = 0; // 朗读文本的初始位置
 | ||||||
|  | let to_speak = ""; // 朗读的初始内容
 | ||||||
| 
 | 
 | ||||||
| function getWord() { | function getWord() { | ||||||
|     return window.getSelection ? window.getSelection() : document.selection.createRange().text; |     return window.getSelection ? window.getSelection() : document.selection.createRange().text; | ||||||
|  | @ -7,7 +11,7 @@ function getWord() { | ||||||
| 
 | 
 | ||||||
| function fillInWord() { | function fillInWord() { | ||||||
|     let word = getWord(); |     let word = getWord(); | ||||||
|     if (isRead) Reader.read(word, inputSlider.value); |     if (isRead) read(word); | ||||||
|     if (!isChoose) return; |     if (!isChoose) return; | ||||||
|     const element = document.getElementById("selected-words"); |     const element = document.getElementById("selected-words"); | ||||||
|     element.value = element.value + " " + word; |     element.value = element.value + " " + word; | ||||||
|  | @ -15,17 +19,50 @@ function fillInWord() { | ||||||
| 
 | 
 | ||||||
| document.getElementById("text-content").addEventListener("click", fillInWord, false); | document.getElementById("text-content").addEventListener("click", fillInWord, false); | ||||||
| 
 | 
 | ||||||
| const sliderValue = document.getElementById("rangeValue"); | function makeUtterance(str, rate) { | ||||||
| const inputSlider = document.getElementById("rangeComponent"); |     let msg = new SpeechSynthesisUtterance(str); | ||||||
|  |     msg.rate = rate; | ||||||
|  |     msg.lang = "en-US"; // TODO: add language options menu
 | ||||||
|  |     msg.onboundary = ev => { | ||||||
|  |         if (ev.name == "word") { | ||||||
|  |             current_position = ev.charIndex; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return msg; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const sliderValue = document.getElementById("rangeValue"); // 显示值
 | ||||||
|  | const inputSlider = document.getElementById("rangeComponent"); // 滑块元素
 | ||||||
| inputSlider.oninput = () => { | inputSlider.oninput = () => { | ||||||
|     let value = inputSlider.value; |     let value = inputSlider.value; // 获取滑块的值
 | ||||||
|     sliderValue.textContent = value + '×'; |     sliderValue.textContent = value + '×'; | ||||||
|  |     if (!reader.speaking) return; | ||||||
|  |     reader.cancel(); | ||||||
|  |     let msg = makeUtterance(to_speak.substring(original_position + current_position), value); | ||||||
|  |     original_position = original_position + current_position; | ||||||
|  |     current_position = 0; | ||||||
|  |     reader.speak(msg); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | function read(s) { | ||||||
|  |     to_speak = s.toString(); | ||||||
|  |     original_position = 0; | ||||||
|  |     current_position = 0; | ||||||
|  |     let msg = makeUtterance(to_speak, inputSlider.value); | ||||||
|  |     reader.speak(msg); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| function onReadClick() { | function onReadClick() { | ||||||
|     isRead = !isRead; |     isRead = !isRead; | ||||||
|  |     if (!isRead) { | ||||||
|  |         reader.cancel(); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function onChooseClick() { | function onChooseClick() { | ||||||
|     isChoose = !isChoose; |     isChoose = !isChoose; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | function stopRead() { | ||||||
|  |     reader.cancel(); | ||||||
|  | } | ||||||
|  | @ -1,35 +0,0 @@ | ||||||
| var Reader = (function() { |  | ||||||
|     let reader = window.speechSynthesis; |  | ||||||
|     let current_position = 0; |  | ||||||
|     let original_position = 0; |  | ||||||
|     let to_speak = ""; |  | ||||||
| 
 |  | ||||||
|     function makeUtterance(str, rate) { |  | ||||||
|         let msg = new SpeechSynthesisUtterance(str); |  | ||||||
|         msg.rate = rate; |  | ||||||
|         msg.lang = "en-US"; |  | ||||||
|         msg.onboundary = ev => { |  | ||||||
|             if (ev.name == "word") { |  | ||||||
|                 current_position = ev.charIndex; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         return msg; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     function read(s, rate) { |  | ||||||
|         to_speak = s.toString(); |  | ||||||
|         original_position = 0; |  | ||||||
|         current_position = 0; |  | ||||||
|         let msg = makeUtterance(to_speak, rate); |  | ||||||
|         reader.speak(msg); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     function stopRead() { |  | ||||||
|         reader.cancel(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return { |  | ||||||
|         read: read, |  | ||||||
|         stopRead: stopRead |  | ||||||
|     }; |  | ||||||
| })(); |  | ||||||
|  | @ -62,13 +62,6 @@ function delete_word(theWord) { | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function read_word(theWord) { |  | ||||||
|     let to_speak = $("#word_" + theWord).text(); |  | ||||||
|     original_position = 0; |  | ||||||
|     current_position = 0; |  | ||||||
|     Reader.read(to_speak, inputSlider.value); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*  | /*  | ||||||
|  * interface Word { |  * interface Word { | ||||||
|  *   word: string, |  *   word: string, | ||||||
|  | @ -102,7 +95,6 @@ function wordTemplate(word) { | ||||||
|         <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> |  | ||||||
|     </p>`; |     </p>`; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,8 +5,6 @@ | ||||||
|     <meta name="viewport" |     <meta name="viewport" | ||||||
|           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"/> | ||||||
|     <meta name="format-detection" content="telephone=no"/> |     <meta name="format-detection" content="telephone=no"/> | ||||||
|     <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet"> |  | ||||||
|     <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js"></script> |  | ||||||
| 
 | 
 | ||||||
|     {{ yml['header'] | safe }} |     {{ yml['header'] | safe }} | ||||||
|     {% if yml['css']['item'] %} |     {% if yml['css']['item'] %} | ||||||
|  | @ -28,40 +26,12 @@ | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         @keyframes shakes { |         @keyframes shakes { | ||||||
|             10%, 90% { |             10%, 90% { transform: translate3d(-1px, 0, 0); } | ||||||
|                 transform: translate3d(-1px, 0, 0); |             20%, 50% { transform: translate3d(+2px, 0, 0); } | ||||||
|  |             30%, 70% { transform: translate3d(-4px, 0, 0); } | ||||||
|  |             40%, 60% { transform: translate3d(+4px, 0, 0); } | ||||||
|  |             50% { transform: translate3d(-4px, 0, 0); } | ||||||
|         } |         } | ||||||
|             20%, 50% { |  | ||||||
|                 transform: translate3d(+2px, 0, 0); |  | ||||||
|             } |  | ||||||
|             30%, 70% { |  | ||||||
|                 transform: translate3d(-4px, 0, 0); |  | ||||||
|             } |  | ||||||
|             40%, 60% { |  | ||||||
|                 transform: translate3d(+4px, 0, 0); |  | ||||||
|             } |  | ||||||
|             50% { |  | ||||||
|                 transform: translate3d(-4px, 0, 0); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         .lead { |  | ||||||
|             font-size: 22px; |  | ||||||
|             font-family: Helvetica, sans-serif; |  | ||||||
|             white-space: pre-wrap; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         .arrow { |  | ||||||
|             padding: 0; |  | ||||||
|             font-size: 20px; |  | ||||||
|             line-height: 21px; |  | ||||||
|             display: inline-block; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         .arrow:hover { |  | ||||||
|             cursor: pointer; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     </style> |     </style> | ||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
|  | @ -75,37 +45,22 @@ | ||||||
|         <a class="btn btn-secondary" href="/reset" role="button" onclick="stopRead()">重设密码</a> |         <a class="btn btn-secondary" href="/reset" role="button" onclick="stopRead()">重设密码</a> | ||||||
| 
 | 
 | ||||||
|     </p> |     </p> | ||||||
|     {% for message in get_flashed_messages() %} | {#    {% for message in flashed_messages %}#} {# 根据user_service.userpage,取消了参数flashed_messages,因此注释了这段代码 #} | ||||||
|         <div class="alert alert-warning alert-dismissible fade show" role="alert"> | {#        <div class="alert alert-warning" role="alert">Congratulations! {{ message }}</div>#} | ||||||
|             {{ message }} | {#    {% endfor %}#} | ||||||
|             <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> |  | ||||||
|         </div> |  | ||||||
|     {% endfor %} |  | ||||||
| 
 | 
 | ||||||
|     <button class="arrow" id="load_next_article" onclick="load_next_article();Reader.stopRead()" |         <button class="btn btn-success" id="load_next_article" onclick="load_next_article()"> 下一篇 Next Article </button> | ||||||
|             title="下一篇 Next Article">⇨ |         <button class="btn btn-success" id="load_pre_article" onclick="load_pre_article()" > 上一篇 Previous Article </button> | ||||||
|     </button> |  | ||||||
|     <button class="arrow" id="load_pre_article" onclick="load_pre_article();Reader.stopRead()" style="display: none" |  | ||||||
|             title="上一篇 Previous Article">⇦ |  | ||||||
|     </button> |  | ||||||
| 
 | 
 | ||||||
|     <p><b>阅读文章并回答问题</b></p> |     <p><b>阅读文章并回答问题</b></p> | ||||||
|     <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 |             <div class="alert alert-success" role="alert">According to your word list, your level is <span class="badge bg-success" id="user-level">{{ today_article["user_level"] }}</span>  and we have chosen an article with a difficulty level of <span class="badge bg-success" id="text_level">{{ today_article["text_level"] }}</span> for you.</div> | ||||||
|                     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. |  | ||||||
|             </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/> | ||||||
|             <div class="p-3 mb-2 bg-light text-dark" style="margin: 0 0.5%;"><br/> |             <div class="p-3 mb-2 bg-light text-dark"><br/> | ||||||
|                 <p class="display-6" id="article_title">{{ today_article["article_title"] }}</p><br/> |             <p class="display-5" id="article_title">{{ today_article["article_title"] }}</p><br/> | ||||||
|                 <p class="lead"><font id="article">{{ today_article["article_body"] }}</font></p><br/> |             <p class="lead"><font id="article" size=2>{{ today_article["article_body"] }}</font></p><br/> | ||||||
|                 <div> |  | ||||||
|             <p><small class="text-muted" id="source">{{ today_article['source'] }}</small></p><br/> |             <p><small class="text-muted" id="source">{{ today_article['source'] }}</small></p><br/> | ||||||
|                 </div> |  | ||||||
| 
 |  | ||||||
|             <p><b id="question">{{ today_article['question'] }}</b></p><br/> |             <p><b id="question">{{ today_article['question'] }}</b></p><br/> | ||||||
|                 <script type="text/javascript"> |                 <script type="text/javascript"> | ||||||
|                     function toggle_visibility(id) { {# https://css-tricks.com/snippets/javascript/showhide-element/#} |                     function toggle_visibility(id) { {# https://css-tricks.com/snippets/javascript/showhide-element/#} | ||||||
|  | @ -117,22 +72,20 @@ | ||||||
|                     } |                     } | ||||||
|                 </script> |                 </script> | ||||||
|                 <button onclick="toggle_visibility('answer');">ANSWER</button> |                 <button onclick="toggle_visibility('answer');">ANSWER</button> | ||||||
|                 <div id="answer" style="display:none;">{{ today_article['answer'] }}</div> |                 <div id="answer" style="display:none;">{{ today_article['answer'] }}</div><br/> | ||||||
|                 <br/> |  | ||||||
|             </div> |             </div> | ||||||
|         </div> |         </div> | ||||||
|         <div class="alert alert-success" role="alert" id="not_found" style="display:none;"> |         <div class="alert alert-success" role="alert" id="not_found" style="display:none;"> | ||||||
|             <p class="text-muted"><span class="badge bg-success">Notes:</span><br>No article is currently available for |             <p class="text-muted"><span class="badge bg-success">Notes:</span><br>No article is currently available for you. You can try again a few times or mark new words in the passage to improve your level.</p> | ||||||
|                 you. You can try again a few times or mark new words in the passage to improve your level.</p> |  | ||||||
|         </div> |         </div> | ||||||
|         <div class="alert alert-success" role="alert" id="read_all" style="display:none;"> |         <div class="alert alert-success" role="alert" id="read_all" style="display:none;"> | ||||||
|             <p class="text-muted"><span class="badge bg-success">Notes:</span><br>You've read all the articles.</p> |             <p class="text-muted"><span class="badge bg-success">Notes:</span><br>You've read all the articles.</p> | ||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|     <input type="checkbox" id="highlightCheckbox" onclick="toggleHighlighting()"/>生词高亮 |     <input type="checkbox" onclick="toggleHighlighting()" checked/>生词高亮 | ||||||
|     <input type="checkbox" id="readCheckbox" onclick="onReadClick()"/>大声朗读 |     <input type="checkbox" onclick="onReadClick()" checked/>大声朗读 | ||||||
|     <input type="checkbox" id="chooseCheckbox" onclick="onChooseClick()"/>划词入库 |     <input type="checkbox" onclick="onChooseClick()" checked/>划词入库 | ||||||
|     <div class="range"> |     <div class="range"> | ||||||
|         <div class="field"> |         <div class="field"> | ||||||
|             <div class="sliderValue"> |             <div class="sliderValue"> | ||||||
|  | @ -144,8 +97,8 @@ | ||||||
|     <p><b>收集生词吧</b> (可以在正文中划词,也可以复制黏贴)</p> |     <p><b>收集生词吧</b> (可以在正文中划词,也可以复制黏贴)</p> | ||||||
|     <form method="post" action="/{{ username }}/userpage"> |     <form method="post" action="/{{ username }}/userpage"> | ||||||
|         <textarea name="content" id="selected-words" rows="10" cols="120"></textarea><br/> |         <textarea name="content" id="selected-words" rows="10" cols="120"></textarea><br/> | ||||||
|         <button class="btn btn-primary btn-lg" type="submit" onclick="Reader.stopRead()">把生词加入我的生词库</button> |         <input type="submit" value="把生词加入我的生词库"/> | ||||||
|         <button class="btn btn-primary btn-lg" type="reset" onclick="clearSelectedWords()">清除</button> |         <input type="reset" value="清除"/> | ||||||
|     </form> |     </form> | ||||||
|     {% if session.get['thisWord'] %} |     {% if session.get['thisWord'] %} | ||||||
|         <script type="text/javascript"> |         <script type="text/javascript"> | ||||||
|  | @ -174,14 +127,12 @@ | ||||||
|                 {% if session.get('thisWord') == x[0] and session.get('time') == 1 %} |                 {% if session.get('thisWord') == x[0] and session.get('time') == 1 %} | ||||||
|                 {% endif %} |                 {% endif %} | ||||||
|                 <p id='p_{{ word }}' class="new-word" > |                 <p id='p_{{ word }}' class="new-word" > | ||||||
|                     <a id="word_{{ word }}" class="btn btn-light" |                     <a id="word_{{ word }}"  class="btn btn-light" href='http://youdao.com/w/eng/{{ word }}/#keyfrom=dict2.index' | ||||||
|                        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> |  | ||||||
|                 </p> |                 </p> | ||||||
|             {% endfor %} |             {% endfor %} | ||||||
|         </div> |         </div> | ||||||
|  | @ -195,57 +146,7 @@ | ||||||
|     {% endfor %} |     {% endfor %} | ||||||
| {% endif %} | {% endif %} | ||||||
| <script type="text/javascript"> | <script type="text/javascript"> | ||||||
|     window.onload = function () { // 页面加载时执行 |  | ||||||
|         const settings = { |  | ||||||
|             // initialize settings from localStorage |  | ||||||
|             highlightChecked: localStorage.getItem('highlightChecked') !== 'false', // localStorage stores strings, default to true. same below |  | ||||||
|             readChecked: localStorage.getItem('readChecked') !== 'false', |  | ||||||
|             chooseChecked: localStorage.getItem('chooseChecked') !== 'false', |  | ||||||
|             rangeValue: localStorage.getItem('rangeValue') || '1', |  | ||||||
|             selectedWords: localStorage.getItem('selectedWords') || '' |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         const elements = { |  | ||||||
|             highlightCheckbox: document.querySelector('#highlightCheckbox'), |  | ||||||
|             readCheckbox: document.querySelector('#readCheckbox'), |  | ||||||
|             chooseCheckbox: document.querySelector('#chooseCheckbox'), |  | ||||||
|             rangeComponent: document.querySelector('#rangeComponent'), |  | ||||||
|             rangeValueDisplay: document.querySelector('#rangeValue'), |  | ||||||
|             selectedWordsInput: document.querySelector('#selected-words') |  | ||||||
|         }; |  | ||||||
|         // 应用设置到页面元素 |  | ||||||
|         elements.highlightCheckbox.checked = settings.highlightChecked; |  | ||||||
|         elements.readCheckbox.checked = settings.readChecked; |  | ||||||
|         elements.chooseCheckbox.checked = settings.chooseChecked; |  | ||||||
|         elements.rangeComponent.value = settings.rangeValue; |  | ||||||
|         elements.rangeValueDisplay.textContent = `${settings.rangeValue}x`; |  | ||||||
|         elements.selectedWordsInput.value = settings.selectedWords; |  | ||||||
| 
 |  | ||||||
|         // 刷新页面或进入页面时判断,若不是首篇文章,则上一篇按钮可见 |  | ||||||
|         if (sessionStorage.getItem('pre_page_button') !== 'display' && sessionStorage.getItem('pre_page_button')) { |  | ||||||
|             $('#load_pre_article').show(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // 事件监听器 |  | ||||||
|         elements.selectedWordsInput.addEventListener('input', () => { |  | ||||||
|             localStorage.setItem('selectedWords', elements.selectedWordsInput.value); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         elements.rangeComponent.addEventListener('input', () => { |  | ||||||
|             const rangeValue = elements.rangeComponent.value; |  | ||||||
|             elements.rangeValueDisplay.textContent = `${rangeValue}x`; |  | ||||||
|             localStorage.setItem('rangeValue', rangeValue); |  | ||||||
|         }); |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     function clearSelectedWords() { |  | ||||||
|         localStorage.removeItem('selectedWords'); |  | ||||||
|         document.querySelector('#selected-words').value = ''; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     function load_next_article(){ |     function load_next_article(){ | ||||||
|         $("#load_next_article").prop("disabled", true) |  | ||||||
|         $.ajax({ |         $.ajax({ | ||||||
|             url: '/get_next_article/{{username}}', |             url: '/get_next_article/{{username}}', | ||||||
|             dataType: 'json', |             dataType: 'json', | ||||||
|  | @ -256,12 +157,9 @@ | ||||||
|                     check_pre(data['visited_articles']); |                     check_pre(data['visited_articles']); | ||||||
|                     check_next(data['result_of_generate_article']); |                     check_next(data['result_of_generate_article']); | ||||||
|                 } |                 } | ||||||
|             }, complete: function (xhr, status) { |  | ||||||
|                 $("#load_next_article").prop("disabled", false) |  | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     function load_pre_article(){ |     function load_pre_article(){ | ||||||
|         $.ajax({ |         $.ajax({ | ||||||
|             url: '/get_pre_article/{{username}}', |             url: '/get_pre_article/{{username}}', | ||||||
|  | @ -275,9 +173,8 @@ | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     function update(today_article){ |     function update(today_article){ | ||||||
|         $('#user_level').html(today_article['user_level']); |         $('#user-level').html(today_article['user_level']); | ||||||
|         $('#text_level').html(today_article["text_level"]); |         $('#text_level').html(today_article["text_level"]); | ||||||
|         $('#date').html('Article added on: '+today_article["date"]); |         $('#date').html('Article added on: '+today_article["date"]); | ||||||
|         $('#article_title').html(today_article["article_title"]); |         $('#article_title').html(today_article["article_title"]); | ||||||
|  | @ -285,31 +182,18 @@ | ||||||
|         $('#source').html(today_article['source']); |         $('#source').html(today_article['source']); | ||||||
|         $('#question').html(today_article["question"]); |         $('#question').html(today_article["question"]); | ||||||
|         $('#answer').html(today_article["answer"]); |         $('#answer').html(today_article["answer"]); | ||||||
|         document.querySelector('#text_level').classList.add('mark'); // highlight text difficult level for 2 seconds |  | ||||||
|         setTimeout(() => { |  | ||||||
|             document.querySelector('#text_level').classList.remove('mark'); |  | ||||||
|         }, 2000); |  | ||||||
|         document.querySelector('#user_level').classList.add('mark'); // do the same thing for user difficulty level |  | ||||||
|         setTimeout(() => { |  | ||||||
|             document.querySelector('#user_level').classList.remove('mark'); |  | ||||||
|         }, 2000); |  | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| <!-- 检查是否存在上一篇或下一篇,不存在则对应按钮隐藏--> | <!-- 检查是否存在上一篇或下一篇,不存在则对应按钮隐藏--> | ||||||
|     function check_pre(visited_articles){ |     function check_pre(visited_articles){ | ||||||
|         if((visited_articles=='')||(visited_articles['index']<=0)){ |         if((visited_articles=='')||(visited_articles['index']<=0)){ | ||||||
|             $('#load_pre_article').hide(); |             $('#load_pre_article').hide(); | ||||||
|             sessionStorage.setItem('pre_page_button', 'display') |  | ||||||
|         }else{ |         }else{ | ||||||
|             $('#load_pre_article').show(); |             $('#load_pre_article').show(); | ||||||
|             sessionStorage.setItem('pre_page_button', 'show') |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     function check_next(result_of_generate_article){ |     function check_next(result_of_generate_article){ | ||||||
|         if(result_of_generate_article == "found"){ |         if(result_of_generate_article == "found"){ | ||||||
|             $('#found').show(); |             $('#found').show();$('#not_found').hide(); | ||||||
|             $('#not_found').hide(); |  | ||||||
|             $('#read_all').hide(); |             $('#read_all').hide(); | ||||||
|         }else if(result_of_generate_article == "not found"){ |         }else if(result_of_generate_article == "not found"){ | ||||||
|             $('#found').hide(); |             $('#found').hide(); | ||||||
|  |  | ||||||
|  | @ -1,85 +0,0 @@ | ||||||
| ''' Contributed by Lin Junhong et al. 2023-06.''' |  | ||||||
| 
 |  | ||||||
| from selenium import webdriver |  | ||||||
| from selenium.webdriver.common.desired_capabilities import DesiredCapabilities |  | ||||||
| 
 |  | ||||||
| from selenium.webdriver.support.ui import WebDriverWait |  | ||||||
| from selenium.webdriver.support import expected_conditions as EC |  | ||||||
| from selenium.common.exceptions import UnexpectedAlertPresentException, NoAlertPresentException |  | ||||||
| import random, time |  | ||||||
| import string |  | ||||||
| 
 |  | ||||||
| # 初始化webdriver |  | ||||||
| # driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.CHROME) |  | ||||||
| # driver.implicitly_wait(10) |  | ||||||
| driver = webdriver.Chrome("C:\\Users\\12993\AppData\Local\Programs\Python\Python38\\chromedriver.exe") |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def test_next_article(): |  | ||||||
|     try: |  | ||||||
|         driver.get("http://118.25.96.118:90") |  | ||||||
|         assert 'English Pal -' in driver.page_source |  | ||||||
|         # login |  | ||||||
|         elem = driver.find_element_by_link_text('登录') |  | ||||||
|         elem.click() |  | ||||||
| 
 |  | ||||||
|         uname = 'abcdefg' |  | ||||||
|         password = 'abcdefg' |  | ||||||
|         elem = driver.find_element_by_id('username') |  | ||||||
|         elem.send_keys(uname) |  | ||||||
| 
 |  | ||||||
|         elem = driver.find_element_by_id('password') |  | ||||||
|         elem.send_keys(password) |  | ||||||
|         elem = driver.find_element_by_xpath('/html/body/div/button')  # 找到登录按钮 |  | ||||||
|         elem.click() |  | ||||||
| 
 |  | ||||||
|         time.sleep(0.5) |  | ||||||
|         assert 'EnglishPal Study Room for ' + uname in driver.title |  | ||||||
|         for i in range(50): |  | ||||||
|             time.sleep(0.1) |  | ||||||
|             # 找到固定按钮 |  | ||||||
|             elem = driver.find_element_by_xpath('//*[@id="load_next_article"]') |  | ||||||
|             elem.click() |  | ||||||
|     except Exception as e: |  | ||||||
|         print(e) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def test_local_next_article(): |  | ||||||
|     try: |  | ||||||
|         driver.get("http://127.0.0.1:5000") |  | ||||||
|         assert 'English Pal -' in driver.page_source |  | ||||||
|         # login |  | ||||||
|         elem = driver.find_element_by_link_text('注册') |  | ||||||
|         elem.click() |  | ||||||
| 
 |  | ||||||
|         uname = 'abcdefg' |  | ||||||
|         password = 'abcdefg' |  | ||||||
|         elem = driver.find_element_by_id('username') |  | ||||||
|         elem.send_keys(uname) |  | ||||||
| 
 |  | ||||||
|         elem = driver.find_element_by_id('password') |  | ||||||
|         elem.send_keys(password) |  | ||||||
| 
 |  | ||||||
|         elem = driver.find_element_by_id('password2') |  | ||||||
|         elem.send_keys(password) |  | ||||||
| 
 |  | ||||||
|         time.sleep(0.5) |  | ||||||
| 
 |  | ||||||
|         elem = driver.find_element_by_class_name('btn')  # 找到提交按钮 |  | ||||||
|         elem.click() |  | ||||||
|         time.sleep(0.5) |  | ||||||
|         try: |  | ||||||
|             WebDriverWait(driver, 1).until(EC.alert_is_present()) |  | ||||||
|             driver.switch_to.alert.accept() |  | ||||||
|         except (UnexpectedAlertPresentException, NoAlertPresentException): |  | ||||||
|             pass |  | ||||||
| 
 |  | ||||||
|         time.sleep(0.5) |  | ||||||
|         assert 'EnglishPal Study Room for ' + uname in driver.title |  | ||||||
|         for i in range(50): |  | ||||||
|             time.sleep(0.1) |  | ||||||
|             # 找到固定按钮 |  | ||||||
|             elem = driver.find_element_by_xpath('//*[@id="load_next_article"]') |  | ||||||
|             elem.click() |  | ||||||
|     except Exception as e: |  | ||||||
|         print(e) |  | ||||||
|  | @ -39,7 +39,7 @@ def file2str(fname):#文件转字符 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def remove_punctuation(s): # 这里是s是形参 (parameter)。函数被调用时才给s赋值。 | def remove_punctuation(s): # 这里是s是形参 (parameter)。函数被调用时才给s赋值。 | ||||||
|     special_characters = '\_©~<=>+/[]*&$%^@.,?!:;#()"“”—‘’{}|' # 把里面的字符都去掉 |     special_characters = '\_©~<=>+-/[]*&$%^@.,?!:;#()"“”—‘’{}|' # 把里面的字符都去掉 | ||||||
|     for c in special_characters: |     for c in special_characters: | ||||||
|         s = s.replace(c, ' ') # 防止出现把 apple,apple 移掉逗号后变成 appleapple 情况 |         s = s.replace(c, ' ') # 防止出现把 apple,apple 移掉逗号后变成 appleapple 情况 | ||||||
|     s = s.replace('--', ' ') |     s = s.replace('--', ' ') | ||||||
|  |  | ||||||
|  | @ -1,8 +1,5 @@ | ||||||
| Flask==2.0.3 | Flask==1.1.2 | ||||||
| selenium==3.141.0 | selenium==3.141.0 | ||||||
| PyYAML~=6.0 | PyYAML~=6.0 | ||||||
| pony==0.7.16 | pony==0.7.16 | ||||||
| snowballstemmer==2.2.0 | snowballstemmer==2.2.0 | ||||||
| Werkzeug==2.2.2 |  | ||||||
| 
 |  | ||||||
| pytest~=8.1.1 |  | ||||||
		Loading…
	
		Reference in New Issue