Compare commits

...

8 Commits

9 changed files with 606 additions and 180 deletions

1
.gitignore vendored
View File

@@ -3,4 +3,5 @@ app/files/
app/pwfile.json app/pwfile.json
app/dest app/dest
app.log app.log
init.log
app/__pycache__/ app/__pycache__/

Binary file not shown.

View File

@@ -1,10 +1,12 @@
import paramiko from os.path import isdir
from stat import S_ISDIR, S_ISREG from stat import S_ISDIR, S_ISREG
import re import re
import pathlib
# from base64 import decodebytes # from base64 import decodebytes
import json import json
import mariadb import mariadb
import logging
CATEGORIES = [ CATEGORIES = [
"Prüfungen", "Prüfungen",
@@ -16,7 +18,18 @@ CATEGORIES = [
"Multimedia", "Multimedia",
] ]
SUBCAT_CATEGORIES = ["Klausuren", "Übungen", "Labore"] SUBCAT_CATEGORIES = ["Klausuren", "Übungen", "Labore"]
unizeug_path = "/mnt/save/daten/Unizeug/" unizeug_path = "/home/wildarch/web/fet_unizeug/unizeug/"
log = logging.getLogger(__name__)
logging.basicConfig(
filename="init.log",
level=logging.INFO,
format="[%(asctime)s, %(filename)s:%(lineno)s -> %(funcName)10s() ]%(levelname)s: %(message)s",
)
debug = log.debug
info = log.info
error = log.error
db = mariadb.connect( db = mariadb.connect(
host="localhost", user="wildserver", password="DBPassword", database="Unizeug" host="localhost", user="wildserver", password="DBPassword", database="Unizeug"
) )
@@ -53,29 +66,31 @@ db.commit()
def get_dirstruct(): def get_dirstruct():
with open("app/pwfile.json", "r") as f: # with open("app/pwfile.json", "r") as f:
cred = json.load(f) # cred = json.load(f)
ssh = paramiko.SSHClient() # ssh = paramiko.SSHClient()
print(cred["sftpurl"]) # print(cred["sftpurl"])
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# key=paramiko.RSAKey(data=decodebytes(bytes(cred["key"],"utf-8"))) # key=paramiko.RSAKey(data=decodebytes(bytes(cred["key"],"utf-8")))
# ssh.get_host_keys().add(cred["sftpurl"], 'ssh-rsa', key) # ssh.get_host_keys().add(cred["sftpurl"], 'ssh-rsa', key)
ssh.connect(cred["sftpurl"], username=cred["sftpuser"], password=cred["sftpPW"]) # ssh.connect(cred["sftpurl"], username=cred["sftpuser"], password=cred["sftpPW"])
sftp = ssh.open_sftp() # sftp = ssh.open_sftp()
folders = sftp.listdir_attr(unizeug_path) # folders = sftp.listdir_attr(unizeug_path)
for entry in folders: folders=pathlib.Path(unizeug_path)
for entry in folders.iterdir():
if entry is None: if entry is None:
continue continue
if not S_ISDIR(entry.st_mode): if not entry.is_dir():
continue continue
fname = str(entry.filename) fname = str(entry.name)
regex = re.compile(r"Multimedia_only") regex = re.compile(r"Multimedia_only")
if regex.search(fname): if regex.search(fname):
continue continue
# print(fname) # print(fname)
lvid = re.search(r"[a-zA-Z0-9]{3}\.[a-zA-Z0-9]{3}", fname) lvid = re.search(r"[a-zA-Z0-9]{3}\.[a-zA-Z0-9]{3}", fname)
print(lvid) # print(lvid)
if lvid is None: if lvid is None:
error(f"Didnt Find LVA ID in Directory {fname}")
continue continue
lvid = lvid.group()[:3] + lvid.group()[4:] lvid = lvid.group()[:3] + lvid.group()[4:]
# name = fname[:-8] # name = fname[:-8]
@@ -89,39 +104,35 @@ def get_dirstruct():
cur.execute("SELECT id FROM LVAs WHERE lvid=?", (lvid,)) cur.execute("SELECT id FROM LVAs WHERE lvid=?", (lvid,))
lid = cur.fetchone()[0] lid = cur.fetchone()[0]
db.commit() db.commit()
for profsdir in sftp.listdir_attr(unizeug_path + fname + "/"): for profsdir in entry.iterdir():
if profsdir is None or not S_ISDIR(profsdir.st_mode): if profsdir is None:
continue
if not profsdir.is_dir():
continue continue
# print(profsdir.filename) # print(profsdir.filename)
try: try:
lastname, firstname = re.split(r"[_\-\s]", str(profsdir.filename)) lastname, firstname = re.split(r"[_\-\s]", str(profsdir.name))
pid = link_prof(firstname, lastname, lid) pid = link_prof(firstname, lastname, lid)
except ValueError: except ValueError:
print(f"{name} is broken") error(f"Couldnt get Profs from {fname}")
continue continue
for cat in sftp.listdir_attr( for cat in profsdir.iterdir():
unizeug_path + fname + "/" + profsdir.filename + "/" if cat is None:
):
if cat is None or not S_ISDIR(cat.st_mode):
continue continue
if cat.filename not in SUBCAT_CATEGORIES: if not cat.is_dir():
continue continue
idx = CATEGORIES.index(cat.filename) if cat.name not in SUBCAT_CATEGORIES:
for subcat in sftp.listdir_attr( continue
unizeug_path idx = CATEGORIES.index(cat.name)
+ fname for subcat in cat.iterdir():
+ "/" if subcat is None:
+ profsdir.filename continue
+ "/" if not subcat.is_dir():
+ cat.filename
+ "/"
):
if subcat is None or not S_ISDIR(subcat.st_mode):
continue continue
cur = db.cursor() cur = db.cursor()
cur.execute( cur.execute(
"INSERT INTO SubCats (LId,PId,cat,name) VALUES(?,?,?,?)", "INSERT INTO SubCats (LId,PId,cat,name) VALUES(?,?,?,?)",
(lid, pid, idx, subcat.filename), (lid, pid, idx, subcat.name),
) )
db.commit() db.commit()

152
app/init_ssh.py Normal file
View File

@@ -0,0 +1,152 @@
import paramiko
from stat import S_ISDIR, S_ISREG
import re
# from base64 import decodebytes
import json
import mariadb
CATEGORIES = [
"Prüfungen",
"Klausuren",
"Übungen",
"Labore",
"Unterlagen",
"Zusammenfassungen",
"Multimedia",
]
SUBCAT_CATEGORIES = ["Klausuren", "Übungen", "Labore"]
unizeug_path = "/mnt/save/daten/Unizeug/"
db = mariadb.connect(
host="localhost", user="wildserver", password="DBPassword", database="Unizeug"
)
c = db.cursor()
try:
c.execute("DROP TABLE LVAs")
except mariadb.OperationalError:
pass
c.execute(
"CREATE TABLE LVAs(id BIGINT(20) unsigned NOT NULL AUTO_INCREMENT,lvid VARCHAR(6), lvname VARCHAR(256), lvpath VARCHAR(256),PRIMARY KEY(id))"
)
try:
c.execute("DROP TABLE Profs")
except mariadb.OperationalError:
pass
c.execute(
"CREATE TABLE Profs(id BIGINT(20) unsigned NOT NULL AUTO_INCREMENT,name VARCHAR(256),PRIMARY KEY(id))"
)
try:
c.execute("DROP TABLE LPLink")
except mariadb.OperationalError:
pass
c.execute(
"CREATE TABLE LPLink(id BIGINT(20) unsigned NOT NULL AUTO_INCREMENT,LId bigint(20),PId bigint(20),PRIMARY KEY(id))"
)
try:
c.execute("DROP TABLE SubCats")
except mariadb.OperationalError:
pass
c.execute(
"CREATE TABLE SubCats(id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,LId BIGINT(20),PId BIGINT(20),cat TINYINT UNSIGNED,name VARCHAR(256), PRIMARY KEY(id))"
)
db.commit()
def get_dirstruct():
with open("app/pwfile.json", "r") as f:
cred = json.load(f)
ssh = paramiko.SSHClient()
print(cred["sftpurl"])
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# key=paramiko.RSAKey(data=decodebytes(bytes(cred["key"],"utf-8")))
# ssh.get_host_keys().add(cred["sftpurl"], 'ssh-rsa', key)
ssh.connect(cred["sftpurl"], username=cred["sftpuser"], password=cred["sftpPW"])
sftp = ssh.open_sftp()
folders = sftp.listdir_attr(unizeug_path)
for entry in folders:
if entry is None:
continue
if not S_ISDIR(entry.st_mode):
continue
fname = str(entry.filename)
regex = re.compile(r"Multimedia_only")
if regex.search(fname):
continue
# print(fname)
lvid = re.search(r"[a-zA-Z0-9]{3}\.[a-zA-Z0-9]{3}", fname)
print(lvid)
if lvid is None:
continue
lvid = lvid.group()[:3] + lvid.group()[4:]
# name = fname[:-8]
name = re.sub(r"[a-zA-Z0-9]{3}\.[a-zA-Z0-9]{3}", "", fname)
# print(name)
# print(lvid)
cur = db.cursor()
cur.execute(
"INSERT INTO LVAs (lvid, lvname, lvpath) VALUES(?,?,?)", (lvid, name, fname)
)
cur.execute("SELECT id FROM LVAs WHERE lvid=?", (lvid,))
lid = cur.fetchone()[0]
db.commit()
for profsdir in sftp.listdir_attr(unizeug_path + fname + "/"):
if profsdir is None or not S_ISDIR(profsdir.st_mode):
continue
# print(profsdir.filename)
try:
lastname, firstname = re.split(r"[_\-\s]", str(profsdir.filename))
pid = link_prof(firstname, lastname, lid)
except ValueError:
print(f"{name} is broken")
continue
for cat in sftp.listdir_attr(
unizeug_path + fname + "/" + profsdir.filename + "/"
):
if cat is None or not S_ISDIR(cat.st_mode):
continue
if cat.filename not in SUBCAT_CATEGORIES:
continue
idx = CATEGORIES.index(cat.filename)
for subcat in sftp.listdir_attr(
unizeug_path
+ fname
+ "/"
+ profsdir.filename
+ "/"
+ cat.filename
+ "/"
):
if subcat is None or not S_ISDIR(subcat.st_mode):
continue
cur = db.cursor()
cur.execute(
"INSERT INTO SubCats (LId,PId,cat,name) VALUES(?,?,?,?)",
(lid, pid, idx, subcat.filename),
)
db.commit()
def link_prof(firstname, lastname, lid):
cur = db.cursor()
cur.execute("SELECT id from Profs WHERE name=?", (lastname + " " + firstname,))
res = cur.fetchone()
if res is not None:
cur.execute("INSERT INTO LPLink (LId,PId) VALUES(?,?)", (lid, res[0]))
db.commit()
return res[0]
cur.execute("SELECT id from Profs WHERE name=?", (firstname + " " + lastname,))
res = cur.fetchone()
if res is not None:
cur.execute("INSERT INTO LPLink (LId,PId) VALUES(?,?)", (lid, res[0]))
db.commit()
return res[0]
cur.execute("INSERT INTO Profs (name) VALUES(?)", (lastname + " " + firstname,))
cur.execute("SELECT id FROM Profs WHERE name=?", (lastname + " " + firstname,))
res = cur.fetchone()
cur.execute("INSERT INTO LPLink (LId,PId) VALUES(?,?)", (lid, res[0]))
db.commit()
return res[0]
if __name__ == "__main__":
get_dirstruct()

View File

@@ -1,16 +1,21 @@
from typing import Annotated from typing import Annotated
from typing import List, Dict, Tuple, Sequence from typing import List, Dict, Tuple, Sequence
from starlette.responses import StreamingResponse
from annotated_types import IsDigit from annotated_types import IsDigit
from fastapi import FastAPI, File, HTTPException, UploadFile, Request, Form from fastapi import FastAPI, File, HTTPException, UploadFile, Request, Form
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
# import multiprocessing # import multiprocessing
# import threading # import threading
# import concurrent.futures # import concurrent.futures
# import asyncio import asyncio
# import fastapi # import fastapi
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
import pymupdf import pymupdf
# import fitz as pymupdf
import json import json
import re import re
@@ -24,6 +29,7 @@ import datetime
import logging import logging
import inspect import inspect
import pathlib
from starlette.types import HTTPExceptionHandler from starlette.types import HTTPExceptionHandler
@@ -76,6 +82,9 @@ GREETINGFILE = "./app/graphics/greeting.pdf"
# for l in cur: # for l in cur:
# print(l) # print(l)
# locpaths = ["./VO_Mathematik_3.pdf"] # replace this with a database # locpaths = ["./VO_Mathematik_3.pdf"] # replace this with a database
censor_status_update_events: Dict[str, asyncio.Event] = {}
censor_status_datas: Dict[str, Dict[str, int | None | str | bool]] = {}
# censor_finished_flags: Dict[str, asyncio.Event] = {}
def _sql_quarry( def _sql_quarry(
@@ -405,14 +414,14 @@ async def get_submission(
pagescales: Annotated[ pagescales: Annotated[
str, Form() str, Form()
], # Scales of Pages # Annotated[List[Dict[str, float]], Form()], ], # Scales of Pages # Annotated[List[Dict[str, float]], Form()],
censor: Annotated[str, Form()], ocr: Annotated[str, Form()],
): ):
"""handles submission""" """handles submission"""
print( print(
f"lva: {lva}, prof: {prof}, fname {fname}, stype: {stype}, subcat: {subcat}, sem: {sem}, ex_date: {ex_date}, rects: {rects}, pagescales: {pagescales}, censor: {censor}" f"lva: {lva}, prof: {prof}, fname {fname}, stype: {stype}, subcat: {subcat}, sem: {sem}, ex_date: {ex_date}, rects: {rects}, pagescales: {pagescales}, ocr: {ocr}"
) )
info( info(
f"lva: {lva}, prof: {prof}, fname {fname}, stype: {stype}, subcat: {subcat}, sem: {sem}, ex_date: {ex_date}, rects: {rects}, pagescales: {pagescales}, censor: {censor}" f"lva: {lva}, prof: {prof}, fname {fname}, stype: {stype}, subcat: {subcat}, sem: {sem}, ex_date: {ex_date}, rects: {rects}, pagescales: {pagescales}, ocr: {ocr}"
) )
rects_p = json.loads(rects) rects_p = json.loads(rects)
scales_p = json.loads(pagescales) scales_p = json.loads(pagescales)
@@ -441,25 +450,123 @@ async def get_submission(
except ValueError as e: except ValueError as e:
error(f"Error creating savepath: {e}") error(f"Error creating savepath: {e}")
raise HTTPException(status_code=400, detail=f"Error creation savepath: {e}") raise HTTPException(status_code=400, detail=f"Error creation savepath: {e}")
await censor_pdf( # censor_finished_flags[fileId] = asyncio.Event()
filepath, dest, rects_p, scales_p, False if censor == "False" else True censor_status_datas[fileId] = {}
) if fileId not in censor_status_update_events:
censor_status_update_events[fileId] = asyncio.Event()
if ocr == "True":
await asyncio.to_thread(
censor_pdf_ocr,
filepath,
dest,
rects_p,
scales_p,
fileId,
)
else:
await asyncio.to_thread(
censor_pdf,
filepath,
dest,
rects_p,
scales_p,
fileId,
)
# return {"done": "ok"} # return {"done": "ok"}
# print(dest) # print(dest)
# await censor_finished_flags[fileId].wait()
# censor_finished_flags[fileId].clear()
info(f"Saved file {fileId} as {dest}") info(f"Saved file {fileId} as {dest}")
delete_from_FIP(fileId) delete_from_FIP(fileId)
return FileResponse(dest, content_disposition_type="inline") return FileResponse(dest, content_disposition_type="inline")
async def censor_pdf( @app.get("/get_censor_status/{file_id}")
async def get_censor_status(file_id: str):
"""Yields the currrent page being censored and the total number of pages"""
if len(sql("Select filename from FIP where id=?", (file_id,))) < 1:
raise HTTPException(
400,
detail="You are trying to get a status updater for a file that dosent exist.",
)
if file_id not in censor_status_update_events:
censor_status_update_events[file_id] = asyncio.Event()
return StreamingResponse(
yield_censor_status(file_id), media_type="text/event-stream"
)
async def yield_censor_status(file_id: str):
"""Internal function to yield updates to the stream"""
while True:
await censor_status_update_events[file_id].wait()
censor_status_update_events[file_id].clear()
yield f"event: censorUpdate\ndata: {json.dumps(censor_status_datas[file_id])}\n\n"
if censor_status_datas[file_id]["done"]:
del censor_status_update_events[file_id]
del censor_status_datas[file_id]
return
def censor_pdf(
path: str, path: str,
destpath: str, destpath: str,
rects: List[List[List[float]]], rects: List[List[List[float]]],
scales: List[Dict[str, float]], scales: List[Dict[str, float]],
secure: bool, file_id: str,
):
"""Censors pdf and saves the file to the given Destpath.
Args:
path: path to the pdf document
destpath: Path where the result is supposed to be saved to
rects: Coordinates of rectangles to be placed on the pdf document
scales: Scales of the rects coordinates for the pdf document
secure: weather or not the pdf document is supposed to be converted into an Image (and back) to make shure, the censoring is irreversible
Returns:
None
"""
doc = pymupdf.open(path)
page = doc[0]
npage = doc.page_count
for i in range(npage):
page = doc[i]
if i < len(rects) and rects[i] != []:
print(i)
wfac = page.rect.width / scales[i]["width"]
hfac = page.rect.height / scales[i]["height"]
for rect in rects[i]:
prect = pymupdf.Rect(
rect[0] * wfac,
rect[1] * hfac,
(rect[0] + rect[2]) * wfac,
(rect[1] + rect[3]) * hfac,
)
page.add_redact_annot(
prect,
fill=(0, 0, 0),
)
page.apply_redactions()
censor_status_datas[file_id]["page"] = i + 1
censor_status_datas[file_id]["pages"] = npage
censor_status_datas[file_id]["done"] = False
censor_status_update_events[file_id].set()
doc.set_metadata({})
doc.save(destpath, garbage=4, deflate=True, clean=True)
censor_status_datas[file_id]["done"] = True
censor_status_update_events[file_id].set()
def censor_pdf_ocr(
path: str,
destpath: str,
rects: List[List[List[float]]],
scales: List[Dict[str, float]],
file_id: str,
): ):
"""Censors pdf and runs OCR """Censors pdf and runs OCR
If Secure is True the file is converted to Pixels and then recreated; else the censored sections are just covering the text below and can be easiliy removed with e.g. Inkscape The file is converted to Pixels and then recreated.
Saves the file to the given Destpath.
Args: Args:
path: path to the pdf document path: path to the pdf document
destpath: Path where the result is supposed to be saved to destpath: Path where the result is supposed to be saved to
@@ -472,12 +579,7 @@ async def censor_pdf(
doc = pymupdf.open(path) doc = pymupdf.open(path)
output = pymupdf.open() output = pymupdf.open()
page = doc[0] page = doc[0]
# width = page.rect.width
# height = page.rect.height
# print(width, height)
npage = doc.page_count npage = doc.page_count
# pages = []
# tasks = []
for i in range(npage): for i in range(npage):
page = doc[i] page = doc[i]
if i < len(rects) and rects[i] != []: if i < len(rects) and rects[i] != []:
@@ -496,38 +598,22 @@ async def censor_pdf(
color=(0, 0, 0), color=(0, 0, 0),
fill=(0, 0, 0), fill=(0, 0, 0),
) )
if secure: censor_status_datas[file_id]["page"] = i + 1
# pages.append(page) censor_status_datas[file_id]["pages"] = npage
bitmap = page.get_pixmap(dpi=400) censor_status_datas[file_id]["done"] = False
pdf_bytes = bitmap.pdfocr_tobytes( censor_status_update_events[file_id].set()
language="deu", # THis Costs us dearly
tessdata="/usr/share/tessdata/", # tesseract needs to be installed; this is the path to thetesseract files bitmap = page.get_pixmap(dpi=400)
) pdf_bytes = bitmap.pdfocr_tobytes(
output.insert_pdf(pymupdf.Document(stream=pdf_bytes)) language="deu",
# tasks.append(asyncio.create_task(censor_page(page))) tessdata="/usr/share/tessdata/", # tesseract needs to be installed; this is the path to thetesseract files
print(f"Page {i + 1}/{npage}: CENSORING DONE") )
else: output.insert_pdf(pymupdf.Document(stream=pdf_bytes))
output.insert_pdf(doc, i, i) # End of the costly part
print(f"Page {i + 1}/{npage}: CENSORING DONE")
# if secure:
# pages_bytes: List[bytes] = []
# censor_page(pages[0])
# with multiprocessing.Pool(npage) as p:
# pages_bytes = p.map(censor_page, pages)
# pages_bytes = p.map(test_function, [1, 2, 3, 4])
# for pdf_bytes in pages_bytes:
# output.insert_pdf(pymupdf.Document(stream=pdf_bytes))
# with concurrent.futures.ThreadPoolExecutor() as executor:
# futures = []
# for page in pages:
# futures.append(executor.submit(censor_page, page))
# for future in futures:
# output.insert_pdf(pymupdf.Document(stream=future.result()))
#
# for task in tasks:
# output.insert_pdf(pymupdf.Document(stream=await task))
# print("CENSORING DONE")
output.save(destpath) output.save(destpath)
censor_status_datas[file_id]["done"] = True
censor_status_update_events[file_id].set()
def test_function(i: int) -> bytes: def test_function(i: int) -> bytes:
@@ -607,9 +693,18 @@ def make_savepath(
400, 400,
"You have not specified a date for an upload that requires a date like an exam.", "You have not specified a date for an upload that requires a date like an exam.",
) )
filename += yyyy + "_" + mm + "_" + dd + "_" filename = yyyy + "_" + mm + "_" + dd + "_"
filename += fname + "." + ftype filename += fname
return savepath + filename file = filename + "." + ftype
destpath = pathlib.Path(savepath + file)
i = 0
while destpath.is_file():
file = filename + f"_{i}." + ftype
i += 1
destpath = pathlib.Path(savepath + file)
destpath.touch()
return savepath + file
def get_lvpath(lva: str) -> Tuple[int, str]: def get_lvpath(lva: str) -> Tuple[int, str]:

View File

@@ -1,103 +1,164 @@
<!doctype html> <!doctype html>
<html lang="de"> <html lang="de">
<head>
<title>Unizeug uploader</title>
<link rel="stylesheet" href="static/style.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.4.456/pdf.min.js"></script>
<script src="static/app.js" defer></script>
<script src="static/autocomplete.js" defer></script>
<script src="static/dynhide.js" defer></script>
<script src="static/filedrop.js" defer></script>
<link
rel="icon"
type="image/png"
href="/favicon/favicon-96x96.png"
sizes="96x96"
/>
<link rel="icon" type="image/svg+xml" href="/favicon/favicon.svg" />
<link rel="shortcut icon" href="/favicon/favicon.ico" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="/favicon/apple-touch-icon.png"
/>
<meta name="apple-mobile-web-app-title" content="Unizeug" />
<link rel="manifest" href="/favicon/site.webmanifest" />
</head>
<head> <body>
<title>Unizeug uploader</title> <!-- The Modal -->
<link rel="stylesheet" href="static/style.css" /> <div id="loading" class="modal">
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.4.456/pdf.min.js"></script> <!-- Modal content -->
<script src="static/app.js" defer></script> <div class="loading-content">
<script src="static/autocomplete.js" defer></script> <!-- <span class="close">&times;</span> -->
<script src="static/dynhide.js" defer></script> <div class="loader"></div>
<script src="static/filedrop.js" defer></script> <p id="upload_status" class="upload_status_text">Uploading</p>
<link rel="icon" type="image/png" href="/favicon/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/favicon/favicon.svg" />
<link rel="shortcut icon" href="/favicon/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png" />
<meta name="apple-mobile-web-app-title" content="Unizeug" />
<link rel="manifest" href="/favicon/site.webmanifest" />
</head>
<body>
<div class="main">
<div class="left" id="controldiv">
<div id="fileupload">
<form id="uploadform" enctype="multipart/form-data">
<div class="filetop">
<!-- <label for="filepicker">Choose a file</label> -->
<input type="file" name="files" id="filepicker" multiple placeholder="Drop File" />
</div>
<button type="submit" id="upload" method="POST" class="fileupload">
Upload
</button>
</form>
</div> </div>
<div id="submitdiv"> </div>
<form id="submitform" ,onsubmit="submitFile(event)"> <!-- main -->
<label for="lva">Lehrveranstaltung:</label> <div class="main">
<div class="autocomplete"> <div class="left" id="controldiv">
<input type="text" id="lva" name="lva" placeholder="Lehrveranstaltung" autocomplete="off" /> <div id="fileupload">
</div> <form id="uploadform" enctype="multipart/form-data">
<br /> <div class="filetop">
<!-- <br /> --> <!-- <label for="filepicker">Choose a file</label> -->
<label for="prof">Vortragende*r:</label> <input
<div class="autocomplete"> type="file"
<input type="text" id="prof" name="prof" placeholder="Vortragende*r" autocomplete="off" /> name="files"
</div> id="filepicker"
<br /> multiple
<!-- <br /> --> placeholder="Drop File"
<label for="name">Name:</label> />
<input type="text" id="name" name="fname" placeholder="Prüfung" /><br />
<label for="sem">Semester:</label>
<input type="text" id="sem" name="sem" placeholder="2024W" /><br />
<input type="radio" id="pruefung" name="stype" value="0" checked="checked" />
<label for="pruefung">Prüfung</label><br />
<input type="radio" id="klausur" name="stype" value="1" />
<label for="klausur">Klausur</label><br />
<input type="radio" id="uebung" name="stype" value="2" />
<label for="uebung">Übung</label><br />
<input type="radio" id="labor" name="stype" value="3" />
<label for="labor">Labor</label><br />
<input type="radio" id="unterlagen" name="stype" value="4" />
<label for="unterlagen">Unterlagen</label><br />
<input type="radio" id="zusammenfassungen" name="stype" value="5" />
<label for="zusammenfassungen">Zusammenfassung</label><br />
<input type="radio" id="multimedia" name="stype" value="6" />
<label for="multimedia">Multimedia</label><br />
<br />
<div id="subcatdiv">
<label for="subcat">Veranstaltung</label>
<div class="autocomplete">
<input type="text" id="subcat" name="subcat" placeholder="Klausur 1" autocomplete="off" />
</div> </div>
</div> <button type="submit" id="upload" method="POST" class="fileupload">
<div id="datediv"> Upload
<label for="date">Datum</label> </button>
<input type="date" id="date" name="ex_date" placeholder="Drop File" /><br /> </form>
</div> </div>
<input type="checkbox" name="censor" id="sec_censor" value="True" checked /><label <div id="submitdiv">
for="sec_censor">Zensieren</label><br /><br /> <form id="submitform" ,onsubmit="submitFile(event)">
<button type="submit" id="send">Senden</button> <label for="lva">Lehrveranstaltung:</label>
</form> <div class="autocomplete">
</div> <input
</div> type="text"
<div class="right" id="rightdiv"> id="lva"
<div class="buttons" id="buttonsdiv"> name="lva"
<button id="prev">Prev</button><button id="next">Next</button> placeholder="Lehrveranstaltung"
<div> autocomplete="off"
<span id="npage"></span> />
<span>/</span> </div>
<span id="npages"></span> <br />
<!-- <br /> -->
<label for="prof">Vortragende*r:</label>
<div class="autocomplete">
<input
type="text"
id="prof"
name="prof"
placeholder="Vortragende*r"
autocomplete="off"
/>
</div>
<br />
<!-- <br /> -->
<label for="name">Name:</label>
<input
type="text"
id="name"
name="fname"
placeholder="Prüfung"
/><br />
<label for="sem">Semester:</label>
<input type="text" id="sem" name="sem" placeholder="2024W" /><br />
<input
type="radio"
id="pruefung"
name="stype"
value="0"
checked="checked"
/>
<label for="pruefung">Prüfung</label><br />
<input type="radio" id="klausur" name="stype" value="1" />
<label for="klausur">Klausur</label><br />
<input type="radio" id="uebung" name="stype" value="2" />
<label for="uebung">Übung</label><br />
<input type="radio" id="labor" name="stype" value="3" />
<label for="labor">Labor</label><br />
<input type="radio" id="unterlagen" name="stype" value="4" />
<label for="unterlagen">Unterlagen</label><br />
<input type="radio" id="zusammenfassungen" name="stype" value="5" />
<label for="zusammenfassungen">Zusammenfassung</label><br />
<input type="radio" id="multimedia" name="stype" value="6" />
<label for="multimedia">Multimedia</label><br />
<br />
<div id="subcatdiv">
<label for="subcat">Veranstaltung</label>
<div class="autocomplete">
<input
type="text"
id="subcat"
name="subcat"
placeholder="Klausur 1"
autocomplete="off"
/>
</div>
</div>
<div id="datediv">
<label for="date">Datum</label>
<input
type="date"
id="date"
name="ex_date"
placeholder="Drop File"
/><br />
</div>
<input
type="checkbox"
name="ocr"
id="sec_censor"
value="True"
/><label for="sec_censor">OCR</label><br /><br />
<button type="submit" id="send">Senden</button>
</form>
</div> </div>
<button id="clr">Clear Page</button><button id="ca">Claer All</button>
</div> </div>
<div id="cnvdiv"> <div class="right" id="rightdiv">
<div class="stack" id="cnvcont"> <div class="buttons" id="buttonsdiv">
<canvas id="cnv"></canvas> <button id="prev">Prev</button><button id="next">Next</button>
<canvas id="drw_cnv"></canvas> <div>
<span id="npage"></span>
<span>/</span>
<span id="npages"></span>
</div>
<button id="clr">Clear Page</button><button id="ca">Claer All</button>
</div>
<div id="cnvdiv">
<div class="stack" id="cnvcont">
<canvas id="cnv"></canvas>
<canvas id="drw_cnv"></canvas>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </body>
</body>
</html> </html>

View File

@@ -188,6 +188,9 @@ class PDFDocument {
} }
} }
var mouseIsDown = false; var mouseIsDown = false;
var modal;
var close_loading;
var upload_status;
//var startX = 0; //var startX = 0;
//var startY = 0; //var startY = 0;
//var pdf; //var pdf;
@@ -273,18 +276,31 @@ function submitPdf(eve) {
formdata.append("fileId", doc.fID); formdata.append("fileId", doc.fID);
//formdata.append("filename", doc.filename); //formdata.append("filename", doc.filename);
formdata.append("ftype", doc.filetype); formdata.append("ftype", doc.filetype);
if (!formdata.has("censor")) { if (!formdata.has("ocr")) {
formdata.append("censor", "False"); formdata.append("ocr", "False");
} }
console.log(formdata); console.log(formdata);
submitForm(formdata); submitForm(formdata);
} }
async function submitForm(formData) { async function submitForm(formData) {
try { try {
const updateEventSource = new EventSource(
"http://127.0.0.1:8000/get_censor_status/" + doc.fID,
);
modal.style.display = "flex";
// console.log("http://127.0.0.1:8000/get_censor_status/" + doc.fID);
updateEventSource.addEventListener("censorUpdate", function(eve) {
console.log(eve.data);
var data = JSON.parse(eve.data);
upload_status.innerText =
"Censoring Page " + data.page + "/" + data.pages;
});
const response = await fetch("http://127.0.0.1:8000/submit", { const response = await fetch("http://127.0.0.1:8000/submit", {
method: "POST", method: "POST",
body: formData, body: formData,
}); });
updateEventSource.close();
modal.style.display = "none";
//let responseJSON=await response.json(); //let responseJSON=await response.json();
if (response.ok) { if (response.ok) {
console.log("Submit OK"); console.log("Submit OK");
@@ -366,6 +382,14 @@ function initListeners() {
doc.clearAll(); doc.clearAll();
}); });
} }
function initLoading() {
modal = document.querySelector("#loading");
// close_loading = document.querySelector(".close");
upload_status = document.querySelector("#upload_status");
// close_loading.addEventListener("click", function() {
// modal.style.display = "none";
// });
}
const startPdf = () => { const startPdf = () => {
// doc = new PDFDocument( // doc = new PDFDocument(
// "./files/b78c869f-e0bb-11ef-9b58-84144d05d665", // "./files/b78c869f-e0bb-11ef-9b58-84144d05d665",
@@ -374,6 +398,7 @@ const startPdf = () => {
// ); // );
//pdf = new PDFView("./VO_Mathematik_3.pdf"); //pdf = new PDFView("./VO_Mathematik_3.pdf");
doc = new PDFDocument("./files/greeting", "greeting", "pdf"); doc = new PDFDocument("./files/greeting", "greeting", "pdf");
initLoading();
initDraw(); initDraw();
initUpload(); initUpload();
initListeners(); initListeners();

View File

@@ -21,7 +21,7 @@ function autocomplete(inp, type) {
i, i,
apirq, apirq,
iname, iname,
val = this.value; val = inp.value;
/*close any already open lists of autocompleted values*/ /*close any already open lists of autocompleted values*/
closeAllLists(); closeAllLists();
if (!val && type === "lva" && pid === null) { if (!val && type === "lva" && pid === null) {
@@ -56,7 +56,7 @@ function autocomplete(inp, type) {
a.setAttribute("id", this.id + "autocomplete-list"); a.setAttribute("id", this.id + "autocomplete-list");
a.setAttribute("class", "autocomplete-items"); a.setAttribute("class", "autocomplete-items");
/*append the DIV element as a child of the autocomplete container:*/ /*append the DIV element as a child of the autocomplete container:*/
this.parentNode.appendChild(a); inp.parentNode.appendChild(a);
/*for each item in the array...*/ /*for each item in the array...*/
//await response; //await response;
if (response.ok) { if (response.ok) {

View File

@@ -238,3 +238,84 @@ input[type="file"]::file-selector-button {
width: 100%; width: 100%;
/* background-color: purple; */ /* background-color: purple; */
} }
/* The Modal (background) */
.modal {
display: none;
/* Hidden by default */
position: fixed;
/* Stay in place */
z-index: 1;
/* Sit on top */
left: 0;
top: 0;
width: 100%;
/* Full width */
height: 100%;
/* Full height */
overflow: auto;
/* Enable scroll if needed */
background-color: #4f5977;
/* Fallback color */
background-color: rgba(0, 0, 0, 0.4);
/* Black w/ opacity */
justify-content: center;
}
/* Modal Content/Box */
.loading-content {
background-color: #4f5977;
margin: auto;
/* 15% from the top and centered */
padding: 20px;
/* border: 1px solid #888; */
/* width: 80%; */
border-radius: 15px;
display: flex;
flex-direction: column;
/* Could be more or less, depending on screen size */
align-items: center;
text-align: center;
}
/* The Close Button */
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
.upload_status_text {
color: #ffffff;
font-size: 16pt;
}
.loader {
margin: auto;
border: 16px solid #f3f3f3;
/* Light grey */
border-top: 16px solid #3498db;
/* Blue */
border-radius: 50%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}