forked from mrlan/EnglishPal
				
			feat: add admin_service blueprint
							parent
							
								
									13d8977636
								
							
						
					
					
						commit
						ade10e5843
					
				|  | @ -0,0 +1,118 @@ | |||
| from flask import * | ||||
| from model import * | ||||
| from pony.orm import * | ||||
| from Yaml import yml | ||||
| from Login import md5 | ||||
| from datetime import datetime | ||||
| 
 | ||||
| # ? from difficulty import text_difficulty_level | ||||
| 
 | ||||
| ADMIN_NAME = "114514"  # unique admin name | ||||
| _cur_page = 1  # current article page | ||||
| _page_size = 5  # article sizes per page | ||||
| adminService = Blueprint("admin_service", __name__) | ||||
| 
 | ||||
| 
 | ||||
| @adminService.route("/admin", methods=["GET", "POST"]) | ||||
| def admin(): | ||||
|     global _cur_page, _page_size | ||||
|     # 未登录,跳转到未登录界面 | ||||
|     if not session.get("logged_in"): | ||||
|         return render_template("not_login.html") | ||||
| 
 | ||||
|     # 获取session里的用户名 | ||||
|     username = session.get("username") | ||||
|     if username != ADMIN_NAME: | ||||
|         return "You are not admin!" | ||||
| 
 | ||||
|     article_len = get_articles_len() | ||||
|     try: | ||||
|         _page_size = min(int(request.args.get("size", 5)), article_len) | ||||
|         if _page_size <= 0: | ||||
|             raise ZeroDivisionError | ||||
|         _cur_page = min(int(request.args.get("page", 1)), article_len // _page_size) | ||||
|     except ValueError: | ||||
|         return "page parmas must be int!" | ||||
|     except ZeroDivisionError: | ||||
|         return "page size must bigger than zero" | ||||
| 
 | ||||
|     context = { | ||||
|         "text_len": article_len, | ||||
|         "page_size": _page_size, | ||||
|         "cur_page": _cur_page, | ||||
|         "text_list": get_page_articles(_cur_page, _page_size), | ||||
|         "user_list": get_users(), | ||||
|         "username": username, | ||||
|         "yml": yml, | ||||
|     } | ||||
| 
 | ||||
|     def _update_context(): | ||||
|         article_len = get_articles_len() | ||||
|         context["text_len"] = article_len | ||||
|         context["text_list"] = get_page_articles(_cur_page, _page_size) | ||||
| 
 | ||||
|     if request.method == "GET": | ||||
|         if delete_id := int(request.args.get("delete_id", 0)):  # delete article | ||||
|             delete_article(delete_id) | ||||
|             _update_context() | ||||
|     else: | ||||
|         data = request.form | ||||
|         content = data.get("content", "") | ||||
|         source = data.get("source", "") | ||||
|         question = data.get("question", "") | ||||
|         username = data.get("username", "") | ||||
|         if content: | ||||
|             add_article(content, source, question) | ||||
|             _update_context() | ||||
|         if username: | ||||
|             update_user_password(username) | ||||
| 
 | ||||
|     return render_template("admin_index.html", **context) | ||||
| 
 | ||||
| 
 | ||||
| def add_article(content, source="manual_input", question="No question"): | ||||
|     with db_session: | ||||
|         # add one atricle to sqlite | ||||
|         Article( | ||||
|             text=content, | ||||
|             source=source, | ||||
|             date=datetime.now().strftime("%-d %b %Y"),  # format style of `5 Oct 2022` | ||||
|             level="1", | ||||
|             question=question, | ||||
|         ) | ||||
|         # ? There is a question that: | ||||
|         # ? How can i get one article level? | ||||
|         # ? I try to use the function `text_difficulty_level(content,{"test":1})` | ||||
|         # ? However, i lose one dict parma from pickle | ||||
|         # ? So I temporarily fixed the level to 1 | ||||
| 
 | ||||
| 
 | ||||
| def delete_article(article_id): | ||||
|     article_id &= 0xFFFFFFFF  # max 32 bits | ||||
|     with db_session: | ||||
|         if article := Article.select(article_id=article_id): | ||||
|             article.first().delete() | ||||
| 
 | ||||
| 
 | ||||
| def get_articles_len(): | ||||
|     with db_session: | ||||
|         return len(Article.select()[:]) | ||||
| 
 | ||||
| 
 | ||||
| def get_page_articles(num, size): | ||||
|     with db_session: | ||||
|         return [ | ||||
|             x | ||||
|             for x in Article.select().order_by(desc(Article.article_id)).page(num, size) | ||||
|         ] | ||||
| 
 | ||||
| 
 | ||||
| def get_users(): | ||||
|     with db_session: | ||||
|         return User.select()[:] | ||||
| 
 | ||||
| 
 | ||||
| def update_user_password(username, password="123456"): | ||||
|     with db_session: | ||||
|         if user := User.select(name=username).first(): | ||||
|             user.password = md5(username + password) | ||||
							
								
								
									
										77
									
								
								app/main.py
								
								
								
								
							
							
						
						
									
										77
									
								
								app/main.py
								
								
								
								
							|  | @ -5,24 +5,24 @@ | |||
| # Copyright 2019 (C) Hui Lan <hui.lan@cantab.net> | ||||
| # Written permission must be obtained from the author for commercial uses. | ||||
| ########################################################################### | ||||
| from datetime import datetime | ||||
| from flask import escape | ||||
| from Login import * | ||||
| from Article import * | ||||
| import Yaml | ||||
| from user_service import userService | ||||
| from account_service import accountService | ||||
| from admin_service import adminService | ||||
| app = Flask(__name__) | ||||
| app.secret_key = 'lunch.time!' | ||||
| 
 | ||||
| # 将蓝图注册到Lab app | ||||
| app.register_blueprint(userService) | ||||
| app.register_blueprint(accountService) | ||||
| app.register_blueprint(adminService) | ||||
| 
 | ||||
| path_prefix = '/var/www/wordfreq/wordfreq/' | ||||
| path_prefix = './'  # comment this line in deployment | ||||
| 
 | ||||
| 
 | ||||
| def get_random_image(path): | ||||
|     ''' | ||||
|     返回随机图 | ||||
|  | @ -102,79 +102,6 @@ def mainpage(): | |||
|                                d_len=d_len, lst=lst, yml=Yaml.yml) | ||||
| 
 | ||||
| 
 | ||||
| def insert_article(content, source='manual_input', level=5, question=''): | ||||
|     sql = f"INSERT into article (text,source, date, level, question) VALUES " \ | ||||
|           f"('{content}','{source}','{datetime.now().strftime('%Y-%m-%d')}', '{level}', '{question}');" | ||||
|     print(sql) | ||||
|     rq = RecordQuery('./static/wordfreqapp.db') | ||||
|     rq.instructions(sql) | ||||
|     rq.do() | ||||
| 
 | ||||
| 
 | ||||
| def get_articles(): | ||||
|     sql = f"SELECT * from article order by -article_id;" | ||||
|     rq = RecordQuery('./static/wordfreqapp.db') | ||||
|     rq.instructions(sql) | ||||
|     rq.do() | ||||
|     result = rq.get_results() | ||||
|     return result | ||||
| 
 | ||||
| 
 | ||||
| def get_users(): | ||||
|     sql = f"SELECT * from user;" | ||||
|     rq = RecordQuery('./static/wordfreqapp.db') | ||||
|     rq.instructions(sql) | ||||
|     rq.do() | ||||
|     result = rq.get_results() | ||||
|     return result | ||||
| 
 | ||||
| 
 | ||||
| def update_user_password(username, password='123456'): | ||||
|     password = md5(username + password) | ||||
|     sql = f"UPDATE user SET password='{password}' where name='{username}';" | ||||
|     rq = RecordQuery('./static/wordfreqapp.db') | ||||
|     rq.instructions(sql) | ||||
|     rq.do() | ||||
| 
 | ||||
| 
 | ||||
| @app.route("/admin", methods=['GET', 'POST']) | ||||
| def admin(): | ||||
|     ''' | ||||
|     ''' | ||||
|     # 未登录,跳转到未登录界面 | ||||
|     if not session.get('logged_in'): | ||||
|         return render_template('not_login.html') | ||||
| 
 | ||||
|     # 获取session里的用户名 | ||||
|     username = session.get('username') | ||||
| 
 | ||||
|     context = { | ||||
|         # 'user': request.user, | ||||
|         'text_list': get_articles(), | ||||
|         'user_list': get_users(), | ||||
|         'username': username | ||||
|     } | ||||
|     if request.method == 'GET': | ||||
|         return render_template('admin_index.html', **context) | ||||
|     else: | ||||
|         data = request.form | ||||
|         content = data.get('content') | ||||
|         source = data.get('source', '') | ||||
|         question = data.get('question', '') | ||||
|         username = data.get('username') | ||||
|         if content: | ||||
|             insert_article( | ||||
|                 content=content, | ||||
|                 source=source, | ||||
|                 question=question | ||||
|             ) | ||||
|             context['text_list'] = get_articles() | ||||
|         if username: | ||||
|             update_user_password(username) | ||||
|         return render_template('admin_index.html', **context) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     ''' | ||||
|     运行程序 | ||||
|  |  | |||
|  | @ -1,112 +1,120 @@ | |||
| <html> | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| 
 | ||||
| <head> | ||||
|     <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" | ||||
|           integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous"> | ||||
|     <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 %} | ||||
| 
 | ||||
| </head> | ||||
| 
 | ||||
| <body class="container" style="width: 800px; margin: auto; margin-top:24px;"> | ||||
| <nav class="navbar navbar-expand-lg bg-light"> | ||||
|     <div class="container-fluid"> | ||||
|         <a class="navbar-brand" href="#">你好 {{ username }}</a> | ||||
|         <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" | ||||
|     <nav class="navbar navbar-expand-lg bg-light"> | ||||
|         <div class="container-fluid"> | ||||
|             <a class="navbar-brand" href="#">管理员 {{ username }} 您好!</a> | ||||
|             <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" | ||||
|                 aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> | ||||
|             <span class="navbar-toggler-icon"></span> | ||||
|         </button> | ||||
|         <div class="collapse navbar-collapse" id="navbarNav"> | ||||
|             <ul class="navbar-nav"> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/logout">退出登录</a> | ||||
|                 </li> | ||||
|             </ul> | ||||
|                 <span class="navbar-toggler-icon"></span> | ||||
|             </button> | ||||
|             <div class="collapse navbar-collapse" id="navbarNav"> | ||||
|                 <ul class="navbar-nav"> | ||||
|                     <li class="nav-item"> | ||||
|                         <a class="nav-link" href="/{{ username }}">返回主页</a> | ||||
|                     </li> | ||||
|                     <li class="nav-item"> | ||||
|                         <a class="nav-link" href="/logout">退出登录</a> | ||||
|                     </li> | ||||
|                 </ul> | ||||
|             </div> | ||||
|         </div> | ||||
|     </nav> | ||||
| 
 | ||||
|     <div class="card" style="margin-top:24px;"> | ||||
|         <h5 style="margin-top: 10px;padding-left: 10px;">重置选中用户密码</h5> | ||||
|         <form action="" method="post" class="container mb-3"> | ||||
|             <div class="mb-3"> | ||||
|                 <label for="exampleInputEmail1" class="form-label">用户</label> | ||||
|                 <select id="username" name="username" class="form-select" aria-label="Default select example"> | ||||
|                     <option selected>选择用户</option> | ||||
|                     {% for user in user_list %} | ||||
|                     <option value="{{ user.name }}">{{ user.name }}</option> | ||||
|                     {% endfor %} | ||||
|                 </select> | ||||
|             </div> | ||||
|             <input type="submit" value="重置密码为:123456" class="btn btn-outline-primary"> | ||||
|         </form> | ||||
|     </div> | ||||
| </nav> | ||||
| 
 | ||||
| <div class="card" style="margin-top:24px;"> | ||||
|     <h5>重置选中用户密码</h5> | ||||
|     <form action="" method="post" class="container mb-3"> | ||||
|         <div class="mb-3"> | ||||
|             <label for="exampleInputEmail1" class="form-label">用户</label> | ||||
|             <select | ||||
|                     id="username" | ||||
|                     name="username" | ||||
|                     class="form-select" | ||||
|                     aria-label="Default select example"> | ||||
|               <option selected>选择用户</option> | ||||
|                 {% for user in user_list %} | ||||
|                     <option value="{{ user['name'] }}">{{ user['name'] }}</option> | ||||
|                 {% endfor %} | ||||
| 
 | ||||
|             </select> | ||||
|         </div> | ||||
| 
 | ||||
|         <input type="submit" value="重置密码为:123456" class="btn btn-primary"> | ||||
|     </form> | ||||
| </div> | ||||
| 
 | ||||
| <div class="card" style="margin-top:24px;"> | ||||
|     {% if tips %} | ||||
|     <div class="card" style="margin-top:24px;"> | ||||
|         {% if tips %} | ||||
|         <div class="alert alert-success" role="alert"> | ||||
|             {{ tips }} | ||||
|         </div> | ||||
|     {% endif %} | ||||
|         {% endif %} | ||||
|         <div class="card-content"> | ||||
|             <h5 style="margin-top: 10px;padding-left: 10px;">录入文章</h5> | ||||
|             <form action="" method="post" class="container mb-3"> | ||||
|                 <div class="mb-3"> | ||||
|                     <label for="exampleInputEmail1" class="form-label">文章内容</label> | ||||
|                     <textarea id="content" name="content" class="form-control" placeholder="请输入文章内容"></textarea> | ||||
|                     <label for="exampleInputEmail1" class="form-label">文章来源</label> | ||||
| 
 | ||||
|     <div class="card-content"> | ||||
|         <h5>录入文章</h5> | ||||
|         <form action="" method="post" class="container mb-3"> | ||||
|             <div class="mb-3"> | ||||
|                 <label for="exampleInputEmail1" class="form-label">文章内容</label> | ||||
|                 <textarea | ||||
|                         id="content" | ||||
|                         name="content" | ||||
|                         class="form-control" | ||||
|                         placeholder="请输入文章内容" | ||||
|                 ></textarea> | ||||
|                 <label for="exampleInputEmail1" class="form-label">文章来源</label> | ||||
|                     <textarea id="source" name="source" class="form-control" placeholder="请输入来源"></textarea> | ||||
|                     <label for="exampleInputEmail1" class="form-label">文章问题</label> | ||||
| 
 | ||||
|                 <textarea | ||||
|                         id="source" | ||||
|                         name="source" | ||||
|                         class="form-control" | ||||
|                         placeholder="请输入来源" | ||||
|                 ></textarea> | ||||
|                 <label for="exampleInputEmail1" class="form-label">文章问题</label> | ||||
| 
 | ||||
|                 <textarea | ||||
|                         id="question" | ||||
|                         name="question" | ||||
|                         class="form-control" | ||||
|                         placeholder="请输入问题" | ||||
|                 ></textarea> | ||||
|             </div> | ||||
| 
 | ||||
|             <input type="submit" value="保存" class="btn btn-primary"> | ||||
|         </form> | ||||
|                     <textarea id="question" name="question" class="form-control" placeholder="请输入问题"></textarea> | ||||
|                 </div> | ||||
|                 <input type="submit" value="保存" class="btn btn-outline-primary"> | ||||
|             </form> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| 
 | ||||
| <div class="card" style="margin-top:24px;"> | ||||
|         <h5>文章列表</h5> | ||||
|     <div class="list-group"> | ||||
|         {% for text in text_list %} | ||||
|     <div class="card" style="margin-top:24px;"> | ||||
|         <h5 style="margin-top: 10px;padding-left: 10px;">文章列表</h5> | ||||
|         <div class="list-group"> | ||||
|             {% for text in text_list %} | ||||
|             <div class="list-group-item list-group-item-action" aria-current="true"> | ||||
|                 <div class="d-flex w-100 justify-content-between"> | ||||
|                     <h5 class="mb-1">{{ text['source'] }}</h5> | ||||
|                     <small>Date:{{ text['date'] }} Level:{{ text['level'] }}</small> | ||||
|                     <h5 class="mb-1">{{ text.source }}</h5> | ||||
|                     <small>Date:{{ text.date }} Level:{{ text.level }}</small> | ||||
|                 </div> | ||||
|                 <p class="mb-1">{{ text['text'] }}</p> | ||||
|                 <div style="text-align: right; padding-bottom: 5px;"><a href="/admin?delete_id={{text.article_id}}" | ||||
|                         class="btn btn-outline-danger btn-sm"> | ||||
|                         删除文章 | ||||
|                     </a></div> | ||||
|                 <p class="mb-1">{{ text.text }}</p> | ||||
|             </div> | ||||
|         {% endfor %} | ||||
| 
 | ||||
|             {% endfor %} | ||||
|         </div> | ||||
|     </div> | ||||
|     <div style="margin:20px 0;"> | ||||
|         <ul class="pagination pagination-sm justify-content-center"> | ||||
|             <li class="page-item"><a class="page-link" href="/admin?page={{cur_page-1}}&size={{page_size}}">Previous</a> | ||||
|             </li> | ||||
|             {% for i in range(1, text_len//page_size+1) %} | ||||
|             {% if cur_page == i %} | ||||
|             <li class="page-item active"><a class="page-link" href="/admin?page={{i}}&size={{page_size}}">{{ i }}</a> | ||||
|             </li> | ||||
|             {% else %} | ||||
|             <li class="page-item"><a class="page-link" href="/admin?page={{i}}&size={{page_size}}">{{ i }}</a></li> | ||||
|             {% endif %} | ||||
|             {% endfor %} | ||||
|             <li class="page-item"><a class="page-link" href="/admin?page={{cur_page+1}}&size={{page_size}}">Next</a> | ||||
|             </li> | ||||
|         </ul> | ||||
|     </div> | ||||
| </div> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| </body> | ||||
| 
 | ||||
| </html> | ||||
| 
 | ||||
| 
 | ||||
| </html> | ||||
		Loading…
	
		Reference in New Issue