created dynamic frontend
This commit is contained in:
parent
93b7f68b54
commit
2535006f02
89
app.py
89
app.py
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
*/
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
||||
|
||||
|
@ -53,13 +55,13 @@ new Vue({
|
|||
const backendURL = file2 ? '/merge_to_pdf' : '/split_to_zip';
|
||||
|
||||
fetch(backendURL, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.debug("data from Backend: ", data);
|
||||
this.downloadURI(data["url"], data["name"]);
|
||||
console.debug("data from Backend: ", data);
|
||||
this.downloadURI(data["url"], data["name"]);
|
||||
})
|
||||
.catch(error => console.error(error));
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue