diff --git a/.gitignore b/.gitignore index 4cef53c..7c1773c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,6 @@ !/README.md !/requirements.txt +/flask/static/covers/** +!/flask/static/covers/default.webp **__pycache__** \ No newline at end of file diff --git a/flask/app.py b/flask/app.py index 806876c..f032c05 100644 --- a/flask/app.py +++ b/flask/app.py @@ -24,6 +24,7 @@ def convsize(s): s=s/1000 return str("%.2f" % s)+sizes[n] + ## WEB FRONTEND @app.route('/') def homepage(): @@ -92,6 +93,39 @@ def loginpage(): else: return render_template("login.html", title="Login") +@app.route('/view/') +def viewpage(archid:int): + logged_in,userdata=get_login_info(request.cookies.get('session')) + archive,category,labels=db.get_archive_info(archid) + return render_template("view.html", title="View Archive",userdata=userdata,login=logged_in,archive=archive,category=category,labels=labels) + +@app.route('/labels/', methods=["GET","POST"]) +def labeleditpage(archid:int): + logged_in,userdata=get_login_info(request.cookies.get('session')) + archive,category,labels=db.get_archive_info(archid) + label_dict=db.get_label_labeltypes(category[3]) + if not logged_in or userdata[0] != archive[8]: + return make_response(redirect(f"/view/{archid}")) + + # POST: parse everything and update labels + if request.method == 'POST': + on_labels=[] + print(request.form) + for i in request.form: + on_labels.append(i) + res, data=db.update_labels(archid, on_labels) + if not res: + return f"

ERROR: {data}

Go back and try again" + return make_response(redirect(f"/view/{archid}")) + + + # GET: return normal labels page + labels_name_list=[] + for i in labels: + labels_name_list.append(i[0]) + 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(): # try to get userdata, else logout state @@ -144,7 +178,7 @@ def setcookie(name:str,value:str,lifetime:int=10000): ## Gets all categories and returns them (with or without parents) ## OUTPUT: […,(ID:int,NAME:str),…] def get_category_selection(include_parents:bool=True): - catlist=db.get_all_categories() + catlist=db.get_categories() htmlcatlist=[] # parse all categories and sort them into list for cat in catlist: diff --git a/flask/func.py b/flask/func.py index b3c6d6b..9ec92fa 100644 --- a/flask/func.py +++ b/flask/func.py @@ -111,7 +111,7 @@ class db: archive["hash"]=archive["hash"].upper() if not re.match('[A-Z0-9]{40}', archive["hash"]): return False, "Hash needs to be 40 characters in hexadecimal (SHA-1)." - if re.match('.*[^A-Za-z0-9\. _-].*', archive["name"]): + if re.match('.*[^A-Za-z0-9\. +_-].*', archive["name"]): return False, "The name contains illegal characters. Allowed chars: '[A-Za-z0-9\. _-]'" print(archive["name"]) @@ -126,28 +126,93 @@ class db: ## Returns all relevant information about one (1) archive - ## 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): - self.cur.execute(f"""SELECT Archs.NAME,Archs.HASH,Archs.IMPORTED,Cats.CATEGORY,Cats.DESCRIPTION,Users.UNAME,Users.DNAME FROM Archs + ## OUTPUT: archive:tuple=(ID:int,NAME:str,HASH:str,SIZE:int,IMPORTED[UNIX]:int,CATEGORY.ID:int,CATEGORY,str,CATEGORY.DESCRIPTION:str,USER.ID:int,DNAME:str), + ## category:tuple=(ID:int,CATEGORY:str,DESCRIPTION:str,PID:int,PCAT:str,PDESC:str) + ## labels:array=[…,(LABEL:str,LABTYPE:int,LABDESC:str),…] + def get_archive_info(self, archid:int): + # get info about archive itself + self.cur.execute(f"""SELECT Archs.ID,Archs.NAME,Archs.HASH,Archs.SIZE,Archs.IMPORTED,Cats.ID,Cats.CATEGORY,Cats.DESCRIPTION,Users.ID,Users.DNAME FROM Archs JOIN Cats ON Cats.ID=Archs.CATEGORY JOIN Users ON Users.ID=Archs.OWNER - WHERE hash='{hash}'""") + WHERE Archs.ID='{archid}'""") archive=self.cur.fetchone() - self.cur.execute(f"""SELECT Labs.LABEL,Cats.CATEGORY,Cats.DESCRIPTION AS CATDESC,LabType.NAME AS LABTYPE,LabType.DESCRIPTION AS LABDESC FROM ArchLab + # get info about category and it's parent + self.cur.execute(f"""SELECT c.ID,c.CATEGORY,c.DESCRIPTION,p.ID AS PID,p.CATEGORY as PCAT,p.DESCRIPTION AS PDESC FROM Cats c, Cats p + WHERE c.ID={archive[5]} AND c.PARENT=p.ID""") + category=self.cur.fetchone() + # get info about labels of archive + self.cur.execute(f"""SELECT Labs.LABEL,LabType.ID AS LABTYPE,LabType.DESCRIPTION AS LABDESC FROM ArchLab JOIN Archs ON Archs.ID=ArchLab.ARCHID JOIN Labs ON Labs.ID=ArchLab.LABID - JOIN Cats ON Labs.CATEGORY=Cats.ID JOIN LabType ON Labs.TYPE=LabType.ID - WHERE ARCHID=1;""") + WHERE ARCHID={archid};""") labels=self.cur.fetchall() - return archive, labels + return archive, category, labels ## Returns all categories. ## OUTPUT: array=[…,(ID:int,CATEGORY:str,PARENT:int,DESCRIPTION:str),…] - def get_all_categories(self): + def get_categories(self): self.cur.execute("SELECT * FROM Cats;") return self.cur.fetchall() + + ## get all labeltypes and their respective labels based on a category parent + ## OUTPUT: res_dict:dict={…,LabType.NAME:[…,(ID:int,NAME:str),…],…} + def get_label_labeltypes(self, catparentid:int): + # gets all relevant labtypes: […,(ID.int,NAME:str),…] + self.cur.execute(f"""SELECT LabType.ID,LabType.NAME FROM CatLabType + JOIN Cats ON Cats.ID=CatLabType.CATID + JOIN LabType ON LabType.ID=CatLabType.LABID + WHERE Cats.ID={catparentid} + ORDER BY LabType.NAME ASC""") + labtypes_list=self.cur.fetchall() + labtypes_ids,labtypes_names=[],[] + for w,e in labtypes_list: + labtypes_ids.append(str(w)) + labtypes_names.append(e) + ltid_string="(" + ",".join(labtypes_ids) + ")" + # gets all relevant labs: […,(ID:int,NAME:str,LTNAME:str),…] + self.cur.execute(f"""SELECT Labs.ID,Labs.LABEL,LabType.NAME AS LTNAME FROM Labs + JOIN LabType ON Labs.TYPE=LabType.ID + WHERE LabType.ID IN {ltid_string} + ORDER BY Labs.LABEL ASC""") + labs_list=self.cur.fetchall() + res_dict={} + # creates all labtype entries in dict + for i in labtypes_names: + res_dict[i]=[] + # puts all labs into their respective labtype + for entry in labs_list: + res_dict[entry[2]].append(entry[:2]) + return res_dict + + ## get a list of enabled labels and update the DB to reflect that state + ## OUTPUT: (if on_labels empty) bool=False, str + ## (else) + def update_labels(self, archid:int, on_labels:list): # TODO: CLEAN!!!! + # fail if no labels passed + if len(on_labels) == 0: + return False, "You have to select at least one label!" + + # get all relevant labels + self.cur.execute(f"""SELECT ArchLab.LABID FROM ArchLab + WHERE ArchLab.ARCHID={archid}""") + existing_labs=[] + for i in self.cur.fetchall(): + existing_labs.append(i[0]) + to_add=[] + # get all missing labels to add + for lab in on_labels: + if int(lab) not in existing_labs: + to_add.append(lab) + + # remove all labels which are not on + self.cur.execute(f"""DELETE FROM ArchLab WHERE ARCHID={archid} AND LABID NOT IN ({",".join(on_labels)})""") + to_add_list=[] + for i in to_add: + to_add_list.append("(" + str(archid) + "," + str(i) + ")") + # add all new labels + self.cur.execute(f"""INSERT INTO ArchLab(ARCHID,LABID) VALUES{",".join(to_add_list)}""") + return True, "" ## Returns n archives, sorted by (imported )time or size ## OUTPUT: archives:array=[…,(ID:int,NAME:str,SIZE:str,IMPORTED[UNIX]:int),…] @@ -167,11 +232,10 @@ class db: 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}' " + keyword_string+=f"OR HASH = '{keywords[0]}' " # Get all children of category (if exist) and put into query string - categories=self.get_all_categories() + categories=self.get_categories() catlist=[str(category)] for i in categories: if i[2] == int(category): diff --git a/flask/static/covers/default.webp b/flask/static/covers/default.webp new file mode 100644 index 0000000..08df195 Binary files /dev/null and b/flask/static/covers/default.webp differ diff --git a/flask/static/labels.css b/flask/static/labels.css new file mode 100644 index 0000000..ea0be13 --- /dev/null +++ b/flask/static/labels.css @@ -0,0 +1,8 @@ +div.flex-container { + display: flex; + flex: auto; +} + +div.flex-item { + display: inline-block; +} \ No newline at end of file diff --git a/flask/static/view.css b/flask/static/view.css new file mode 100644 index 0000000..ccc5520 --- /dev/null +++ b/flask/static/view.css @@ -0,0 +1,27 @@ +div.grid-container { + display: grid; + grid-template-columns: auto 20%; +} + +.archive-info { + display: grid; + grid-template-columns: max-content auto; + grid-template-rows: max-content max-content max-content max-content max-content max-content; +} + +.archive-info > * { + padding: 0.3em 0; +} + +.label { + display: inline-block; + padding: 0.1em; + border-radius: 0.4em; + background: linear-gradient(160deg, white, pink 65%); +} + +img#cover { + margin: auto auto; + border-radius: 1em; + width: 90%; +} \ No newline at end of file diff --git a/flask/templates/labels.html b/flask/templates/labels.html new file mode 100644 index 0000000..c686491 --- /dev/null +++ b/flask/templates/labels.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} + +{% block meta %} + +{% endblock %} + +{% block content %} +

{{archive[1]}}

+
+
+ {% for ltype in res_labels %} +
{{ltype}} + {% for for_temp in res_labels[ltype] %} + + {% endfor %} +
+ {% endfor %} +
+ +
+{% endblock %} \ No newline at end of file diff --git a/flask/templates/view.html b/flask/templates/view.html new file mode 100644 index 0000000..f837e1d --- /dev/null +++ b/flask/templates/view.html @@ -0,0 +1,25 @@ +{% extends "base.html" %} + +{% block meta %} + +{% endblock %} + +{% block content %} +

{{archive[1]}}

+
+
+ Hash: {{archive[2]}} + Owner: {{archive[-1]}} + Category: {{category[4]}}/{{category[1]}} + Imported: {{archive[4]|ctime}} + Size: {{archive[4]|spacer}} + Labels: {% if login and userdata[0] == archive[8] %}Edit{% endif %} +
+ {% for lab in labels %} +
{{lab[0]}}
+ {% endfor %} +
+
+ Cover image +
+{% endblock %} \ No newline at end of file