Added label search and pages
This commit is contained in:
parent
78ed396c36
commit
e6b69eb7b4
|
@ -1,6 +1,8 @@
|
|||
# RAR-Index
|
||||
It's some project for my school which is supposed to index _rar_-archives and make them searchable over a very ugly web frontend.
|
||||
|
||||
**!!Works only on python 3.10 and newer!!**
|
||||
|
||||
## Features
|
||||
- Add and delete archives
|
||||
- edit their labels (as their owner)
|
||||
|
|
25
flask/app.py
25
flask/app.py
|
@ -36,7 +36,7 @@ def convsize(s:int):
|
|||
def homepage():
|
||||
# try to get userdata, else logout state
|
||||
logged_in,userdata=get_login_info(request.cookies.get('session-id'))
|
||||
archives=db.get_n_archives()
|
||||
archives,count=db.get_n_archives()
|
||||
return render_template("home.html", title="Homepage",userdata=userdata,login=logged_in,archives=archives)
|
||||
|
||||
@app.route('/user')
|
||||
|
@ -195,7 +195,8 @@ def labeleditpage(archid:int):
|
|||
return render_template("labels.html", title="Edit Labels",userdata=userdata,login=logged_in,archive=archive,res_labels=label_dict,labels_names=labels_name_list)
|
||||
|
||||
@app.route('/search')
|
||||
def searchpage():
|
||||
@app.route('/search/<int:page>')
|
||||
def searchpage(page:int=1):
|
||||
# try to get userdata, else logout state
|
||||
logged_in,userdata=get_login_info(request.cookies.get('session-id'))
|
||||
|
||||
|
@ -205,8 +206,12 @@ def searchpage():
|
|||
except Exception as e:
|
||||
sorttype="time"
|
||||
try:
|
||||
category=request.args['category']
|
||||
label_dict=db.get_label_labeltypes(int(category))
|
||||
category=int(request.args['category'])
|
||||
catparentid=db.get_category_info(category)[2]
|
||||
if catparentid:
|
||||
label_dict=db.get_label_labeltypes(catparentid)
|
||||
else:
|
||||
label_dict=db.get_label_labeltypes(category)
|
||||
except Exception as e:
|
||||
category=0
|
||||
label_dict={}
|
||||
|
@ -215,7 +220,7 @@ def searchpage():
|
|||
except Exception as e:
|
||||
keywords=[]
|
||||
try:
|
||||
count=request.args['count']
|
||||
count=int(request.args['count'])
|
||||
except Exception as e:
|
||||
count=20
|
||||
labels=[]
|
||||
|
@ -228,11 +233,16 @@ def searchpage():
|
|||
continue
|
||||
except Exception as e:
|
||||
labels=[]
|
||||
archives=db.get_n_archives(sorttype,category,keywords,count,labels)
|
||||
archives,total_count=db.get_n_archives(sorttype,category,keywords,count,labels,page)
|
||||
# if the requested page is larger than the maximum, return page 1
|
||||
if page*count > total_count and (page-1)*count >= total_count:
|
||||
page=1
|
||||
archives,total_count=db.get_n_archives(sorttype,category,keywords,count,labels,page)
|
||||
|
||||
|
||||
htmlcatlist=get_category_selection()
|
||||
|
||||
return render_template("search.html", title="Advanced Search",categories=htmlcatlist,userdata=userdata,login=logged_in,archives=archives,res_labels=label_dict,labels=labels)
|
||||
return render_template("search.html", title="Advanced Search",page=page,count=count,total_count=total_count,categories=htmlcatlist,userdata=userdata,login=logged_in,archives=archives,res_labels=label_dict,labels=labels)
|
||||
|
||||
########## FUNCTIONS
|
||||
|
||||
|
@ -254,6 +264,7 @@ def get_login_info(sesskey:str):
|
|||
userdata=()
|
||||
return logged_in,userdata
|
||||
|
||||
## returns a cookie and redirects to the homepage
|
||||
def setcookie(name:str,value:str,lifetime:int=10000):
|
||||
resp = make_response(redirect('/'))
|
||||
resp.set_cookie(name, value, max_age=lifetime)
|
||||
|
|
|
@ -183,6 +183,12 @@ class db:
|
|||
def get_categories(self):
|
||||
self.cur.execute("SELECT * FROM Cats;")
|
||||
return self.cur.fetchall()
|
||||
|
||||
## Gets and returns all info about one (1) category
|
||||
## OUTPUT: tup=(ID:int,CATEGORY:str,PARENT:int,DESCRIPTION:str)
|
||||
def get_category_info(self, catid:int):
|
||||
self.cur.execute(f"SELECT * FROM Cats WHERE ID={catid}")
|
||||
return self.cur.fetchone()
|
||||
|
||||
## get all labeltypes and their respective labels based on a category parent
|
||||
## OUTPUT: res_dict:dict={…,LabType.NAME:[…,(ID:int,NAME:str),…],…}
|
||||
|
@ -245,24 +251,24 @@ class db:
|
|||
return True, ""
|
||||
|
||||
## Returns n archives, sorted by a column
|
||||
## OUTPUT: archives:array=[…,(ID:int,NAME:str,SIZE:str,IMPORTED[UNIX]:int),…]
|
||||
def get_n_archives(self, sorttype:str="time",category:int=0, keywords:list=[], count:int=20,labels:list=[]): # TODO: CLEANN!!!!!
|
||||
## OUTPUT: archives:array=[…,(ID:int,NAME:str,SIZE:str,IMPORTED[UNIX]:int),…], total_count:int
|
||||
def get_n_archives(self, sorttype:str="time",category:int=0, keywords:list=[], count:int=20, labels:list=None, page:int=1): # TODO: CLEANN!!!!!
|
||||
match sorttype:
|
||||
case "size":
|
||||
sorttype="SIZE DESC"
|
||||
sorttype="Archs.SIZE DESC"
|
||||
case "time":
|
||||
sorttype="IMPORTED DESC"
|
||||
sorttype="Archs.IMPORTED DESC"
|
||||
case "za":
|
||||
sorttype="NAME DESC"
|
||||
sorttype="Archs.NAME DESC"
|
||||
case _:
|
||||
sorttype="NAME ASC"
|
||||
sorttype="Archs.NAME ASC"
|
||||
|
||||
# create SQL query for keywords
|
||||
keyword_string=""
|
||||
for w in keywords:
|
||||
keyword_string+=f"AND NAME LIKE '%{w}%' "
|
||||
keyword_string+=f"AND Archs.NAME LIKE '%{w}%' "
|
||||
if len(keywords) == 1:
|
||||
keyword_string+=f"OR HASH = '{keywords[0]}' "
|
||||
keyword_string+=f"OR Archs.HASH = '{keywords[0]}' "
|
||||
|
||||
# get all children of category (if exist) and put into query string
|
||||
categories=self.get_categories()
|
||||
|
@ -272,13 +278,20 @@ class db:
|
|||
catlist.append(str(i[0]))
|
||||
categories="(" + ",".join(catlist) + ")"
|
||||
|
||||
self.cur.execute(f"""SELECT Archs.ID,Archs.NAME,Archs.SIZE,Archs.IMPORTED FROM Archs
|
||||
{"WHERE 1=1" if category==0 else "WHERE CATEGORY IN " + categories}
|
||||
# select all archives by keywords, categories and labels
|
||||
self.cur.execute(f"""SELECT DISTINCT Archs.ID,Archs.NAME,Archs.SIZE,Archs.IMPORTED FROM Archs
|
||||
JOIN ArchLab ON Archs.ID=ArchLab.ArchID
|
||||
{"WHERE 1=1" if category==0 else "WHERE Archs.CATEGORY IN " + categories}
|
||||
{f"AND ArchLab.LabID IN ({','.join(labels)})" if labels else ""}
|
||||
{keyword_string}
|
||||
ORDER BY {sorttype} LIMIT {count if count else 20}""")
|
||||
GROUP BY Archs.ID
|
||||
{f"HAVING COUNT(Archs.ID)={len(labels)}" if labels else ""}
|
||||
ORDER BY {sorttype}""")
|
||||
archives=self.cur.fetchall()
|
||||
res_archives=archives[page*count-count:page*count] # get archives for the selected page
|
||||
total_count=len(archives)
|
||||
|
||||
return archives
|
||||
return res_archives, total_count
|
||||
|
||||
if __name__ == "__main__":
|
||||
#startup()
|
||||
|
|
|
@ -31,4 +31,44 @@ a.grid-item {
|
|||
|
||||
div.clickable:hover {
|
||||
background: lightgrey;
|
||||
}
|
||||
|
||||
div.pageselect {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr max-content 1fr;
|
||||
margin: 1em auto;
|
||||
}
|
||||
|
||||
div.align-right {
|
||||
display: inline-flex;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
div.align-left {
|
||||
display: inline-flex;
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
div.pageitem {
|
||||
display: inline-block;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
height: 2em;
|
||||
width: 1.5em;
|
||||
}
|
||||
|
||||
|
||||
.pagebutton {
|
||||
background: lightgrey;
|
||||
font-weight: 600;
|
||||
justify-self: auto;
|
||||
border: none;
|
||||
margin: 0 0.2em;
|
||||
height: 2em;
|
||||
width: 1.5em;
|
||||
}
|
||||
|
||||
input.pagebutton:hover {
|
||||
background: grey;
|
||||
border: none;
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
{% block content %}
|
||||
{% if archives|length > 0 %}
|
||||
<h2>Newest archives</h2>
|
||||
<div class="grid-container">
|
||||
<div class="grid-header"><b>ARCHIVE</b></div>
|
||||
<div class="grid-header"><b>SIZE</b></div>
|
||||
|
@ -18,6 +19,6 @@
|
|||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>No matching archives</p>
|
||||
<p>Add some archives first!</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -18,7 +18,7 @@
|
|||
<option value="size" {% if "size" == request.args.get('sort') %}selected="selected"{% endif %}>Archive Size</option>
|
||||
</select>
|
||||
<b>Category:</b>
|
||||
<select name="category">
|
||||
<select onchange="this.form.submit()" name="category">
|
||||
<option value="0">None</option>
|
||||
{% for i in categories %}
|
||||
<option value="{{i[0]}}" {% if i[0] == request.args.get('category')|int %}selected="selected"{% endif %}>{{i[1]}}</option>
|
||||
|
@ -26,7 +26,6 @@
|
|||
</select>
|
||||
<b>Count: </b><input type="number" name="count" step="1" placeholder="Number of items" {% if request.args.get('count') %}value="{{request.args.get('count')}}"{% else %} value="20"{% endif %}>
|
||||
</div>
|
||||
{# <!-- Can't get that to work
|
||||
<div class="labels-container">
|
||||
{% for ltype in res_labels %}
|
||||
<div class="flex-item"><b>{{ltype}}</b>
|
||||
|
@ -35,8 +34,7 @@
|
|||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>-->
|
||||
#}
|
||||
</div>
|
||||
</div>
|
||||
{% if request.args.get('q') %}
|
||||
<input type="text" name="q" value="{{request.args.get('q')}}" placeholder="Keywords">
|
||||
|
@ -44,20 +42,46 @@
|
|||
<input type="text" name="q" placeholder="Keywords">
|
||||
{% endif %}
|
||||
<input type="submit" value="Search!">
|
||||
</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 %}
|
||||
{% 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 %}
|
||||
{% else %}
|
||||
<p>No matching archives</p>
|
||||
{% endif %}
|
||||
{% if total_count > page*count %}
|
||||
<div class="pageselect">
|
||||
<div class="align-right">
|
||||
{% if page-3 > 1 %}
|
||||
<div class="pageitem">…</div>
|
||||
{% endif %}
|
||||
{% for pagenum in range(page-3,page) %}
|
||||
{% if pagenum > 0 %}
|
||||
<input class="pagebutton" type=submit formaction="/search/{{pagenum}}" value="{{pagenum}}">
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="pageitem" id="currentpage">{{page}}</div>
|
||||
<div class="align-left">
|
||||
{% for pagenum in range(page+1,page+4) %}
|
||||
{% if (pagenum-1)*count < total_count %}
|
||||
<input class="pagebutton" type=submit formaction="/search/{{pagenum}}" value="{{pagenum}}">
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if (page+4)*count < total_count %}
|
||||
<div class="pageitem">…</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -20,6 +20,6 @@
|
|||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<img id="cover" src="/static/covers/{{archive[0]}}.webp" onerror="this.onerror=null; this.src='/static/covers/default.webp'" alt="Cover image">
|
||||
<img id="cover" src="/static/covers/custom/{{archive[0]}}.webp" onerror="this.onerror=null; this.src='/static/covers/default.webp'" alt="Cover image">
|
||||
</div>
|
||||
{% endblock %}
|
Loading…
Reference in a new issue