Search function

Added a (hopefully) functioning search function.
This commit is contained in:
Michael Rodin 2023-10-18 17:24:59 +02:00
parent 0d55022ba8
commit 978c634067
8 changed files with 191 additions and 29 deletions

View file

@ -14,6 +14,7 @@ app = Flask(__name__)
@app.template_filter('ctime')
def timectime(s):
return datetime.utcfromtimestamp(s).strftime('%Y-%m-%d %H:%M')
@app.template_filter('spacer')
def convsize(s):
sizes=("B","KB","MB","GB","TB")
@ -26,25 +27,30 @@ def convsize(s):
## WEB FRONTEND
@app.route('/')
def homepage():
# try to get sesskey, else logout state
# try to get userdata, else logout state
print(request.base_url)
try:
# get sesskey and get info about user
sesskey=request.cookies.get('session')
res,userid=db.check_sesskey(sesskey)
if not res:
return 500
userdata=db.get_user_info(userid)
logged_in=True
logged_in,userdata=get_login_info(request.cookies.get('session'))
except Exception as e:
logged_in=False
userdata=()
logged_in,userdata=False,()
#try: # TODO: CLEAN!
# # get sesskey and get info about user
# sesskey=request.cookies.get('session')
# res,userid=db.check_sesskey(sesskey)
# if not res:
# return 500
# userdata=db.get_user_info(userid)
# logged_in=True
#except Exception as e:
# logged_in=False
# userdata=()
archives=db.get_n_archives()
return render_template("home.html", title="Homepage",userdata=userdata,login=logged_in,archives=archives)
@app.route('/user')
@app.route('/user/<userid>')
@app.route('/user/<int:userid>')
def userpage(userid:int=0):
if userid==0:
if userid == 0:
return make_response(redirect('/'))
@app.route('/login', methods=["GET","POST"])
@ -64,18 +70,72 @@ def loginpage():
resp=setcookie("session",sesskey,lifetime)
return resp
else:
return "<h2>You've entered the wrong password. This incident will be reported.</h2><br> Go back and try again.<br>" + password.upper() + "<br>" + passhash.upper() # TODO: DELETE
return "<h2>You've entered the wrong password. This incident will be reported.</h2><br> Go back and try again."
# GET: Login form
else:
return render_template("login.html", title="Login")
@app.route('/search')
def searchpage():
# try to get userdata, else logout state
try:
logged_in,userdata=get_login_info(request.cookies.get('session'))
except Exception as e:
logged_in,userdata=False,()
# try to set all required variables, else defaults
try:
sorttype=request.args['sort']
except Exception as e:
sorttype="time"
try:
category=request.args['category']
except Exception as e:
category=0
try:
keywords=request.args['q']
keywords="".join(keywords).split(" ")
except Exception as e:
keywords=[]
try:
count=request.args['count']
except Exception as e:
count=20
archives=db.get_n_archives(sorttype,category,keywords,count)
catlist=db.get_all_categories()
htmlcatlist=[]
# parse all categories and sort them into select box
for cat in catlist:
if not cat[2]:
htmlcatlist.append((cat[0],cat[1]))
parent=cat[1]
parentid=cat[0]
for i in catlist:
if i[2] == parentid:
htmlcatlist.append((i[0],f"{parent}/{i[1]}"))
return render_template("search.html", title="Advanced Search",categories=htmlcatlist,userdata=userdata,login=logged_in,archives=archives)
## FUNCTIONS
## Checks if given sesskey is valid and returns user data
## OUTPUT: (if sesskey valid) logged_in:bool=True, userdata:tup
## (if sesskey invalid)
def get_login_info(sesskey:str):
logged_in,userid=db.check_sesskey(sesskey)
if logged_in:
userdata=db.get_user_info(userid)
else:
userdata=()
return logged_in,userdata
def setcookie(name:str,value:str,lifetime:int=10000):
resp = make_response(redirect('/'))
resp.set_cookie(name, value, max_age=lifetime)
return resp
## API CALLS
## API CALLS (NO THANKS)
# main driver function
if __name__ == '__main__':

View file

@ -96,7 +96,6 @@ class db:
## Gets and returns all user info about one (1) user
## OUTPUT: tuple=(ID:int,UNAME:str,DNAME:str,CREATED:int,STATE:text,PASSHASH:text)
def get_user_info(self, userid:int):
print("USERID:::::::::::" + userid)
self.cur.execute(f"SELECT * FROM Users WHERE ID='{userid}'")
return self.cur.fetchone()
@ -104,7 +103,6 @@ class db:
## OUTPUT: archive:tuple=(NAME:str,HASH:str,IMPORTED[UNIX]:int,CATEGORY,str,CATEGORY.DESCRIPTION:str,UNAME:str,DNAME:str)
## labels:array=[…,(LABEL:str,CATEGORY:str,CATDESC:str,LABTYPE:str,LABDESC:str),…]
def get_archive_info(self, hash:str):
#global cur
self.cur.execute(f"""SELECT Archs.NAME,Archs.HASH,Archs.IMPORTED,Cats.CATEGORY,Cats.DESCRIPTION,Users.UNAME,Users.DNAME FROM Archs
JOIN Cats ON Cats.ID=Archs.CATEGORY
JOIN Users ON Users.ID=Archs.OWNER
@ -119,16 +117,45 @@ class db:
labels=self.cur.fetchall()
return archive, labels
## Returns all categories.
## OUTPUT: array=[…,(ID:int,CATEGORY:str,PARENT:int,DESCRIPTION:str),…]
def get_all_categories(self):
self.cur.execute("SELECT * FROM Cats;")
return self.cur.fetchall()
## Returns n archives, sorted by (imported )time or size
## OUTPUT: archives:array=[…,(ID:int,NAME:str,SIZE:str,IMPORTED[UNIX]:int),…]
def get_n_archives(self, sorttype:str="time",category:int=0, count:int=20):
global cur
def get_n_archives(self, sorttype:str="time",category:int=0, keywords:list=[], count:int=20):
match sorttype:
case "size":
sorttype="SIZE"
sorttype="SIZE DESC"
case "time":
sorttype="IMPORTED DESC"
case "za":
sorttype="NAME DESC"
case _:
sorttype="IMPORTED"
self.cur.execute(f"""SELECT ID,NAME,SIZE,IMPORTED FROM Archs{"" if category==0 else " WHERE CATEGORY=" + category} ORDER BY {sorttype} DESC LIMIT {count};""")
sorttype="NAME ASC"
# create SQL query for keywords
keyword_string=""
for w in keywords:
keyword_string+=f"AND NAME LIKE '%{w}%' "
if len(keywords) == 1:
for w in keywords:
keyword_string+=f"OR HASH = '{w}' "
# Get all children of category (if exist) and put into query string
categories=self.get_all_categories()
catlist=[category]
for i in categories:
if i[2] == int(category):
catlist.append(str(i[0]))
category="(" + ",".join(catlist) + ")"
self.cur.execute(f"""SELECT ID,NAME,SIZE,IMPORTED FROM Archs
{"WHERE 1=1" if category==0 else " WHERE CATEGORY IN " + category}
{keyword_string}
ORDER BY {sorttype} LIMIT {count};""")
archives=self.cur.fetchall()
return archives

View file

@ -26,6 +26,7 @@ header > div#container {
height: 2em;
margin: auto 0.1em;
border: none;
border-radius: 0.1em;
background: linear-gradient(135deg, white, yellow);
}

View file

@ -1,8 +1,24 @@
div.grid-container {
display: grid;
grid-template-columns: auto auto auto;
grid-template-columns: auto max-content max-content;
}
div.grid-item {
padding: 0.2em;
border: 2px black solid;
}
div.grid-header:first-child {
border-radius: 0.5em 0 0 0;
}
div.grid-header:nth-child(3) {
border-radius: 0 0.5em 0 0;
}
div.grid-header {
text-align: center;
background: lightyellow;
padding: 0.4em;
border: 2px black solid;
}

0
flask/static/search.css Normal file
View file

View file

@ -12,7 +12,12 @@
<header>
<span id="title">{{title}}</span>
<div id="container">
<form action="/search" method="get">
<!-- BUTTONS IF USER LOGGED IN -->
{% if login %}
<a href="/add"><button class="big-button"><b>&#x2B</b></button></a>
{% endif %}
<!------------------------------->
<form action="/search" method="get">
<input type="text" name="q" placeholder="search">
<input type="submit" value="&#x1F50E;" class="big-button" id="search-button">
</form>

View file

@ -7,13 +7,16 @@
{% block content %}
{% if archives|length > 0 %}
<div class="grid-container">
{% for arch in archives %}
<a class="grid-item" href="/view/{{arch[0]}}"><div class="grid-item clickable"><b>{{arch[1]}}</b></div></a>
<div class="grid-item"><p>{{arch[2]|spacer}}</p></div>
<div class="grid-item"><p>{{arch[3]|ctime}}</p></div>
<div class="grid-header"><b>ARCHIVE</b></div>
<div class="grid-header"><b>SIZE</b></div>
<div class="grid-header"><b>IMPORTED</b></div>
{% for arch in archives %}
<a class="grid-item" href="/view/{{arch[0]}}"><div class="grid-item clickable"><b>{{arch[1]}}</b></div></a>
<div class="grid-item"><p> {{arch[2]|spacer}} </p></div>
<div class="grid-item"><p> {{arch[3]|ctime}} </p></div>
{% endfor %}
</div>
{% endfor %}
</div>
{% else %}
<p>No matching archives</p>
{% endif %}

View file

@ -0,0 +1,50 @@
{% extends "base.html" %}
{% block meta %}
<link rel="stylesheet" href="/static/search.css" />
<link rel="stylesheet" href="/static/home.css" />
{% endblock %}
{% block content %}
<form action="/search" method="get">
<div class="grid-selection">
<b>Sort by:</b>
<select name="sort">
<option value="time">Import Time</option>
<option value="az">A-Z</option>
<option value="za">Z-A</option>
<option value="size">Archive Size</option>
</select>
</div>
<div class="grid-selection">
<b>Category:</b>
<select name="category">
{% for i in categories %}
<option value="{{i[0]}}">{{i[1]}}</option>
{% endfor %}
</select>
</div>
{% if request.args.get('q') %}
<input type="text" name="q" value="{{request.args.get('q')}}" placeholder="Keywords">
{% else %}
<input type="text" name="q" placeholder="Keywords">
{% endif %}
<input type="submit" value="Search!">
<div class="grid-selection"></div>
</form>
{% if archives|length > 0 %}
<div class="grid-container">
<div class="grid-header"><b>ARCHIVE</b></div>
<div class="grid-header"><b>SIZE</b></div>
<div class="grid-header"><b>IMPORTED</b></div>
{% for arch in archives %}
<a class="grid-item" href="/view/{{arch[0]}}"><div class="grid-item clickable"><b>{{arch[1]}}</b></div></a>
<div class="grid-item"><p> {{arch[2]|spacer}} </p></div>
<div class="grid-item"><p> {{arch[3]|ctime}} </p></div>
{% endfor %}
</div>
{% else %}
<p>No matching archives</p>
{% endif %}
{% endblock %}