瀏覽代碼

feat: 完善搜索功能

SongZihuan 3 年之前
父節點
當前提交
f485151f71
共有 8 個文件被更改,包括 182 次插入40 次删除
  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)
             return user.load_user(name, None)
 
 
         self.register_blueprint(home.home, url_prefix="/")
         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):
     def update_config(self):
         self.config.update(configure.conf)
         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 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 flask_wtf import FlaskForm
 from wtforms import StringField, PasswordField, BooleanField, SubmitField, ValidationError
 from wtforms import StringField, PasswordField, BooleanField, SubmitField, ValidationError
 from wtforms.validators import DataRequired, Length, EqualTo
 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}]")
                 f"{register_form.name.data} with {register_form.template.data} register fail [{flat}]")
             flash("Register fail")
             flash("Register fail")
     return redirect(url_for("home.index"))
     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_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 app.user import UserWordDataBase
 from itsdangerous import URLSafeTimedSerializer
 from itsdangerous import URLSafeTimedSerializer
 from itsdangerous.exc import BadData
 from itsdangerous.exc import BadData
-
+from threading import Thread
+from typing import Optional
+from core.word import Word
 
 
 test = blueprints.Blueprint("test", __name__)
 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
     user: UserWordDataBase = current_user
-    word = user.rand_word()
+    box, box_sum = user.get_box_count()
+    search_from = SearchForm()
     if word is None:
     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"])
     serializer = URLSafeTimedSerializer(current_app.config["SECRET_KEY"])
     word_id = serializer.dumps({"word": word.name})
     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>")
 @test.route("/right/<string:word_id>")
@@ -72,3 +94,40 @@ def download(word: str):
     response = make_response(w_str)
     response = make_response(w_str)
     response.headers["Content-Disposition"] = f"attachment;filename={word}.henglish.txt"
     response.headers["Content-Disposition"] = f"attachment;filename={word}.henglish.txt"
     return response
     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 configure import conf
 from flask_login import UserMixin, AnonymousUserMixin
 from flask_login import UserMixin, AnonymousUserMixin
 import shutil
 import shutil
-from typing import Optional
+from typing import Optional, Tuple
 
 
 
 
 class AnonymousUser(AnonymousUserMixin):
 class AnonymousUser(AnonymousUserMixin):
@@ -38,6 +38,13 @@ class UserWordDataBase(WordDatabase, UserMixin):
     def delete_user(self):
     def delete_user(self):
         self.delete_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():
 def check_base_db():
     if os.path.exists(os.path.join(conf["DB_TEMPLATE"], "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()
         self.wd = word.WordDict()
 
 
-    def __add_word(self, q: str):
+    def __add_word(self, q: str, add: bool):
         r = self.wd.get_requests(q)
         r = self.wd.get_requests(q)
         if not r.is_find:
         if not r.is_find:
             return None
             return None
@@ -162,9 +162,10 @@ class WordDatabase(DataBase):
                 part = comment.part.replace("'", "''")
                 part = comment.part.replace("'", "''")
                 english = comment.english.replace("'", "''")
                 english = comment.english.replace("'", "''")
                 chinese = comment.chinese.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}")
                 self.__logger.info(f"Add word name: {name_lower} part: {part}")
         return ret
         return ret
 
 
@@ -178,7 +179,7 @@ class WordDatabase(DataBase):
             w.add_comment(c)
             w.add_comment(c)
         return w
         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"],
         res = self.search(columns=["id", "word", "part", "english", "chinese", "eg"],
                           table="Word",
                           table="Word",
                           where=f"LOWER(word)='{q.lower()}'")
                           where=f"LOWER(word)='{q.lower()}'")
@@ -186,7 +187,7 @@ class WordDatabase(DataBase):
             res = []
             res = []
         if len(res) <= 0:
         if len(res) <= 0:
             if search:
             if search:
-                return self.__add_word(q)
+                return self.__add_word(q, add)
             return None
             return None
         self.__logger.debug(f"Find word (not add) {q}")
         self.__logger.debug(f"Find word (not add) {q}")
         return self.__make_word(q, res)
         return self.__make_word(q, res)
@@ -212,7 +213,7 @@ class WordDatabase(DataBase):
         word_list = self.word_pattern.findall(line)
         word_list = self.word_pattern.findall(line)
         for w in word_list:
         for w in word_list:
             try:
             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")
                     self.__logger.debug(f"update word {w} fail")
                     response.add_error(w)
                     response.add_error(w)
                 else:
                 else:

+ 3 - 3
templates/index.html

@@ -1,7 +1,7 @@
 {% extends "base.html" %}
 {% extends "base.html" %}
 
 
 {% block body %}
 {% 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>
         <p class="col-8 offset-2 text-center" > {{ about }} </p>
         <section class="mt-2 text-center">
         <section class="mt-2 text-center">
             <ul id="auth" class="nav nav-tabs justify-content-center mb-2">
             <ul id="auth" class="nav nav-tabs justify-content-center mb-2">
@@ -12,7 +12,7 @@
             <div id="authContent" class="tab-content">
             <div id="authContent" class="tab-content">
                 <div class="tab-pane fade" id="login">
                 <div class="tab-pane fade" id="login">
                     <div class="col-12 col-lg-6 offset-lg-3">
                     <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() }}
                             {{ login_form.hidden_tag() }}
 
 
                             <div class="form-group text-left">
                             <div class="form-group text-left">
@@ -34,7 +34,7 @@
                 </div>
                 </div>
                 <div class="tab-pane fade" id="register">
                 <div class="tab-pane fade" id="register">
                     <div class="col-12 col-lg-6 offset-lg-3">
                     <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() }}
                             {{ register_form.hidden_tag() }}
 
 
                             <div class="form-group text-left">
                             <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" %}
 {% extends "base.html" %}
 
 
 {% block body %}
 {% 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>
         <p class="col-8 offset-2 text-center" > {{ about }} </p>
         <section class="mt-2 text-center">
         <section class="mt-2 text-center">
             <ul id="test" class="nav nav-tabs justify-content-center mb-2">
             <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="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>
                 <li class="nav-item"> <a href="#user" data-toggle="tab" class="nav-link"> User </a> </li>
             </ul>
             </ul>
 
 
             <div id="testContent" class="tab-content">
             <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="tab-pane fade" id="info">
                     <div class="col-12 col-lg-6 offset-lg-3">
                     <div class="col-12 col-lg-6 offset-lg-3">
                         {% for w in word.comment %}
                         {% 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-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-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>
                     </div>
                 </div>
                 </div>
+                {% endif %}
                 <div class="tab-pane fade" id="user">
                 <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>
             </div>
             </div>
         </section>
         </section>