소스 검색

feat: 错误处理

SongZihuan 3 년 전
부모
커밋
010a037eb5
8개의 변경된 파일149개의 추가작업 그리고 107개의 파일을 삭제
  1. 7 1
      app/app.py
  2. 2 0
      app/home.py
  3. 38 16
      app/test.py
  4. 6 6
      app/word_list.py
  5. 5 3
      core/db.py
  6. 9 0
      templates/error.html
  7. 81 80
      templates/test.html
  8. 1 1
      templates/word_list.html

+ 7 - 1
app/app.py

@@ -1,4 +1,4 @@
-from flask import Flask
+from flask import Flask, render_template
 from flask.logging import default_handler
 from flask_login import LoginManager
 import configure
@@ -50,6 +50,12 @@ class HEnglishFlask(Flask):
         def user_loader(name: str):
             return user.load_user(name, None)
 
+        func = {"render_template": render_template}
+        for i in [400, 401, 403, 404, 405, 408, 410, 414, 500, 501, 502]:
+            exec(f"def error_{i}(e):\n"
+                 f"\treturn render_template('error.html', error_status={i}, error_info=e)", func)
+            self.errorhandler(i)(func[f"error_{i}"])
+
         self.register_blueprint(home.home, url_prefix="/")
         self.register_blueprint(test.test, url_prefix="/study")
         self.register_blueprint(word_list.word_list, url_prefix="/word")

+ 2 - 0
app/home.py

@@ -45,6 +45,7 @@ def index():
 def login():
     if not current_user.is_anonymous:
         current_app.logger.debug(f"re-login and abort(304)")
+        flash(f"You are login as {current_user.name}")
         abort(304)
 
     login_form = LoginForm()
@@ -67,6 +68,7 @@ def login():
 def register():
     if not current_user.is_anonymous:
         current_app.logger.debug(f"re-login and register(304)")
+        flash(f"You are login as {current_user.name}")
         abort(304)
 
     register_form = RegisterForm()

+ 38 - 16
app/test.py

@@ -1,6 +1,6 @@
 from flask import blueprints, render_template, current_app, abort, redirect, url_for, flash, make_response, request
 from flask import send_file
-from flask_login import current_user, login_required
+from flask_login import current_user, login_required, logout_user
 from flask_wtf import FlaskForm
 from wtforms import StringField, SubmitField, BooleanField, PasswordField
 from wtforms.validators import DataRequired, Length
@@ -22,26 +22,26 @@ class SearchForm(FlaskForm):
     submit = SubmitField("Search")
 
 
-class ResetForm(FlaskForm):
+class ResetDeleteForm(FlaskForm):
     name = StringField("User name", validators=[DataRequired(), Length(1, 32)])
     passwd = PasswordField("Passwd", validators=[DataRequired(), Length(4, 32)])
-    submit = SubmitField("Reset")
+    submit = SubmitField("Submit")
 
 
 def __load_word(word):
     user: UserWordDataBase = current_user
     box, box_sum = user.get_box_count()
     search_from = SearchForm()
-    reset_form = ResetForm()
+    reset_delete_form = ResetDeleteForm()
     if word is None:
         return render_template("test.html", word=word, len=len,
                                box=box, box_sum=box_sum,
-                               search=search_from, have_word=False)
+                               search=search_from, have_word=False, reset_delete=reset_delete_form)
     serializer = URLSafeTimedSerializer(current_app.config["SECRET_KEY"])
     word_id = serializer.dumps({"word": word.name})
     return render_template("test.html", word=word, len=len,
                            word_id=word_id, box=box, box_sum=box_sum,
-                           search=search_from, have_word=True, reset=reset_form)
+                           search=search_from, have_word=True, reset_delete=reset_delete_form)
 
 
 @test.route("/")
@@ -59,7 +59,10 @@ def right(word_id: str):
         word: dict = serializer.loads(word_id, max_age=120)  # 120s内生效
         user: UserWordDataBase = current_user
         user.right_word(word["word"])
-    except (BadData, KeyError):
+    except BadData:
+        flash(f"Timeout for confirm word")
+        abort(408)
+    except KeyError:
         abort(404)
     return redirect(url_for("test.question"))
 
@@ -72,7 +75,10 @@ def wrong(word_id: str):
         word: dict = serializer.loads(word_id, max_age=120)  # 120s内生效
         user: UserWordDataBase = current_user
         user.wrong_word(word["word"])
-    except (BadData, KeyError):
+    except BadData:
+        flash(f"Timeout for confirm word")
+        abort(408)
+    except KeyError:
         abort(404)
     return redirect(url_for("test.question"))
 
@@ -130,7 +136,7 @@ def search():
     if not form.validate_on_submit():
         word = request.args.get("word", "")
         if len(word) == 0:
-            abort(404)
+            abort(400)
         user = current_user._get_current_object()
         th = Search(user, word, request.args.get("internet", 0) != '0', request.args.get("add", 0) != '0')
         th.start()
@@ -149,7 +155,7 @@ def download_table(file_type: str):
     try:
         max_eg = int(request.args.get("max_eg", -1))
     except (ValueError, TypeError):
-        return abort(404)
+        return abort(400)
     df = user.export_frame(max_eg, file_type == "html")
     if file_type == "csv":
         df_io = io.BytesIO()
@@ -173,21 +179,37 @@ def download_table(file_type: str):
         df.to_latex(df_io)
         df_io = io.BytesIO(df_io.getvalue().encode('utf-8'))
     else:
-        return abort(404)
+        return abort(400)
     df_io.seek(0, io.SEEK_SET)
     return send_file(df_io, attachment_filename=f"{user.user}.henglish.{file_type}", as_attachment=True)
 
 
-@test.route("/reset", methods=["POST"])
+@test.route("/reset_user", methods=["POST"])
 @login_required
 def reset_user():
-    reset = ResetForm()
-    if reset.validate_on_submit():
+    reset_form = ResetDeleteForm()
+    if reset_form.validate_on_submit():
         user: UserWordDataBase = current_user
-        if not user.check_passwd(reset.passwd.data):
+        if not user.check_passwd(reset_form.passwd.data):
             flash("Passwd error.")
         else:
             flash("User reset")
             user.reset()
         return redirect(url_for("test.question"))
-    abort(404)
+    abort(400)
+
+
+@test.route("/delete_user", methods=["POST"])
+@login_required
+def delete_user():
+    delete_form = ResetDeleteForm()
+    if delete_form.validate_on_submit():
+        user: UserWordDataBase = current_user
+        if not user.check_passwd(delete_form.passwd.data):
+            flash("Passwd error.")
+        else:
+            flash("User reset")
+            logout_user()
+            user.delete_user()
+        return redirect(url_for("test.question"))
+    abort(400)

+ 6 - 6
app/word_list.py

@@ -7,7 +7,7 @@ word_list = blueprints.Blueprint("word_list", __name__)
 
 def __load_word_list(res, page, page_count, word_list_title, url, up_url, next_url):
     if res is None or (page != 1 and page > page_count):
-        abort(404)
+        abort(400)
     word = []
     for i in res:
         item = [i[1], i[0], i[2], i[3], i[4], ""]
@@ -38,9 +38,9 @@ def show_all_word():
     try:
         page = int(request.args.get("page", 1))
     except (ValueError, TypeError):
-        return abort(404)
+        return abort(400)
     if page < 1:
-        abort(404)
+        abort(400)
     res = user.search(columns=["word", "box", "part", "english", "chinese", "eg"],
                       table="Word", limit=20, offset=(page - 1) * 20,
                       order_by=[("word", "ASC"), ("box", "ASC")])
@@ -59,13 +59,13 @@ def show_all_word():
 def show_box_word(box: int):
     user: UserWordDataBase = current_user
     if box < 0 or box > 5:
-        abort(404)
+        abort(400)
     try:
         page = int(request.args.get("page", 1))
     except (ValueError, TypeError):
-        return abort(404)
+        return abort(400)
     if page < 1:
-        abort(404)
+        abort(400)
     res = user.search(columns=["word", "box", "part", "english", "chinese", "eg"],
                       table="Word", limit=20, offset=(page - 1) * 20, where=f"box={box}",
                       order_by=[("word", "ASC"), ("box", "ASC")])

+ 5 - 3
core/db.py

@@ -180,9 +180,10 @@ class WordDatabase(DataBase):
         return w
 
     def find_word(self, q: str, search: bool = True, add: bool = True) -> Optional[word.Word]:
+        name_lower = q.lower().replace("'", "''")
         res = self.search(columns=["id", "word", "part", "english", "chinese", "eg"],
                           table="Word",
-                          where=f"LOWER(word)='{q.lower()}'")
+                          where=f"LOWER(word)='{name_lower}'")
         if res is None:
             res = []
         if len(res) <= 0:
@@ -282,7 +283,8 @@ class WordDatabase(DataBase):
         count = 0
         word_list = self.word_pattern.findall(line)
         for w in word_list:
-            cur = self.delete(table="Word", where=f"LOWER(word)='{w.lower()}'")
+            name_lower = w.lower().replace("'", "''")
+            cur = self.delete(table="Word", where=f"LOWER(word)='{name_lower}'")
             if cur[1].rowcount != -1:
                 self.__logger.debug(f"delete word {w} success")
                 count += 1
@@ -308,7 +310,7 @@ class WordDatabase(DataBase):
         return True
 
     def wrong_word(self, w: str):
-        name_lower = w.lower().replace("'", "''")
+        name_lower = w.lower().replace('\'', '\'\'')
         self.update(table="Word", kw={"box": "1"}, where=f"LOWER(word)='{name_lower}'")
         return True
 

+ 9 - 0
templates/error.html

@@ -0,0 +1,9 @@
+{% extends "base.html" %}
+
+{% block body %}
+    <article class="container mt-2 mb-2 text-center">
+        <h4 class="col-12"> {{ error_status }} </h4>
+        <p class="col-12"> {{ error_info }} </p>
+        <a class="btn btn-primary" href="{{ url_for("home.index") }}"> Back to study </a>
+    </article>
+{% endblock %}

+ 81 - 80
templates/test.html

@@ -13,84 +13,84 @@
             </ul>
 
             <div id="testContent" class="tab-content">
-                {% if have_word %}
-                    <div class="tab-pane fade" id="question">
+                <div class="tab-pane fade" id="question">
+                    {% if have_word %}
                         <p class="text-center h2"> <u> {{ word.name }} </u> </p>
-                    </div>
-                {% else %}
-                    <div class="text-center">
-                        <p> Sorry, there not any word. </p>
-                        <a class="btn btn-dark mr-2 mb-2" href="{{ url_for("test.question") }}"> Next word </a>
-                    </div>
-                {% endif %}
-            {% if have_word %}
-                <div class="tab-pane fade" id="info">
-                    <div class="col-12 col-lg-6 offset-lg-3">
-                        {% for w in word.comment %}
-                            <p class="h6 text-left">
-                                {{ word.comment[w].part }}
-                            </p>
-
-                            <ul class="text-left">
-                                {% for e in word.comment[w].eg %}
-                                    <li> <p> {{ e.split('##')[0] }} </p> </li>
-                                {% endfor %}
-                            </ul>
-                        {% endfor %}
-                    </div>
+                    {% else %}
+                        <div class="text-center">
+                            <p> Sorry, there not any word. </p>
+                            <a class="btn btn-dark mr-2 mb-2" href="{{ url_for("test.question") }}"> Next word </a>
+                        </div>
+                    {% endif %}
                 </div>
-                <div class="tab-pane fade" id="answer">
-                    <div class="col-12 col-lg-6 offset-lg-3">
-                        {% for w in word.comment %}
-                            <p class="h5 text-left">
-                                {{ word.comment[w].chinese }}
-                            </p>
-
-                            <p class="h6 text-left">
-                                {{ word.comment[w].english }}
-                            </p>
-
-                            <p class="h6 text-left">
-                                ({{ word.comment[w].part }})
-                            </p>
-
-                            <ul class="text-left">
-                                {% for e in word.comment[w].eg %}
-                                    <li>
-                                        <p> {{ e.split('##')[0] }} </p>
-                                        <p> {{ e.split('##')[1] }} </p>
-                                    </li>
-                                {% endfor %}
-                            </ul>
-                        {% endfor %}
-                        <div class="row justify-content-center mb-2">
-
-                            <div id="DeleteModal" class="modal fade" role="dialog" aria-hidden="true">
-                                <div class="modal-dialog">
-                                    <div class="modal-content">
-                                        <div class="modal-header">
-                                            <h4 class="modal-title"> Do you sure delete this word? </h4>
-                                        </div>
-                                        <div class="modal-body">
-                                            <p> Do you sure delete '{{ word.name }}' ? </p>
-                                            <p> After doing this, you will never see this word again. </p>
-                                        </div>
-                                        <div class="modal-footer">
-                                            <a class="btn btn-danger" href="{{ url_for("test.delete", word=word.name) }}" > Sure </a>
-                                            <a class="btn btn-secondary" data-dismiss="modal"> No </a>
+                {% if have_word %}
+                    <div class="tab-pane fade" id="info">
+                        <div class="col-12 col-lg-6 offset-lg-3">
+                            {% for w in word.comment %}
+                                <p class="h6 text-left">
+                                    {{ word.comment[w].part }}
+                                </p>
+
+                                <ul class="text-left">
+                                    {% for e in word.comment[w].eg %}
+                                        <li> <p> {{ e.split('##')[0] }} </p> </li>
+                                    {% endfor %}
+                                </ul>
+                            {% endfor %}
+                        </div>
+                    </div>
+                    <div class="tab-pane fade" id="answer">
+                        <div class="col-12 col-lg-6 offset-lg-3">
+                            {% for w in word.comment %}
+                                <p class="h5 text-left">
+                                    {{ word.comment[w].chinese }}
+                                </p>
+
+                                <p class="h6 text-left">
+                                    {{ word.comment[w].english }}
+                                </p>
+
+                                <p class="h6 text-left">
+                                    ({{ word.comment[w].part }})
+                                </p>
+
+                                <ul class="text-left">
+                                    {% for e in word.comment[w].eg %}
+                                        <li>
+                                            <p> {{ e.split('##')[0] }} </p>
+                                            <p> {{ e.split('##')[1] }} </p>
+                                        </li>
+                                    {% endfor %}
+                                </ul>
+                            {% endfor %}
+                            <div class="row justify-content-center mb-2">
+
+                                <div id="DeleteModal" class="modal fade" role="dialog" aria-hidden="true">
+                                    <div class="modal-dialog">
+                                        <div class="modal-content">
+                                            <div class="modal-header">
+                                                <h4 class="modal-title"> Do you sure delete this word? </h4>
+                                            </div>
+                                            <div class="modal-body">
+                                                <p> Do you sure delete '{{ word.name }}' ? </p>
+                                                <p> After doing this, you will never see this word again. </p>
+                                            </div>
+                                            <div class="modal-footer">
+                                                <a class="btn btn-danger" href="{{ url_for("test.delete", word=word.name) }}" > Sure </a>
+                                                <a class="btn btn-secondary" data-dismiss="modal"> No </a>
+                                            </div>
                                         </div>
                                     </div>
                                 </div>
-                            </div>
 
-                            <a class="btn btn-dark mr-2 mb-2" href="{{ url_for("test.question") }}"> Next word </a>
-                            <a class="btn btn-light mr-2 mb-2" href="{{ url_for("test.download", word=word.name) }}"> Download word </a>
-                            <a class="btn btn-outline-danger mr-2 mb-2" data-toggle="modal" data-target="#DeleteModal" > Delete word </a>
-                            <a class="btn btn-success mr-2 mb-2" href="{{ url_for("test.right", word_id=word_id) }}"> Right </a>
-                            <a class="btn btn-danger mr-2 mb-2" href="{{ url_for("test.wrong", word_id=word_id) }}" > Wrong </a>
+                                <a class="btn btn-dark mr-2 mb-2" href="{{ url_for("test.question") }}"> Next word </a>
+                                <a class="btn btn-light mr-2 mb-2" href="{{ url_for("test.download", word=word.name) }}"> Download word </a>
+                                <a class="btn btn-outline-danger mr-2 mb-2" data-toggle="modal" data-target="#DeleteModal" > Delete word </a>
+                                <a class="btn btn-success mr-2 mb-2" href="{{ url_for("test.right", word_id=word_id) }}"> Right </a>
+                                <a class="btn btn-danger mr-2 mb-2" href="{{ url_for("test.wrong", word_id=word_id) }}" > Wrong </a>
+                            </div>
                         </div>
                     </div>
-                </div>
                 {% endif %}
                 <div class="tab-pane fade" id="user">
                     <div id="LogoutModal" class="modal fade" role="dialog" aria-hidden="true">
@@ -110,27 +110,28 @@
                         </div>
                     </div>
 
-                    <form method="post" action="{{ url_for("test.reset_user") }}">
-                        {{ reset.hidden_tag() }}
+                    <form>
+                        {{ reset_delete.hidden_tag() }}
                         <div id="ResetModal" class="modal fade" role="dialog" aria-hidden="true">
                             <div class="modal-dialog">
                                 <div class="modal-content">
                                     <div class="modal-header">
-                                        <h4 class="modal-title"> Reset user? </h4>
+                                        <h4 class="modal-title"> Reset or delete user? </h4>
                                     </div>
                                     <div class="modal-body">
-                                        <p> Are you sure you want to reset '{{ current_user.user }}' ? </p>
+                                        <p> Are you sure you want to reset or delete '{{ current_user.user }}' ? </p>
                                         <div class="form-group text-left">
-                                            {{ reset.name.label }}
-                                            {{ reset.name(class="form-control") }}
+                                            {{ reset_delete.name.label }}
+                                            {{ reset_delete.name(class="form-control") }}
                                         </div>
                                         <div class="form-group text-left">
-                                            {{ reset.passwd.label }}
-                                            {{ reset.passwd(class="form-control") }}
+                                            {{ reset_delete.passwd.label }}
+                                            {{ reset_delete.passwd(class="form-control") }}
                                         </div>
                                     </div>
                                     <div class="modal-footer">
-                                        {{ reset.submit(class='btn btn-info mr-2') }}
+                                        {{ reset_delete.submit(class='btn btn-outline-danger mr-2', formmethod="post", formaction=url_for("test.reset_user"), value="Reset") }}
+                                        {{ reset_delete.submit(class='btn btn-danger mr-2', formmethod="post", formaction=url_for("test.delete_user"), value="Delete") }}
                                         <a class="btn btn-secondary" data-dismiss="modal"> No </a>
                                     </div>
                                 </div>
@@ -179,7 +180,7 @@
                         </form>
 
                         <a class="col-12 btn btn-outline-danger mr-2 mb-2" data-toggle="modal" data-target="#LogoutModal"> LOGOUT </a>
-                        <a class="col-12 btn btn-danger mr-2 mb-2" data-toggle="modal" data-target="#ResetModal" > RESET USER </a>
+                        <a class="col-12 btn btn-danger mr-2 mb-2" data-toggle="modal" data-target="#ResetModal" > RESET/DELETE USER </a>
                     </div>
                 </div>
             </div>

+ 1 - 1
templates/word_list.html

@@ -30,10 +30,10 @@
         </table>
         <div class="text-center">
             <div class="btn-group">
-                <a class="btn btn-primary" href="{{ url_for("home.index") }}"> Back to study </a>
                 {% if up_url %}
                     <a class="btn btn-info" href="{{ up_url }}"> up </a>
                 {% endif %}
+                <a class="btn btn-primary" href="{{ url_for("home.index") }}"> Back to study </a>
                 {% if next_url %}
                     <a class="btn btn-dark" href="{{ next_url }}"> next </a>
                 {% endif %}