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> | # Copyright 2019 (C) Hui Lan <hui.lan@cantab.net> | ||||||
| # Written permission must be obtained from the author for commercial uses. | # Written permission must be obtained from the author for commercial uses. | ||||||
| ########################################################################### | ########################################################################### | ||||||
| from datetime import datetime |  | ||||||
| from flask import escape | from flask import escape | ||||||
| from Login import * | from Login import * | ||||||
| from Article import * | from Article import * | ||||||
| import Yaml | import Yaml | ||||||
| from user_service import userService | from user_service import userService | ||||||
| from account_service import accountService | from account_service import accountService | ||||||
|  | from admin_service import adminService | ||||||
| app = Flask(__name__) | app = Flask(__name__) | ||||||
| app.secret_key = 'lunch.time!' | app.secret_key = 'lunch.time!' | ||||||
| 
 | 
 | ||||||
| # 将蓝图注册到Lab app | # 将蓝图注册到Lab app | ||||||
| app.register_blueprint(userService) | app.register_blueprint(userService) | ||||||
| app.register_blueprint(accountService) | app.register_blueprint(accountService) | ||||||
|  | app.register_blueprint(adminService) | ||||||
| 
 | 
 | ||||||
| path_prefix = '/var/www/wordfreq/wordfreq/' | path_prefix = '/var/www/wordfreq/wordfreq/' | ||||||
| path_prefix = './'  # comment this line in deployment | path_prefix = './'  # comment this line in deployment | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| def get_random_image(path): | def get_random_image(path): | ||||||
|     ''' |     ''' | ||||||
|     返回随机图 |     返回随机图 | ||||||
|  | @ -102,79 +102,6 @@ def mainpage(): | ||||||
|                                d_len=d_len, lst=lst, yml=Yaml.yml) |                                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__': | if __name__ == '__main__': | ||||||
|     ''' |     ''' | ||||||
|     运行程序 |     运行程序 | ||||||
|  |  | ||||||
|  | @ -1,112 +1,120 @@ | ||||||
| <html> | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
| 
 | 
 | ||||||
| <head> | <head> | ||||||
|     <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" |     <meta charset="UTF-8"> | ||||||
|           integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous"> |     <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> | </head> | ||||||
| 
 | 
 | ||||||
| <body class="container" style="width: 800px; margin: auto; margin-top:24px;"> | <body class="container" style="width: 800px; margin: auto; margin-top:24px;"> | ||||||
| <nav class="navbar navbar-expand-lg bg-light"> |     <nav class="navbar navbar-expand-lg bg-light"> | ||||||
|     <div class="container-fluid"> |         <div class="container-fluid"> | ||||||
|         <a class="navbar-brand" href="#">你好 {{ username }}</a> |             <a class="navbar-brand" href="#">管理员 {{ username }} 您好!</a> | ||||||
|         <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" |             <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" | ||||||
|                 aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> |                 aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> | ||||||
|             <span class="navbar-toggler-icon"></span> |                 <span class="navbar-toggler-icon"></span> | ||||||
|         </button> |             </button> | ||||||
|         <div class="collapse navbar-collapse" id="navbarNav"> |             <div class="collapse navbar-collapse" id="navbarNav"> | ||||||
|             <ul class="navbar-nav"> |                 <ul class="navbar-nav"> | ||||||
|                 <li class="nav-item"> |                     <li class="nav-item"> | ||||||
|                     <a class="nav-link" href="/logout">退出登录</a> |                         <a class="nav-link" href="/{{ username }}">返回主页</a> | ||||||
|                 </li> |                     </li> | ||||||
|             </ul> |                     <li class="nav-item"> | ||||||
|  |                         <a class="nav-link" href="/logout">退出登录</a> | ||||||
|  |                     </li> | ||||||
|  |                 </ul> | ||||||
|  |             </div> | ||||||
|         </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> |     </div> | ||||||
| </nav> |  | ||||||
| 
 | 
 | ||||||
| <div class="card" style="margin-top:24px;"> |     <div class="card" style="margin-top:24px;"> | ||||||
|     <h5>重置选中用户密码</h5> |         {% if tips %} | ||||||
|     <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="alert alert-success" role="alert"> |         <div class="alert alert-success" role="alert"> | ||||||
|             {{ tips }} |             {{ tips }} | ||||||
|         </div> |         </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"> |                     <textarea id="source" name="source" class="form-control" placeholder="请输入来源"></textarea> | ||||||
|         <h5>录入文章</h5> |                     <label for="exampleInputEmail1" class="form-label">文章问题</label> | ||||||
|         <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 |                     <textarea id="question" name="question" class="form-control" placeholder="请输入问题"></textarea> | ||||||
|                         id="source" |                 </div> | ||||||
|                         name="source" |                 <input type="submit" value="保存" class="btn btn-outline-primary"> | ||||||
|                         class="form-control" |             </form> | ||||||
|                         placeholder="请输入来源" |         </div> | ||||||
|                 ></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> |  | ||||||
|     </div> |     </div> | ||||||
| </div> |  | ||||||
| 
 | 
 | ||||||
| <div class="card" style="margin-top:24px;"> |     <div class="card" style="margin-top:24px;"> | ||||||
|         <h5>文章列表</h5> |         <h5 style="margin-top: 10px;padding-left: 10px;">文章列表</h5> | ||||||
|     <div class="list-group"> |         <div class="list-group"> | ||||||
|         {% for text in text_list %} |             {% for text in text_list %} | ||||||
|             <div class="list-group-item list-group-item-action" aria-current="true"> |             <div class="list-group-item list-group-item-action" aria-current="true"> | ||||||
|                 <div class="d-flex w-100 justify-content-between"> |                 <div class="d-flex w-100 justify-content-between"> | ||||||
|                     <h5 class="mb-1">{{ text['source'] }}</h5> |                     <h5 class="mb-1">{{ text.source }}</h5> | ||||||
|                     <small>Date:{{ text['date'] }} Level:{{ text['level'] }}</small> |                     <small>Date:{{ text.date }} Level:{{ text.level }}</small> | ||||||
|                 </div> |                 </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> |             </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> | ||||||
| </div> |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| </body> | </body> | ||||||
| 
 | 
 | ||||||
| </html> | </html> | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
		Loading…
	
		Reference in New Issue