about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlexander Kabui2024-08-28 14:39:59 +0300
committerGitHub2024-08-28 14:39:59 +0300
commit72089330ca8b2f39174adb873c19919d2bd696f9 (patch)
treebebe24b041a1118878a0b086856415b1bea48eeb
parent16f978ecdaf450cfa700bd6e78194831ed9a8f57 (diff)
parent842a758f31143a5934b2798a4d0f40f11a402919 (diff)
downloadgenenetwork2-72089330ca8b2f39174adb873c19919d2bd696f9.tar.gz
Merge pull request #847 from genenetwork/feature/gnqa-search-history
Feature/gnqa search history
-rw-r--r--gn2/wqflask/templates/gnqa.html4
-rw-r--r--gn2/wqflask/templates/gnqa_answer.html33
-rw-r--r--gn2/wqflask/templates/gnqa_search_history.html46
-rw-r--r--gn2/wqflask/views.py64
4 files changed, 77 insertions, 70 deletions
diff --git a/gn2/wqflask/templates/gnqa.html b/gn2/wqflask/templates/gnqa.html
index 8b50fe43..b3bc74fd 100644
--- a/gn2/wqflask/templates/gnqa.html
+++ b/gn2/wqflask/templates/gnqa.html
@@ -93,7 +93,7 @@
 	    AI Search
 	    <small>
 	      <sup>
-		<button class="search-hist-btn" hx-get="/gnqna/hist/" hx-target="#swap" hx-swap="innerHTML" >
+		<button class="search-hist-btn" hx-get="/gnqna/hist" hx-target="#swap" hx-swap="innerHTML">
 		  [Search History]
 		</button>
 	      </sup>
@@ -107,7 +107,7 @@
 	    <button class="btn btn-default btn-sm col-xs-1 col-sm-1 col-sm-offset-3"
 		    hx-post="/gnqna"
 		    hx-target="#swap"
-		    hx-swap="innerHTML"
+		    hx-swap="innerHTML"		  
 		    hx-indicator="#indicator">
 		<i class="fa fa-search fa-3x" aria-hidden="true" title="Search"></i>
 		<img id="indicator" class="htmx-indicator" src="/static/gif/loader.gif"/>
diff --git a/gn2/wqflask/templates/gnqa_answer.html b/gn2/wqflask/templates/gnqa_answer.html
index 0ddcfde7..41c1b338 100644
--- a/gn2/wqflask/templates/gnqa_answer.html
+++ b/gn2/wqflask/templates/gnqa_answer.html
@@ -3,10 +3,10 @@
     <div class="row container gnqa-answer" style="margin-bottom: 1em">
 	<p class="row lead">
 	    <mark style="font-family: 'Linux Libertine','Georgia','Times','Source Serif Pro',serif;"><b><i>{{ query }}</i></b></mark><br/>
-	    {{ answer }}
+	    {{ answer|safe }}
 	</p>
 	<div class="rating row" data-doc-id="{{query}}">
-	    <button class="btn" id="upvote" data-toggle="tooltip" data-placement="top" title="Vote Up"><i class="fa fa-thumbs-up fa-sm fa-1x" aria-hidden="true"></i></button>
+	    <button class="btn" id="upvote"  data-toggle="tooltip" data-placement="top" title="Vote Up"><i class="fa fa-thumbs-up fa-sm fa-1x" aria-hidden="true"></i></button>
             <button class="btn" id="downvote" data-toggle="tooltip" data-placement="top" title="Vote Down"><i class="fa fa-thumbs-down fa-sm fa-1x" aria-hidden="true"></i></button>
 	    <sub id="rate" class="text-info">	    
 	     </sub>
@@ -32,7 +32,7 @@
 			    </div>
 			    <div id="collapse{{reference.doc_id}}" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="heading{{reference.doc_id}}">
 				<div class="panel-body">
-				    <p class="node-references">{{ reference.comboTxt }}</p>
+				    <p class="node-references">{{ reference.comboTxt|safe }}</p>
 				    <div>
 					{% if reference.pubmed %}
 					<details open>
@@ -60,7 +60,7 @@
 				</div>
 				<div id="collapse{{reference.doc_id}}" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading{{reference.doc_id}}">
 				    <div class="panel-body">
-					<p class="node-references">{{reference.comboTxt}}</p>
+					<p class="node-references">{{ reference.comboTxt|safe }}</p>
 				    <div>
 					{% if reference.pubmed %}
 					<details >
@@ -93,20 +93,27 @@
 {% block js %}
 
 <script>
+  function updateRatingHandler(target, responseObj, args){
+    let {status, response} = responseObj.xhr
+    if (status==200 && args == "upvote"){
+	  htmx.toggleClass(htmx.find('#upvote'), 'btn-success');
+	  htmx.removeClass(htmx.find("#downvote"), "btn-danger");
+      }
+      else if(status == 200 && args == "downvote") {
+          htmx.toggleClass(htmx.find('#downvote'), 'btn-danger');
+	  htmx.removeClass(htmx.find("#upvote"), "btn-success");
+      }
+      else {
+        alert(`Error occurred with status ${status} and Error ${response}` )
+}}
 var query = {{ query|tojson }};
 var answer = {{ answer|tojson }}
 var {task_id} = {{ task_id|tojson }}
-  htmx.on("#upvote", "click", function(evt){
+htmx.on("#upvote", "click", function(evt){
       vote_count = htmx.find(".btn-success") ? 0 : 1
-      htmx.ajax("POST", `/gnqna/rating/${task_id}/${vote_count}`, {target: "#rate", swap:"innerHTML",values: {'query': query, 'answer': answer}}).then(()=>{
-	  htmx.toggleClass(htmx.find('#upvote'), 'btn-success');
-	  htmx.removeClass(htmx.find("#downvote"), "btn-danger");
-})});
+    htmx.ajax("POST", `/gnqna/rating/${task_id}/${vote_count}`, {target: "#rate", handler: (target,obj)=> updateRatingHandler(target,obj,"upvote"), swap:"innerHTML",values: {'query': query, 'answer': answer}})});
   htmx.on("#downvote", "click", function(evt){
       vote_count = htmx.find(".btn-danger") ? 0 : -1
-      htmx.ajax("POST", `/gnqna/rating/${task_id}/${vote_count}`, {target: "#rate", swap:"innerHTML",values: {'query': query, 'answer': answer}}).then(()=>{
-      htmx.toggleClass(htmx.find('#downvote'), 'btn-danger');
-      htmx.removeClass(htmx.find("#upvote"), "btn-success")
-      })});
+      htmx.ajax("POST", `/gnqna/rating/${task_id}/${vote_count}`, {target: "#rate",handler: (target,obj)=> updateRatingHandler(target,obj,"downvote") , swap:"innerHTML",values: {'query': query, 'answer': answer}})});
 </script>
 {% endblock %}
diff --git a/gn2/wqflask/templates/gnqa_search_history.html b/gn2/wqflask/templates/gnqa_search_history.html
index 2c07b8c0..976fd7fd 100644
--- a/gn2/wqflask/templates/gnqa_search_history.html
+++ b/gn2/wqflask/templates/gnqa_search_history.html
@@ -1,42 +1,52 @@
-<section class="container-fluid  gnqa-copy">
+<section class="container-fluid  gnqa-copy" id="search-hist">
   <header class="row">
-
     <div class="panel panel default col-sm-6 col-sm-offset-3">
       <div class="panel panel-default">
 	<div class="panel-heading">
 	  <div>
-	    <h4 class="text-primary">You search History </h4>
+	    <h4 class="text-secondary" style="font-family: 'Linux Libertine','Georgia','Times','Source Serif Pro',serif;font-size:2.3rem">Your AI search History </h4>
 	  </div>
 	</div>
       </div>
     </div>
   </header>
   <div class="container row">
-  <div class="panel panel-default col-sm-6 col-sm-offset-3 ">
-    {% for record in prev_queries %}
-    <div class="panel-body">
-      <div class="row">
-	<input name="" type="checkbox" value="" class="col-sm-1">
+    <div>
+      <div class="col-sm-6 col-sm-offset-3" style="margin-bottom:10px">
+	<button type="button" class="btn btn-danger" id="delete-btn">Delete Selected </button>
+      </div>
+      <div >
+      <div class="panel panel-default col-sm-6 col-sm-offset-3 ">
+      <div>
+	<ul class="list-group list-group-flush" style="overflow-y:scroll">
+    {% for item in  prev_queries %}
+    <li class="row list-group-item">
+      	<input name="" type="checkbox"   value="{{item['task_id']}}" class="col-sm-1" style="height: 20px;
+  width: 20px;">
 	<div class="col-sm-10">
-	  {% for id,val in  record.items() %}
 	    <button
-	      hx-get="/gnqna/hist/search/{{id}}"
+	      hx-get="/gnqna/hist?query={{item['query']}}&search_term={{item['task_id']}}"
               hx-target="#swap"
 	      hx-swap="innerHTML"
 	      hx-trigger= "click"
 	      data-bs-toggle="tooltip"
 	      data-bs-placement="left"
-	      title="/gnqna/hist/search?{{id}}"
+	      title="/gnqna/hist?query={{item['query']}}&search_term={{item['task_id']}}"
 	      style="background:transparent;border:none;cursor:pointer"
 	      >
-	      <b class="text-info">{{val}} </b>
+	      <p class="text-info">{{item.get('query')}} </p>
 	    </button>
-	  {% endfor %}
-	</div>
-      </div>
-      </div>
-     {% endfor %}
+     </div>
+    </li>
+    {% endfor %}
+    </ul>
    </div>
   </div>
   </div>
- </section>
+</section>
+<script>
+  htmx.on("#delete-btn", "click", function(evt){
+      htmx.ajax("DELETE","/gnqna/hist", {target: "#search-hist","swap" :"outerHTML",
+				      values: Array.from(htmx.findAll("input[type=checkbox]:checked"), e => e.value)}) 
+  })
+</script>
diff --git a/gn2/wqflask/views.py b/gn2/wqflask/views.py
index 843ed07a..666e765a 100644
--- a/gn2/wqflask/views.py
+++ b/gn2/wqflask/views.py
@@ -137,6 +137,10 @@ def handle_generic_exceptions(e):
                                          stack={formatted_lines},
                                          error_image=animation,
                                          version=current_app.config.get("GN_VERSION")))
+    try:
+        resp.status_code = exc_type.code
+    except AttributeError:
+        resp.status_code = 500
     resp.set_cookie(err_msg[:32], animation)
     return resp
 
@@ -258,28 +262,19 @@ def gsearchtable():
 @app.route("/gnqna", methods=["POST", "GET"])
 @require_oauth2
 def gnqna():
-
     if request.method == "POST":
         try:
-            def __error__(resp):
-                return resp.json()
-
             def error_page(resp):
                 return render_template("gnqa_errors.html",
                                        **{"status_code": resp.status_code, **resp.json()})
 
             def __success__(resp):
                 return render_template("gnqa_answer.html", **{"gn_server_url": GN3_LOCAL_URL, **(resp.json())})
-            """
-            disable gn-auth currently not stable
-            if not user_logged_in():
-                return error_page("Please Login/Register to  Genenetwork to access this Service")
-            """
             token = session_info()["user"]["token"].either(
                 lambda err: err, lambda tok: tok["access_token"])
             return monad_requests.post(
                 urljoin(GN3_LOCAL_URL,
-                        "/api/llm/gnqna"),
+                        "/api/llm/search"),
                 json=dict(request.form),
                 headers={
                     "Authorization": f"Bearer {token}"
@@ -290,44 +285,39 @@ def gnqna():
                 error_page, __success__)
         except Exception as error:
             return flask.jsonify({"error": str(error)})
-    prev_queries = (monad_requests.get(
-        urljoin(GN3_LOCAL_URL,
-                "/api/llm/get_hist_names")
-    ).then(
-        lambda resp: resp
-    ).either(lambda x: [], lambda x: x.json()["prev_queries"]))
+    return render_template("gnqa.html")
 
-    return render_template("gnqa.html", prev_queries=prev_queries)
-
-
-@app.route("/gnqna/hist/", methods=["GET"])
-@require_oauth2
-def get_hist_titles():
-    token = session_info()["user"]["token"].either(
-        lambda err: err, lambda tok: tok["access_token"])
-    response = monad_requests.get(urljoin(GN3_LOCAL_URL,
-                                          "/api/llm/hist/titles"),
-                                  headers={
-        "Authorization": f"Bearer {token}"
-    }
-    ).then(lambda resp: resp).either(
-        lambda x:  x.json(), lambda x: x.json())
-    return render_template("gnqa_search_history.html", **response)
 
 
-@app.route("/gnqna/hist/search/<search_term>", methods=["GET"])
+@app.route("/gnqna/hist", methods=["GET", "DELETE"])
 @require_oauth2
-def fetch_hist_records(search_term):
+def get_gnqa_history():
+    def _error_(resp):
+        return render_template("gnqa_errors.html",
+                               **{"status_code": resp.status_code,
+                                  **resp.json()})
     token = session_info()["user"]["token"].either(
         lambda err: err, lambda tok: tok["access_token"])
+    if request.method == "DELETE":
+        monad_requests.post(urljoin(GN3_LOCAL_URL, "/api/llm/history"),
+                            json=dict(request.form),
+                            headers={
+                                 "Authorization": f"Bearer {token}"
+                            }
+                            ).either(
+                   _error_, lambda x: x.json())
     response = monad_requests.get(urljoin(GN3_LOCAL_URL,
-                                          f"/api/llm/history/{search_term}"),
+                 (f"/api/llm/history?search_term={request.args.get('search_term')}"
+                  if request.args.get("search_term") else "/api/llm/history")),
                                   headers={
         "Authorization": f"Bearer {token}"
     }
     ).then(lambda resp: resp).either(
-        lambda x:  x.json(), lambda x: x.json())
-    return render_template("gnqa_answer.html", **response)
+        _error_, lambda x: x.json())
+    if request.args.get("search_term"):
+        return render_template("gnqa_answer.html", **response)
+    return render_template("gnqa_search_history.html",
+                           prev_queries=response)
 
 
 @app.route("/gnqna/rating/<task_id>/<int(signed=True):weight>",