Search function
Added a (hopefully) functioning search function.
This commit is contained in:
parent
0d55022ba8
commit
978c634067
88
flask/app.py
88
flask/app.py
|
@ -14,6 +14,7 @@ app = Flask(__name__)
|
||||||
@app.template_filter('ctime')
|
@app.template_filter('ctime')
|
||||||
def timectime(s):
|
def timectime(s):
|
||||||
return datetime.utcfromtimestamp(s).strftime('%Y-%m-%d %H:%M')
|
return datetime.utcfromtimestamp(s).strftime('%Y-%m-%d %H:%M')
|
||||||
|
|
||||||
@app.template_filter('spacer')
|
@app.template_filter('spacer')
|
||||||
def convsize(s):
|
def convsize(s):
|
||||||
sizes=("B","KB","MB","GB","TB")
|
sizes=("B","KB","MB","GB","TB")
|
||||||
|
@ -26,25 +27,30 @@ def convsize(s):
|
||||||
## WEB FRONTEND
|
## WEB FRONTEND
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def homepage():
|
def homepage():
|
||||||
# try to get sesskey, else logout state
|
# try to get userdata, else logout state
|
||||||
|
print(request.base_url)
|
||||||
try:
|
try:
|
||||||
# get sesskey and get info about user
|
logged_in,userdata=get_login_info(request.cookies.get('session'))
|
||||||
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:
|
except Exception as e:
|
||||||
logged_in=False
|
logged_in,userdata=False,()
|
||||||
userdata=()
|
#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()
|
archives=db.get_n_archives()
|
||||||
return render_template("home.html", title="Homepage",userdata=userdata,login=logged_in,archives=archives)
|
return render_template("home.html", title="Homepage",userdata=userdata,login=logged_in,archives=archives)
|
||||||
|
|
||||||
@app.route('/user')
|
@app.route('/user')
|
||||||
@app.route('/user/<userid>')
|
@app.route('/user/<int:userid>')
|
||||||
def userpage(userid:int=0):
|
def userpage(userid:int=0):
|
||||||
if userid==0:
|
if userid == 0:
|
||||||
return make_response(redirect('/'))
|
return make_response(redirect('/'))
|
||||||
|
|
||||||
@app.route('/login', methods=["GET","POST"])
|
@app.route('/login', methods=["GET","POST"])
|
||||||
|
@ -64,18 +70,72 @@ def loginpage():
|
||||||
resp=setcookie("session",sesskey,lifetime)
|
resp=setcookie("session",sesskey,lifetime)
|
||||||
return resp
|
return resp
|
||||||
else:
|
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
|
# GET: Login form
|
||||||
else:
|
else:
|
||||||
return render_template("login.html", title="Login")
|
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
|
## 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):
|
def setcookie(name:str,value:str,lifetime:int=10000):
|
||||||
resp = make_response(redirect('/'))
|
resp = make_response(redirect('/'))
|
||||||
resp.set_cookie(name, value, max_age=lifetime)
|
resp.set_cookie(name, value, max_age=lifetime)
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
## API CALLS
|
## API CALLS (NO THANKS)
|
||||||
|
|
||||||
# main driver function
|
# main driver function
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -96,7 +96,6 @@ class db:
|
||||||
## Gets and returns all user info about one (1) user
|
## Gets and returns all user info about one (1) user
|
||||||
## OUTPUT: tuple=(ID:int,UNAME:str,DNAME:str,CREATED:int,STATE:text,PASSHASH:text)
|
## OUTPUT: tuple=(ID:int,UNAME:str,DNAME:str,CREATED:int,STATE:text,PASSHASH:text)
|
||||||
def get_user_info(self, userid:int):
|
def get_user_info(self, userid:int):
|
||||||
print("USERID:::::::::::" + userid)
|
|
||||||
self.cur.execute(f"SELECT * FROM Users WHERE ID='{userid}'")
|
self.cur.execute(f"SELECT * FROM Users WHERE ID='{userid}'")
|
||||||
return self.cur.fetchone()
|
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)
|
## 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),…]
|
## labels:array=[…,(LABEL:str,CATEGORY:str,CATDESC:str,LABTYPE:str,LABDESC:str),…]
|
||||||
def get_archive_info(self, hash: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
|
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 Cats ON Cats.ID=Archs.CATEGORY
|
||||||
JOIN Users ON Users.ID=Archs.OWNER
|
JOIN Users ON Users.ID=Archs.OWNER
|
||||||
|
@ -119,16 +117,45 @@ class db:
|
||||||
labels=self.cur.fetchall()
|
labels=self.cur.fetchall()
|
||||||
return archive, labels
|
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
|
## Returns n archives, sorted by (imported )time or size
|
||||||
## OUTPUT: archives:array=[…,(ID:int,NAME:str,SIZE:str,IMPORTED[UNIX]:int),…]
|
## 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):
|
def get_n_archives(self, sorttype:str="time",category:int=0, keywords:list=[], count:int=20):
|
||||||
global cur
|
|
||||||
match sorttype:
|
match sorttype:
|
||||||
case "size":
|
case "size":
|
||||||
sorttype="SIZE"
|
sorttype="SIZE DESC"
|
||||||
|
case "time":
|
||||||
|
sorttype="IMPORTED DESC"
|
||||||
|
case "za":
|
||||||
|
sorttype="NAME DESC"
|
||||||
case _:
|
case _:
|
||||||
sorttype="IMPORTED"
|
sorttype="NAME ASC"
|
||||||
self.cur.execute(f"""SELECT ID,NAME,SIZE,IMPORTED FROM Archs{"" if category==0 else " WHERE CATEGORY=" + category} ORDER BY {sorttype} DESC LIMIT {count};""")
|
|
||||||
|
# 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()
|
archives=self.cur.fetchall()
|
||||||
return archives
|
return archives
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ header > div#container {
|
||||||
height: 2em;
|
height: 2em;
|
||||||
margin: auto 0.1em;
|
margin: auto 0.1em;
|
||||||
border: none;
|
border: none;
|
||||||
|
border-radius: 0.1em;
|
||||||
background: linear-gradient(135deg, white, yellow);
|
background: linear-gradient(135deg, white, yellow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,24 @@
|
||||||
div.grid-container {
|
div.grid-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto auto auto;
|
grid-template-columns: auto max-content max-content;
|
||||||
}
|
}
|
||||||
div.grid-item {
|
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;
|
border: 2px black solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
0
flask/static/search.css
Normal file
0
flask/static/search.css
Normal file
|
@ -12,7 +12,12 @@
|
||||||
<header>
|
<header>
|
||||||
<span id="title">{{title}}</span>
|
<span id="title">{{title}}</span>
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<form action="/search" method="get">
|
<!-- BUTTONS IF USER LOGGED IN -->
|
||||||
|
{% if login %}
|
||||||
|
<a href="/add"><button class="big-button"><b>+</b></button></a>
|
||||||
|
{% endif %}
|
||||||
|
<!------------------------------->
|
||||||
|
<form action="/search" method="get">
|
||||||
<input type="text" name="q" placeholder="search">
|
<input type="text" name="q" placeholder="search">
|
||||||
<input type="submit" value="🔎" class="big-button" id="search-button">
|
<input type="submit" value="🔎" class="big-button" id="search-button">
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -7,13 +7,16 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% if archives|length > 0 %}
|
{% if archives|length > 0 %}
|
||||||
<div class="grid-container">
|
<div class="grid-container">
|
||||||
{% for arch in archives %}
|
<div class="grid-header"><b>ARCHIVE</b></div>
|
||||||
<a class="grid-item" href="/view/{{arch[0]}}"><div class="grid-item clickable"><b>{{arch[1]}}</b></div></a>
|
<div class="grid-header"><b>SIZE</b></div>
|
||||||
<div class="grid-item"><p>{{arch[2]|spacer}}</p></div>
|
<div class="grid-header"><b>IMPORTED</b></div>
|
||||||
<div class="grid-item"><p>{{arch[3]|ctime}}</p></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 %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>No matching archives</p>
|
<p>No matching archives</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
50
flask/templates/search.html
Normal file
50
flask/templates/search.html
Normal 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 %}
|
Loading…
Reference in a new issue