Browse Source

feat: 完善搜索功能

SongZihuan 3 years ago
parent
commit
f485151f71
8 changed files with 182 additions and 40 deletions
  1. 1 1
      app/app.py
  2. 9 1
      app/home.py
  3. 67 8
      app/test.py
  4. 8 1
      app/user.py
  5. 8 7
      core/db.py
  6. 3 3
      templates/index.html
  7. 0 9
      templates/not_word.html
  8. 86 10
      templates/test.html

+ 1 - 1
app/app.py

@@ -53,7 +53,7 @@ class HEnglishFlask(Flask):
             return user.load_user(name, None)
 
         self.register_blueprint(home.home, url_prefix="/")
-        self.register_blueprint(test.test, url_prefix="/test")
+        self.register_blueprint(test.test, url_prefix="/study")
 
     def update_config(self):
         self.config.update(configure.conf)

+ 9 - 1
app/home.py

@@ -1,5 +1,5 @@
 from flask import blueprints, url_for, request, redirect, render_template, flash, current_app, abort
-from flask_login import login_user, current_user
+from flask_login import login_user, current_user, login_required, logout_user
 from flask_wtf import FlaskForm
 from wtforms import StringField, PasswordField, BooleanField, SubmitField, ValidationError
 from wtforms.validators import DataRequired, Length, EqualTo
@@ -83,3 +83,11 @@ def register():
                 f"{register_form.name.data} with {register_form.template.data} register fail [{flat}]")
             flash("Register fail")
     return redirect(url_for("home.index"))
+
+
+@home.route('/logout')
+@login_required
+def logout():
+    logout_user()
+    flash("User logout")
+    return redirect(url_for("home.index"))

+ 67 - 8
app/test.py

@@ -1,23 +1,45 @@
-from flask import blueprints, render_template, current_app, abort, redirect, url_for, flash, make_response
+from flask import blueprints, render_template, current_app, abort, redirect, url_for, flash, make_response, request
 from flask_login import current_user, login_required
+from flask_wtf import FlaskForm
+from wtforms import StringField, SubmitField, BooleanField
+from wtforms.validators import DataRequired, Length
 from app.user import UserWordDataBase
 from itsdangerous import URLSafeTimedSerializer
 from itsdangerous.exc import BadData
-
+from threading import Thread
+from typing import Optional
+from core.word import Word
 
 test = blueprints.Blueprint("test", __name__)
 
 
-@test.route("/")
-@login_required
-def question():
+class SearchForm(FlaskForm):
+    search = StringField("Word", validators=[DataRequired(), Length(1, 50)])
+    from_internet = BooleanField("From internet")
+    add_to_db = BooleanField("Add to databases")
+    submit = SubmitField("Search")
+
+
+def __load_word(word):
     user: UserWordDataBase = current_user
-    word = user.rand_word()
+    box, box_sum = user.get_box_count()
+    search_from = SearchForm()
     if word is None:
-        return render_template("not_word.html")
+        return render_template("test.html", word=word, len=len,
+                               box=box, box_sum=box_sum,
+                               search=search_from, have_word=False)
     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)  # 需要使用len函数
+    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)  # 需要使用len函数
+
+
+@test.route("/")
+@login_required
+def question():
+    word = current_user.rand_word()
+    return __load_word(word)
 
 
 @test.route("/right/<string:word_id>")
@@ -72,3 +94,40 @@ def download(word: str):
     response = make_response(w_str)
     response.headers["Content-Disposition"] = f"attachment;filename={word}.henglish.txt"
     return response
+
+
+class Search(Thread):
+    def __init__(self, user: UserWordDataBase, word: str, internet: bool, add: bool):
+        super(Search, self).__init__()
+        self.word: Optional[Word] = None
+        self.word_str = word
+        self.internet = internet
+        self.add = add
+        self.user = user
+        self.daemon = True
+
+    def run(self):
+        self.word = self.user.find_word(self.word_str, self.internet, self.add)
+
+    def wait_event(self) -> Optional[Word]:
+        self.join(timeout=5)
+        return self.word
+
+
+@test.route("/search", methods=["GET", "POST"])
+@login_required
+def search():
+    form = SearchForm()
+    if not form.validate_on_submit():
+        word = request.args.get("word", "")
+        if len(word) == 0:
+            abort(404)
+        user = current_user._get_current_object()
+        th = Search(user, word, request.args.get("internet", 0) != '0', request.args.get("add", 0) != '0')
+        th.start()
+        word = th.wait_event()
+        if th.is_alive():
+            flash("Search timeout")
+        return __load_word(word)
+    return redirect(url_for("test.search",
+                            word=form.search.data, internet=int(form.from_internet.data), add=int(form.add_to_db.data)))

+ 8 - 1
app/user.py

@@ -4,7 +4,7 @@ import os
 from configure import conf
 from flask_login import UserMixin, AnonymousUserMixin
 import shutil
-from typing import Optional
+from typing import Optional, Tuple
 
 
 class AnonymousUser(AnonymousUserMixin):
@@ -38,6 +38,13 @@ class UserWordDataBase(WordDatabase, UserMixin):
     def delete_user(self):
         self.delete_self()
 
+    def get_box_count(self) -> Tuple[list, int]:
+        res = self.search(columns=["COUNT(id)", "box"], table="Word", group_by=["box"])
+        ret = [0, 0, 0, 0, 0]
+        for i in res:
+            ret[i[1] - 1] = i[0]
+        return ret, sum(ret)
+
 
 def check_base_db():
     if os.path.exists(os.path.join(conf["DB_TEMPLATE"], "base.db")):

+ 8 - 7
core/db.py

@@ -142,7 +142,7 @@ class WordDatabase(DataBase):
         )''')
         self.wd = word.WordDict()
 
-    def __add_word(self, q: str):
+    def __add_word(self, q: str, add: bool):
         r = self.wd.get_requests(q)
         if not r.is_find:
             return None
@@ -162,9 +162,10 @@ class WordDatabase(DataBase):
                 part = comment.part.replace("'", "''")
                 english = comment.english.replace("'", "''")
                 chinese = comment.chinese.replace("'", "''")
-                self.insert(table='Word',
-                            columns=['word', 'part', 'english', 'chinese', 'eg'],
-                            values=f"'{name_lower}', '{part}', '{english}', '{chinese}', '{eg} '")
+                if add:
+                    self.insert(table='Word',
+                                columns=['word', 'part', 'english', 'chinese', 'eg'],
+                                values=f"'{name_lower}', '{part}', '{english}', '{chinese}', '{eg} '")
                 self.__logger.info(f"Add word name: {name_lower} part: {part}")
         return ret
 
@@ -178,7 +179,7 @@ class WordDatabase(DataBase):
             w.add_comment(c)
         return w
 
-    def find_word(self, q: str, search: bool = True) -> Optional[word.Word]:
+    def find_word(self, q: str, search: bool = True, add: bool = True) -> Optional[word.Word]:
         res = self.search(columns=["id", "word", "part", "english", "chinese", "eg"],
                           table="Word",
                           where=f"LOWER(word)='{q.lower()}'")
@@ -186,7 +187,7 @@ class WordDatabase(DataBase):
             res = []
         if len(res) <= 0:
             if search:
-                return self.__add_word(q)
+                return self.__add_word(q, add)
             return None
         self.__logger.debug(f"Find word (not add) {q}")
         return self.__make_word(q, res)
@@ -212,7 +213,7 @@ class WordDatabase(DataBase):
         word_list = self.word_pattern.findall(line)
         for w in word_list:
             try:
-                if self.find_word(w, True) is None:
+                if self.find_word(w, True, True) is None:
                     self.__logger.debug(f"update word {w} fail")
                     response.add_error(w)
                 else:

+ 3 - 3
templates/index.html

@@ -1,7 +1,7 @@
 {% extends "base.html" %}
 
 {% block body %}
-    <article class="container mt-2 mb--2">
+    <article class="container mt-2 mb-2">
         <p class="col-8 offset-2 text-center" > {{ about }} </p>
         <section class="mt-2 text-center">
             <ul id="auth" class="nav nav-tabs justify-content-center mb-2">
@@ -12,7 +12,7 @@
             <div id="authContent" class="tab-content">
                 <div class="tab-pane fade" id="login">
                     <div class="col-12 col-lg-6 offset-lg-3">
-                        <form method="post" action="{{ url_for("home.login") }}" class="login-form">
+                        <form method="post" action="{{ url_for("home.login") }}">
                             {{ login_form.hidden_tag() }}
 
                             <div class="form-group text-left">
@@ -34,7 +34,7 @@
                 </div>
                 <div class="tab-pane fade" id="register">
                     <div class="col-12 col-lg-6 offset-lg-3">
-                        <form method="post" action="{{ url_for("home.register") }}" class="register-form">
+                        <form method="post" action="{{ url_for("home.register") }}">
                             {{ register_form.hidden_tag() }}
 
                             <div class="form-group text-left">

+ 0 - 9
templates/not_word.html

@@ -1,9 +0,0 @@
-{% extends "base.html" %}
-
-{% block body %}
-    <article class="container mt-2 mb--2">
-        <p class="col-8 offset-2 text-center" > {{ about }} </p>
-        <p> Sorry, there not any word. </p>
-    </article>
-
-{% endblock %}

+ 86 - 10
templates/test.html

@@ -1,20 +1,30 @@
 {% extends "base.html" %}
 
 {% block body %}
-    <article class="container mt-2 mb--2">
+    <article class="container mt-2 mb-2">
         <p class="col-8 offset-2 text-center" > {{ about }} </p>
         <section class="mt-2 text-center">
             <ul id="test" class="nav nav-tabs justify-content-center mb-2">
                 <li class="active nav-item"> <a href="#question" data-toggle="tab" class="nav-link"> Question </a> </li>
-                <li class="nav-item"> <a href="#info" data-toggle="tab" class="nav-link"> Info </a> </li>
-                <li class="nav-item"> <a href="#answer" data-toggle="tab" class="nav-link"> Answer </a> </li>
+                {% if have_word %}
+                    <li class="nav-item"> <a href="#info" data-toggle="tab" class="nav-link"> Info </a> </li>
+                    <li class="nav-item"> <a href="#answer" data-toggle="tab" class="nav-link"> Answer </a> </li>
+                {% endif %}
                 <li class="nav-item"> <a href="#user" data-toggle="tab" class="nav-link"> User </a> </li>
             </ul>
 
             <div id="testContent" class="tab-content">
-                <div class="tab-pane fade" id="question">
-                    <p class="text-center h2"> <u> {{ word.name }} </u> </p>
-                </div>
+                {% if have_word %}
+                    <div class="tab-pane fade" id="question">
+                        <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 %}
@@ -76,14 +86,80 @@
 
                             <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-danger mr-2 mb-2" data-toggle="modal" data-target="#DeleteModal" > Delete word </a>
-                            <a class="btn btn-info mr-2 mb-2" href="{{ url_for("test.right", word_id=word_id) }}"> Right </a>
-                            <a class="btn btn-info mr-2 mb-2" href="{{ url_for("test.wrong", word_id=word_id) }}" > Wrong </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>
+                {% endif %}
                 <div class="tab-pane fade" id="user">
-                    <p> There is user setting </p>
+                    <div id="LogoutModal" class="modal fade" role="dialog" aria-hidden="true">
+                        <div class="modal-dialog">
+                            <div class="modal-content">
+                                <div class="modal-header">
+                                    <h4 class="modal-title"> Logout? </h4>
+                                </div>
+                                <div class="modal-body">
+                                    <p> Are you sure you want to logout '{{ current_user.user }}' ? </p>
+                                </div>
+                                <div class="modal-footer">
+                                    <a class="btn btn-danger" href="{{ url_for("home.logout") }}" > Sure </a>
+                                    <a class="btn btn-secondary" data-dismiss="modal"> No </a>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+
+                    <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>
+                                </div>
+                                <div class="modal-body">
+                                    <p> Are you sure you want to reset '{{ current_user.user }}' ? </p>
+                                </div>
+                                <div class="modal-footer">
+                                    <a class="btn btn-danger" href="" > Sure </a>
+                                    <a class="btn btn-secondary" data-dismiss="modal"> No </a>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+
+                    <div class="col-12 col-lg-6 offset-lg-3 text-left">
+                        <h4 class="mb-2"> User: {{ current_user.user }} </h4>
+                        <p> You have {{ box_sum }} word(s) in database. </p>
+                        <div class="col-12 justify-content-around text-center mb-2">
+                            <a class="btn btn-light mr-2 mb-2"> <span class="small"> Box1: {{ box[0] }} </span> </a>
+                            <a class="btn btn-light mr-2 mb-2"> <span class="small"> Box2: {{ box[1] }} </span> </a>
+                            <a class="btn btn-light mr-2 mb-2"> <span class="small"> Box3: {{ box[2] }} </span> </a>
+                            <a class="btn btn-light mr-2 mb-2"> <span class="small"> Box4: {{ box[3] }} </span> </a>
+                            <a class="btn btn-light mb-2"> <span class="small"> Box5: {{ box[4] }} </span> </a>
+                        </div>
+
+                        <a class="col-12 btn btn-primary mr-2 mb-2"> Word List </a>
+
+                        <div class="p-2"></div>
+                        <form method="post" action="{{ url_for("test.search") }}">
+                            {{ search.hidden_tag() }}
+
+                            <div class="form-group text-left">
+                                {{ search.search(class="form-control") }}
+                            </div>
+
+                            <div class="text-left">
+                                {{ search.submit(class='btn btn-outline-primary mr-2') }}
+                                {{ search.from_internet() }} {{ search.from_internet.label }}
+                                {{ search.add_to_db() }} {{ search.add_to_db.label }}
+                            </div>
+                        </form>
+
+                        <div class="p-2"></div>
+                        <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>
+                    </div>
                 </div>
             </div>
         </section>