add: [website] tree view history

Flask session store history and after save it's store in DB
main
David Cruciani 2024-02-15 12:01:36 +01:00
parent b38e4feb37
commit 0364deccaa
No known key found for this signature in database
GPG Key ID: 8690CDE1E3994B9B
11 changed files with 449 additions and 61 deletions

View File

@ -1,3 +1,4 @@
import json
from .. import db
@ -32,13 +33,38 @@ class Session_db(db.Model):
query_date = db.Column(db.DateTime, index=True)
def to_json(self):
return
json_dict = {
"id": self.id,
"uuid": self.uuid,
"modules": json.loads(self.modules_list),
"query_enter": self.query_enter,
"input_query": self.input_query,
"config_module": json.loads(self.config_module),
"result": json.loads(self.result),
"nb_errors": self.nb_errors,
"query_date": self.query_date.strftime('%Y-%m-%d')
}
return json_dict
def history_json(self):
json_dict = {
"uuid": self.uuid,
"modules": json.loads(self.modules_list),
"query": self.query_enter,
"input": self.input_query,
"query_date": self.query_date.strftime('%Y-%m-%d')
}
return json_dict
class History(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
session_id = db.Column(db.Integer, index=True)
class History_Tree(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
session_uuid = db.Column(db.String(36), index=True)
tree = db.Column(db.String)
class Config(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)

View File

@ -15,6 +15,13 @@ home_blueprint = Blueprint(
def home():
return render_template("home.html")
@home_blueprint.route("/home/<sid>", methods=["GET", "POST"])
def home_query(sid):
if "query" in request.args:
query = request.args.get("query")
return render_template("home.html", query=query, sid=sid)
return render_template("404.html")
@home_blueprint.route("/query/<sid>")
def query(sid):
session = HomeModel.get_session(sid)
@ -24,7 +31,7 @@ def query(sid):
query_loc = session.query_enter
else:
for s in SessionModel.sessions:
if s.id == sid:
if s.uuid == sid:
flag = True
query_loc = s.query
session=s
@ -64,6 +71,7 @@ def run_modules():
if "input" in request.json:
if "modules" in request.json:
session = SessionModel.Session_class(request.json)
HomeModel.set_flask_session(session, request.json["parent_id"])
session.start()
SessionModel.sessions.append(session)
return jsonify(session.status()), 201
@ -79,7 +87,7 @@ def status(sid):
return jsonify(HomeModel.get_status_db(sess))
else:
for s in SessionModel.sessions:
if s.id == sid:
if s.uuid == sid:
return jsonify(s.status())
return jsonify({'message': 'Scan session not found'}), 404
@ -91,7 +99,7 @@ def result(sid):
return jsonify(HomeModel.get_result_db(sess))
else:
for s in SessionModel.sessions:
if s.id == sid:
if s.uuid == sid:
return jsonify(s.get_result())
return jsonify({'message': 'Scan session not found'}), 404
@ -145,4 +153,35 @@ def history():
def get_history():
"""Get all history"""
histories = HomeModel.get_history()
return histories
return histories
@home_blueprint.route("/history_session", methods=["GET"])
def history_session():
"""View all history"""
return render_template("history_session.html", tree_view=False)
@home_blueprint.route("/get_history_session", methods=["GET"])
def get_history_session():
"""Get all history"""
histories = HomeModel.get_history_session()
if histories:
return histories
return {}
@home_blueprint.route("/save_history/<sid>", methods=["GET"])
def save_history(sid):
return HomeModel.save_history_core(sid)
@home_blueprint.route("/history_tree", methods=["GET"])
def history_tree():
"""View all history"""
return render_template("history_session.html", tree_view=True)
@home_blueprint.route("/get_history_tree", methods=["GET"])
def get_history_tree():
"""Get all history"""
histories = HomeModel.get_history_tree()
if histories:
return histories
return {}

View File

@ -1,7 +1,10 @@
import json
from .utils.utils import query_get_module
from .utils.utils import query_get_module, isUUID
from . import db
from .db_class.db import History, Module, Config, Module_Config, Session_db
from .db_class.db import History, Module, Config, Module_Config, Session_db, History_Tree
from . import sess
from flask import session as sess
from sqlalchemy import desc
def get_module(mid):
@ -29,7 +32,7 @@ def get_module_config_both(mid, cid):
return Module_Config.query.filter_by(module_id=mid, config_id=cid).first()
def get_session(sid):
"""Return a session by id"""
"""Return a session by uuid"""
return Session_db.query.filter_by(uuid=sid).first()
def get_modules():
@ -138,14 +141,142 @@ def get_result_db(session):
def get_history():
"""Return history"""
histories_list = list()
histories = History.query.all()
histories = History.query.order_by(desc(History.id))
for history in histories:
session = Session_db.query.get(history.session_id)
histories_list.append({
"uuid": session.uuid,
"query": session.query_enter,
"modules": json.loads(session.modules_list),
"input": session.input_query,
"query_date": session.query_date.strftime('%Y-%m-%d')
})
histories_list.append(session.history_json())
return histories_list
def util_set_flask_session(parent_id, loc_session, current_session):
if parent_id == loc_session["uuid"]:
loc_json = {
"uuid": current_session.uuid,
"modules": current_session.modules_list,
"query": current_session.query,
"input": current_session.input_query,
"query_date": current_session.query_date.strftime('%Y-%m-%d')
}
loc_session["children"].append(loc_json)
return True
elif "children" in loc_session:
return deep_explore(loc_session["children"], parent_id, current_session)
def deep_explore(session_dict, parent_id, current_session):
for loc_session in session_dict:
if not "children" in loc_session:
loc_session["children"] = list()
if util_set_flask_session(parent_id, loc_session, current_session):
return True
return False
def set_flask_session(current_session, parent_id):
current_query = sess.get("current_query")
if not current_query or current_query not in sess:
loc_json = {
"uuid": current_session.uuid,
"modules": current_session.modules_list,
"query": current_session.query,
"input": current_session.input_query,
"query_date": current_session.query_date.strftime('%Y-%m-%d')
}
sess["current_query"] = current_session.uuid
sess[sess.get("current_query")] = loc_json
sess[sess.get("current_query")]["children"] = list()
else:
# sess["uuid"]
loc_session = sess.get(sess.get("current_query"))
if not "children" in loc_session:
loc_session["children"] = list()
if not util_set_flask_session(parent_id, loc_session, current_session):
sess["current_query"] = current_session.uuid
def get_history_session():
current_query = sess.get("current_query")
loc_list = list()
if current_query:
# If current query have no children then don't display it
# It's already save in history
# Only parent-child tree structure is in flask session
current_query_value = sess.get(sess.get("current_query"))
if current_query_value and current_query_value["children"]:
loc_list.append(current_query_value)
for q in sess:
if isUUID(q):
# If query have no children then don't display it
q_value = sess.get(q)
if q_value["children"]:
if not q == current_query:
loc_list.append(q_value)
return loc_list
def util_save_history(session):
loc_dict = dict()
loc_dict[session["uuid"]] = []
if "children" in session and session["children"]:
for child in session["children"]:
loc_dict[session["uuid"]].append(util_save_history(child))
return loc_dict
def save_history_core(sid):
"""Save history from session to db"""
if sid in sess:
session = sess.get(sid)
# Doesn't already exist
history_tree_db = History_Tree.query.filter_by(session_uuid=session["uuid"]).first()
if not history_tree_db:
if "children" in session and session["children"]:
# Get all children before add to db
loc_dict = util_save_history(session)
h = History_Tree(
session_uuid = session["uuid"],
tree=json.dumps(loc_dict)
)
db.session.add(h)
db.session.commit()
return {"message": "History Save", 'toast_class': "success-subtle"}
return {"message": "No children", 'toast_class': "warning-subtle"}
# Save same session but with new value
elif not json.loads(history_tree_db.tree) == session:
if "children" in session and session["children"]:
# Get all children before add to db
loc_dict = util_save_history(session)
history_tree_db.tree = json.dumps(loc_dict)
db.session.commit()
return {"message": "History updated", 'toast_class': "success-subtle"}
return {"message": "History already saved", 'toast_class': "warning-subtle"}
return {"message": "Session not found", 'toast_class': "danger-subtle"}
def util_get_history_tree(child):
loc_child = list(child.keys())[0]
loc_session = get_session(loc_child)
loc_json = loc_session.history_json()
loc_json["children"] = list()
if child[loc_child]:
for s_child in child[loc_child]:
loc_json["children"].append(util_get_history_tree(s_child))
return loc_json
def get_history_tree():
"""Return all histories saved as tree"""
histories_tree = History_Tree.query.order_by(desc(History_Tree.id))
loc_dict = list()
for history_tree in histories_tree:
tree = json.loads(history_tree.tree)
loc_session = get_session(history_tree.session_uuid)
loc_json = loc_session.history_json()
loc_json["children"] = list()
for child in tree[history_tree.session_uuid]:
loc_json["children"].append(util_get_history_tree(child))
loc_dict.append(loc_json)
return loc_dict

View File

@ -14,7 +14,7 @@ sessions = list()
class Session_class:
def __init__(self, request_json) -> None:
self.id = str(uuid4())
self.uuid = str(uuid4())
self.thread_count = 4
self.jobs = Queue(maxsize=0)
self.threads = []
@ -63,7 +63,7 @@ class Session_class:
registered = len(self.result)
return {
'id': self.id,
'id': self.uuid,
'total': total,
'complete': complete,
'remaining': remaining,
@ -110,7 +110,7 @@ class Session_class:
else:
send_to = {"module": work[1], self.input_query: self.query, "config": loc_config}
res = query_post_query(send_to)
print(res)
# print(res)
if "error" in res:
self.nb_errors += 1
self.result[work[1]] = res
@ -124,7 +124,7 @@ class Session_class:
def save_info(self):
"""Save info in the db"""
s = Session_db(
uuid=str(self.id),
uuid=str(self.uuid),
modules_list=json.dumps(self.modules_list),
query_enter=self.query,
input_query=self.input_query,

View File

@ -1,5 +1,4 @@
const { ref, nextTick } = Vue
export default {
name: "History_view",
delimiters: ['[[', ']]'],
@ -7,36 +6,63 @@ export default {
history: Object,
key_loop: Number
},
setup(props) {
return {
}
},
template: `
<div class="list-group" style="margin-bottom: 20px;">
<a :href="'/query/'+history.uuid" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">[[key_loop+1]]- [[history.query]]</h5>
<small><i>[[history.uuid]]</i></small>
</div>
<p class="mb-1" style="color: green;"><u>Input Attribute</u>:</p>
<div>[[history.input]]</div>
<br>
<p class="mb-1" style="color: #2000ff;"><u>Modules</u>:</p>
<div>
<template v-for="module in history.modules">[[module]],</template>
</div>
<div class="d-flex w-100 justify-content-between">
<div></div>
<small><i>[[history.query_date]]</i></small>
</div>
<div style="display: flex;">
<div style="list-style-type: none; padding: 10px; font-size: large; margin-left: 13px" >
<a v-if="'children' in history && history['children'].length" data-bs-toggle="collapse" style="color: black;" :href="'#collapseChild-'+history.uuid" role="button" aria-expanded="true" :aria-controls="'collapseChild-'+history.uuid">
<i class="fa-solid fa-caret-down"></i>
</a>
</div>
<a style="text-decoration: none; color: black;" data-bs-toggle="collapse" :href="'#collapse'+history.uuid" role="button" aria-expanded="false" :aria-controls="'collapse'+history.uuid">
<ul class="list-group list-group-horizontal" style="padding-top: 5px;">
<li class="list-group-item">
<h5>[[history.query]]</h5>
</li>
<li class="list-group-item">
<h5 style="color: brown"><u>Input Attributes</u></h5>
[[history.input]]
</li>
<li class="list-group-item">
<h5 style="color: brown"><u>Modules</u></h5>
<template v-for="module in history.modules">[[module]],</template>
</li>
</ul
</a>
</div>
</div>
<div>
<div class="collapse" :id="'collapse'+history.uuid" style="width: 70%; margin-left: 30px">
<div class="card card-body">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">[[history.query]]</h5>
<small><i>[[history.uuid]]</i></small>
</div>
<p class="mb-1" style="color: green;"><u>Input Attribute</u>:</p>
<div>[[history.input]]</div>
<br>
<p class="mb-1" style="color: #2000ff;"><u>Modules</u>:</p>
<div>
<template v-for="module in history.modules">[[module]],</template>
</div>
<div></div>
<div class="d-flex w-100 justify-content-between">
<div><a :href="'/query/'+history.uuid">See results</a></div>
<small><i>[[history.query_date]]</i></small>
</div>
</div>
</div>
<div class="collapse show" :id="'collapseChild-'+history.uuid">
<ul style="list-style-type: none;">
<li>
<div class="card-body">
<template v-for="h, key in history['children']">
<history_view :history="h" :key_loop="key" />
</template>
</div>
</li>
</ul>
</div>
</div>
`
}

View File

@ -9,12 +9,32 @@
<hr>
<br>
<div v-if="history">
<template v-for="h, key in history">
<history_view :history="h" :key_loop="key" />
<div class="list-group" style="margin-bottom: 20px;">
<a :href="'/query/'+h.uuid" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">[[key+1]]- [[h.query]]</h5>
<small><i>[[h.uuid]]</i></small>
</div>
<p class="mb-1" style="color: green;"><u>Input Attribute</u>:</p>
<div>[[h.input]]</div>
<br>
<p class="mb-1" style="color: #2000ff;"><u>Modules</u>:</p>
<div>
<template v-for="module in h.modules">[[module]],</template>
</div>
<div class="d-flex w-100 justify-content-between">
<div></div>
<small><i>[[h.query_date]]</i></small>
</div>
</a>
</div>
</template>
</div>
<span id="goTop">[<a href="#top">Go Back Top</a>]</span>
{% endblock %}
@ -22,7 +42,7 @@
{% block script %}
<script type="module">
const { createApp, ref, onMounted, nextTick, defineComponent} = Vue
import {display_toast, message_list} from '/static/js/toaster.js'
import {message_list} from '/static/js/toaster.js'
import history_view from '/static/js/history_view.js'
createApp({
delimiters: ['[[', ']]'],

View File

@ -0,0 +1,137 @@
<!--
Author: David Cruciani
-->
{% extends 'base.html' %}
{% block content %}
<input hidden value="{{tree_view}}" id="tree_view">
<h1 id="top">History</h1>
<small v-if="!tree_view"><i>All histories present here will be delete at the end of the session</i></small>
<hr>
<template v-if="Object.keys(history).length">
<div v-for="his, key in history" style="background-color: white; border: 1px solid #808080a1; border-radius: 8px; padding: 10px; margin-top: 17px;">
<div class="d-flex w-100 justify-content-between">
<h4># [[key + 1]]</h4>
<button v-if="!tree_view" class="btn btn-primary btn-sm" @click="save_history(his)">Save</button>
</div>
<hr>
<div style="display: flex;">
<a style="padding: 10px; font-size: large; color: black;" v-if="'children' in his && his['children'].length" data-bs-toggle="collapse" :href="'#collapseChild-'+his.uuid" aria-expanded="true" :aria-controls="'collapseChild-'+his.uuid">
<i class="fa-solid fa-caret-down"></i>
</a>
<a style="text-decoration: none; color: black;" data-bs-toggle="collapse" :href="'#collapse'+his.uuid" role="button" aria-expanded="false" :aria-controls="'collapse'+his.uuid">
<ul class="list-group list-group-horizontal">
<li class="list-group-item">
<h4>[[his.query]]</h4>
</li>
<li class="list-group-item">
<h5 style="color: brown"><u>Input Attributes</u></h5>
[[his.input]]
</li>
<li class="list-group-item">
<h5 style="color: brown"><u>Modules</u></h5>
<template v-for="module in his.modules">[[module]],</template>
</li>
</ul>
</a>
</div>
<div>
<div class="collapse" :id="'collapse'+his.uuid" style="width: 70%; margin-left:30px">
<div class="card card-body">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">[[his.query]]</h5>
<small><i>[[his.uuid]]</i></small>
</div>
<p class="mb-1" style="color: green;"><u>Input Attribute</u>:</p>
<div>[[his.input]]</div>
<br>
<p class="mb-1" style="color: #2000ff;"><u>Modules</u>:</p>
<div>
<template v-for="module in his.modules">[[module]],</template>
</div>
<div></div>
<div class="d-flex w-100 justify-content-between">
<div><a :href="'/query/'+his.uuid">See results</a></div>
<small><i>[[his.query_date]]</i></small>
</div>
</div>
</div>
<div class="collapse show" :id="'collapseChild-'+his.uuid">
<ul style="list-style-type: none;">
<li>
<div class="card-body">
<template v-for="h, key in his['children']">
<history_view :history="h" :key_loop="key" />
</template>
</div>
</li>
</ul>
</div>
</div>
</div>
</template>
<div v-else>
<i>No History</i>
</div>
<span id="goTop">[<a href="#top">Go Back Top</a>]</span>
{% endblock %}
{% block script %}
<script type="module">
const { createApp, ref, onMounted, nextTick, defineComponent} = Vue
import {display_toast, message_list} from '/static/js/toaster.js'
import history_view from '/static/js/history_view.js'
createApp({
delimiters: ['[[', ']]'],
components: {
history_view
},
setup() {
const history = ref({})
const tree_view = ref(false)
async function get_history_session(){
let res = await fetch("/get_history_session")
let loc = await res.json()
history.value = loc
}
async function get_history_tree(){
let res = await fetch("/get_history_tree")
let loc = await res.json()
history.value = loc
}
async function save_history(history){
const res = await fetch("/save_history/" + history.uuid)
display_toast(res)
}
onMounted(() => {
tree_view.value = $("#tree_view").val()
if(tree_view.value == "True"){
get_history_tree()
tree_view.value = true
}
else{
tree_view.value = false
get_history_session()
}
})
return {
message_list,
history,
tree_view,
save_history,
}
}
}).mount('.container')
</script>
{% endblock %}

View File

@ -15,7 +15,8 @@
<br>
<div style="width:50%; transform: translate(50%, 0);">
<div>
<input type="text" id="process-query" placeholder="Enter here..." autofocus class="form-control" style="border-radius: 5px;" />
<input type="hidden" id="parent_id" value="{{sid}}">
<input type="text" value="{{query}}" id="process-query" placeholder="Enter here..." autofocus class="form-control" style="border-radius: 5px;" />
</div>
<span v-if="status_site" id="status">[[status_site]]</span>
</div>
@ -100,8 +101,9 @@
let error_flag = false
let result_dict = {"modules": $("#modules_select").val(),
"input": $("#input_select").val(),
"query": current_query.value
"input": $("#input_select").val(),
"query": current_query.value,
"parent_id": $("#parent_id").val()
}
result_dict["config"] = {}
for(let el in config_query.value){

View File

@ -127,6 +127,7 @@
<div>
Value: [[attr.value]]
</div>
<a v-if="attr.type != 'counter' && attr.type != 'datetime'" :href="'/home/{{sid}}?query='+attr.value">query</a>
</div>
</div>
<hr>

View File

@ -18,6 +18,12 @@
<a style="margin-top: 30px;" href="/history" class="list-group-item list-group-item-action py-2 ripple">
<i class="fa-solid fa-clock-rotate-left fa-fw me-3"></i><span>History</span>
</a>
<a style="margin-top: 30px;" href="/history_session" class="list-group-item list-group-item-action py-2 ripple">
<i class="fa-solid fa-clock fa-fw me-3"></i><span>History Session</span>
</a>
<a style="margin-top: 30px;" href="/history_tree" class="list-group-item list-group-item-action py-2 ripple">
<i class="fa-solid fa-timeline fa-fw me-3"></i><span>History Tree</span>
</a>
<a style="margin-top: 30px;" href="/modules_config" class="list-group-item list-group-item-action py-2 ripple">
<i class="fa-solid fa-gear fa-fw me-3"></i><span>Config</span>
</a>

View File

@ -1,5 +1,5 @@
# import os
# import uuid
import uuid
import json
import requests
# import jsonschema
@ -31,12 +31,12 @@ def query_post_query(data, headers={'Content-type': 'application/json'}):
return r.json()
# def isUUID(uid):
# try:
# uuid.UUID(str(uid))
# return True
# except ValueError:
# return False
def isUUID(uid):
try:
uuid.UUID(str(uid))
return True
except ValueError:
return False
# def form_to_dict(form):