created dynamic frontend

This commit is contained in:
Niklas Müller 2024-01-26 16:24:53 +01:00
parent 93b7f68b54
commit 2535006f02
5 changed files with 446 additions and 5 deletions

89
app.py
View File

@ -1,5 +1,7 @@
import shutil
import os
import glob
import traceback
from flask import Flask, render_template, request, redirect, jsonify, send_from_directory
from pdf_util.pdf_project_manager import pdf_project_manager
@ -21,7 +23,6 @@ logging.basicConfig(
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'uploads'
@ -30,6 +31,11 @@ def index():
return render_template('index.html')
@app.route('/app/')
def pdf_app():
return render_template('app.html')
@app.route('/split/<path:path>')
def send_report(path):
return send_from_directory('split', path)
@ -40,6 +46,47 @@ def send_merge(path):
return send_from_directory('merge', path)
@app.route('/projects/<path:path>')
def get_project(path):
return send_from_directory('projects', path)
@app.route('/get_single_pages_archive/<uuid>/')
def get_single_pages_archive(uuid):
try:
shutil.make_archive('pdf_splitted', 'zip', "/app/projects/" + uuid + '/splitted')
os.rename('/app/pdf_splitted.zip', '/app/projects/' + uuid + '/pdf_splitted.zip')
response = jsonify({'status': 200, 'url': '/projects/' + uuid + '/pdf_splitted.zip'})
except Exception as e:
logging.debug("There was an error: " + str(e))
logging.debug("Stacktrace: " + str(traceback.format_exc()))
response = jsonify({"status": 500, "error_message": e})
return response
@app.route('/get_single_pages_info/<uuid>/')
def get_single_pages_info(uuid):
try:
pages = []
page_list = glob.glob("/app/projects/" + uuid + "/splitted/*.pdf")
logging.debug("page_list: ")
logging.debug(page_list)
page_list.sort()
logging.debug("sorted_page_list: ")
logging.debug(page_list)
for file in page_list:
pages.append(file[4:]) # Cut of /app
response = jsonify({'status': 200, 'pages': pages})
except Exception as e:
logging.debug("There was an error: " + str(e))
logging.debug("Stacktrace: " + str(traceback.format_exc()))
response = jsonify({"status": 500, "error_message": e})
return response
@app.route('/split_to_zip', methods=['POST'])
def split_to_zip():
if 'pdf_1' not in request.files:
@ -106,6 +153,46 @@ def merge_to_pdf():
return response
@app.route('/init_project/', methods=['GET'])
def init_project():
try:
pdf_project = pdf_project_manager()
response = jsonify({"status": 200, "project_uuid": pdf_project.uuid})
except Exception as e:
logging.debug("There was an error: " + str(e))
logging.debug("Stacktrace: " + str(traceback.format_exc()))
response = jsonify({"status": 500, "project_uuid": ''})
return response
@app.route('/add_pdf_to_project/', methods=['POST'])
def add_pdf_to_project():
try:
if 'pdf' not in request.files:
logging.debug(request)
return redirect(request.url)
else:
pdf_file = request.files['pdf']
filename_1 = os.path.join(os.path.dirname(os.path.realpath(__file__)), app.config['UPLOAD_FOLDER'], pdf_file.filename)
pdf_file.save(filename_1)
pdf_file = request.files['pdf']
uuid = request.form['uuid']
logging.debug(pdf_file)
logging.debug(uuid)
pdf_project = pdf_project_manager(uuid4=uuid)
pdf_project.add_pdf(filename_1)
response = jsonify({"status": 200, "message": 'PDF added'})
except Exception as e:
logging.debug("There was an error: " + str(e))
logging.debug("Stacktrace: " + str(traceback.format_exc()))
response = jsonify({"status": 500, "error_message": e})
return response
if __name__ == '__main__':
app.run(debug=True)

141
static/css/styles.css Normal file
View File

@ -0,0 +1,141 @@
div {
/* border: 2px solid yellowgreen; */
}
#app {
overflow: hidden;
display: block;
text-align: center;
}
#div_content {
padding-left: 2em;
padding-right: 2em;
max-width: 1200px;
margin: 0 auto;
}
#div_website_title {
margin-top: 2em;
}
#div_project_title {
/* border: 2px solid darkorchid; */
float: left;
}
#div_dropzone {
margin-top: 0.5em;
margin-bottom: 1em;
}
.div_float_clear {
clear: both;
}
h1 {
font-size: 26pt;
color: greenyellow;
}
.button_export {
width: 50%;
margin-left: 0%;
margin-right: 0%;
margin-top: 1em;
margin-bottom: 2em;
border-width: 0px;
padding: 1em;
background-image: linear-gradient(to right, #d1d1d1, #dddddd);
}
.button_export:hover {
background-image: linear-gradient(to right, #c1c1c1, #cccccc);
}
#button_download_individual_pages {
float: right;
}
#button_download_complete_pdf {
float: left;
}
#pdf-dropzone {
min-height: 50px;
max-height: 150px;
height: 150px;
border: 1px solid black;
padding-top:60px
}
#pdf-dropzone:hover {
cursor: pointer;
}
#preview-template {
border: 2px solid yellowgreen;
visibility: hidden;
}
/*
#page_overview {
border: 2px solid yellowgreen;
}
*/
#control-div {
width: 40%;
float: left;
/* padding: 1em; */
overflow: hidden; /* Makes the container actually "contain" the floated divs */
display: block;
}
#history-div {
width: 60%;
float: left;
padding: 1em;
}
#comment-label, #comment-input {
width: 100%;
}
/* The snackbar - position it at the bottom and in the middle of the screen */
#snackbar {
visibility: hidden; /* Hidden by default. Visible on click */
min-width: 800px; /* Set a default minimum width */
margin-left: -400px; /* Divide value of min-width by 2 */
background-color: #333; /* Black background color */
color: #fff; /* White text color */
text-align: center; /* Centered text */
border-radius: 2px; /* Rounded borders */
padding: 16px; /* Padding */
position: fixed; /* Sit on top of the screen */
z-index: 1; /* Add a z-index if needed */
left: 50%; /* Center the snackbar */
bottom: 30px; /* 30px from the bottom */
}
/* Show the snackbar when clicking on a button (class added with JavaScript) */
#snackbar.show {
visibility: visible; /* Show the snackbar */
/* Add animation: Take 0.5 seconds to fade in and out the snackbar.
However, delay the fade out process for 2.5 seconds */
/*
-webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
animation: fadein 0.5s, fadeout 0.5s 2.5s;
*/
}

View File

@ -0,0 +1,47 @@
.form__group {
position: relative;
padding: 15px 0 0;
margin-top: 10px;
width: 100%;
}
.form__field {
font-family: inherit;
width: 100%;
border: 0;
border-bottom: 2px solid #9b9b9b;
outline: 0;
font-size: 1em;
color: #333;
padding: 7px 0;
background: transparent;
transition: border-color 0.2s;
}
.form__field::placeholder {
color: #666666;
}
.form__field:focus::placeholder {
color: #666666;
}
.form__field:focus {
padding-bottom: 6px;
border-width: 3px;
border-image: linear-gradient(to right, #477493, #0f719b);
border-image-slice: 1;
}
.form__field_bad_input {
padding-bottom: 6px;
font-weight: 700;
border-width: 3px;
border-image: linear-gradient(to right, #f91d1d, #9f4a4a);
border-image-slice: 1;
}
/* reset input */
.form__field:required, .form__field:invalid {
box-shadow: none;
}

164
templates/app.html Normal file
View File

@ -0,0 +1,164 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="/static/icons/filetype-pdf.svg" type="image/x-icon">
<link rel="stylesheet" href="/static/css/styles.css">
<link rel="stylesheet" href="/static/css/styles_forms.css">
<!-- Dropzone -->
<script src="https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/dropzone@5/dist/min/dropzone.min.css" type="text/css" />
<!-- Bootstrap -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
<title>PDF Web Toolkit</title>
</head>
<body>
<div id="app">
<div id="div_content">
<div id="div_website_title">
<h1>PDF Web Toolkit</h1>
</div>
<div id="div_project_title">
<div class="form__group field">
<input id="project_title" class="form__field" type="text" @change="handleProjectTitleChange($event, 'project_title')" placeholder="Project Title" onfocus="document.getElementById('project_title').classList.remove('form__field_bad_input');"><br>
</div>
</div>
<div class="div_float_clear"></div>
<div id="div_dropzone">
<div class="my-dropzone" id="pdf-dropzone">Drop pdf here or click to upload...</div>
<div class="dz-preview dz-file-preview well" id="preview-template"></div>
</div>
<div class="div_float_clear"></div>
<div id="page_overview">
<!-- Filled by dynamically loaded content -->
</div>
<div id="div_select_buttons">
<button class="button_export" id="button_download_complete_pdf" @click="download_pdf()">Download complete PDF</button>
<button class="button_export" id="button_download_individual_pages" @click="download_split_pdf()">Split PDF into individual pages</button>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script>
var uuid;
new Vue({
el: '#app',
data: {
pdf_file: null,
project_uuid: null
},
mounted(){
window.addEventListener("load", () => this.side_loaded());
},
methods: {
downloadURI(uri, name) {
const link = document.createElement("a");
link.download = name;
link.href = uri;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
delete link;
},
download_pdf() {
this.downloadURI("/projects/" + this.project_uuid + "/complete.pdf", "complete_project.pdf");
},
download_split_pdf() {
fetch('/get_single_pages_archive/' + this.project_uuid + '/', {
method: 'GET'
})
.then(response => response.json())
.then(data => {
console.debug("data from Backend: ", data);
if (data["status"] == 200) {
this.downloadURI("/projects/" + this.project_uuid + "/pdf_splitted.zip", "splitted_pdfs.zip");
console.info("Archive with single Pages-PDFs created");
} else {
console.error("Project could not be created");
}
})
.catch(error => console.error(error));
},
side_loaded() {
fetch('/init_project/', {
method: 'GET'
})
.then(response => response.json())
.then(data => {
console.debug("data from Backend: ", data);
if (data["status"] == 200) {
this.project_uuid = data["project_uuid"];
uuid = data["project_uuid"];
} else {
this.project_uuid = null;
console.error("Project could not be created");
}
})
.catch(error => console.error(error));
// Dropzone has been added as a global variable.
const dropzone = new Dropzone("div#pdf-dropzone", {
url: "/add_pdf_to_project/",
paramName: "pdf"
});
dropzone.on("addedfile", file => {
// console.debug("on addedfile this...", this)
console.log("A file has been added");
file.previewElement.innerHTML = "";
console.debug("Suppress file.previewElement");
console.debug("PDF Project UUID: ", this.project_uuid);
});
dropzone.on("sending", function(file, xhr, formData) {
// console.debug("on sending this...", this)
// formData.append("uuid", this.project_uuid);
formData.append("uuid", uuid);
});
dropzone.on("complete", function(file, xhr, formData) {
div_overview = document.getElementById("page_overview");
div_overview.innerHTML = ""
console.debug("div_overview", div_overview);
fetch('/get_single_pages_info/' + uuid + '/', {
method: 'GET'
})
.then(response => response.json())
.then(data => {
console.debug("data from Backend: ", data);
if (data["status"] == 200) {
for (page in data["pages"]) {
console.debug(page);
// <embed src="" width="500" height="375" type="application/pdf">
node = document.createElement("embed");
node.width = "300";
node.height = "300";
node.type = "application/pdf";
node.src = data["pages"][page]
div_overview.appendChild(node)
}
console.debug(data["status"]);
} else {
console.debug(data["status"]);
console.error("Project could not be created");
}
})
.catch(error => console.error(error));
console.debug("div_overview", div_overview);
});
}
}
});
</script>
</body>
</html>

View File

@ -3,7 +3,9 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="/static/icons/filetype-pdf.svg" type="image/x-icon">
<title>PDF Tools</title>
<meta http-equiv="Refresh" content="0; url='/app/'" />
</head>
<body>