From f19c7861b0369e5f8a6cd7a65cc62387961d30ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20M=C3=BCller?= Date: Fri, 5 Jan 2024 11:53:12 +0100 Subject: [PATCH 1/7] current working example --- .gitignore | 1 + Dockerfile | 1 + app.py | 80 ++++++----- init.sh | 7 +- pdf_util/__init__.py | 1 + pdf_util/pdf_project_manager.py | 9 ++ pdf_util/pdf_util.py | 46 +++++++ requirements.txt | 7 +- tests/__init__.py | 1 + tests/sample_pdfs/sample_10_page.pdf | Bin 0 -> 54836 bytes tests/sample_pdfs/sample_1_page.pdf | Bin 0 -> 70011 bytes tests/sample_pdfs/sample_2_page.pdf | 198 +++++++++++++++++++++++++++ tests/test_pdf_project_manager.py | 15 ++ tests/test_pdf_util.py | 58 ++++++++ 14 files changed, 379 insertions(+), 45 deletions(-) create mode 100644 .gitignore create mode 100644 pdf_util/__init__.py create mode 100644 pdf_util/pdf_project_manager.py create mode 100644 pdf_util/pdf_util.py create mode 100644 tests/__init__.py create mode 100644 tests/sample_pdfs/sample_10_page.pdf create mode 100644 tests/sample_pdfs/sample_1_page.pdf create mode 100644 tests/sample_pdfs/sample_2_page.pdf create mode 100644 tests/test_pdf_project_manager.py create mode 100644 tests/test_pdf_util.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dbe9c82 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index fd57988..8877906 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app RUN mkdir /app/uploads RUN mkdir /app/split RUN mkdir /app/merge +RUN mkdir /app/projects RUN apt-get update # install dependencies diff --git a/app.py b/app.py index c521abf..7082a6e 100644 --- a/app.py +++ b/app.py @@ -1,13 +1,30 @@ import shutil import os from flask import Flask, render_template, request, redirect, jsonify, send_from_directory -from PyPDF2 import PdfReader, PdfWriter +from pypdf import PdfReader, PdfWriter from pathlib import Path +from pdf_util.pdf_util import pdf_util + +import datetime as dt +import logging +import sys + +# Setup Logging +logging.basicConfig( + # level=logging.ERROR, + # level=logging.INFO, + level=logging.DEBUG, + format="Start: " + str(dt.datetime.now()).replace(" ", "_") + " | %(asctime)s [%(levelname)s] %(message)s", + handlers=[ + logging.FileHandler("/var/log/" + str(dt.datetime.today().strftime('%Y-%m-%d')) + "_-_cron.log"), + logging.StreamHandler(sys.stdout) + ] +) + app = Flask(__name__) -UPLOAD_FOLDER = 'uploads' -app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER +app.config['UPLOAD_FOLDER'] = 'uploads' @app.route('/') @@ -36,27 +53,19 @@ def split_to_zip(): return redirect(request.url) if pdf_file: - in_filename = pdf_file.filename + in_filename = pdf_file.filename.rsplit('.', 1)[0] filename = os.path.join(app.config['UPLOAD_FOLDER'], pdf_file.filename) pdf_file.save(filename) - out_filenames = [] - Path("/tmp/split_pdf").mkdir(parents=True, exist_ok=True) - with open(filename, 'rb') as pdf_file: - pdf_reader = PdfReader(pdf_file) - num_pages = len(pdf_reader.pages) - for page_num in range(num_pages): + # Use pdf_utils Module to split File + out_filenames = pdf_util(filename).split_pdf() + logging.debug(out_filenames) - writer = PdfWriter() - writer.add_page(pdf_reader.pages[page_num]) - - out_filename = '/tmp/split_pdf/' + in_filename + '_' + str(page_num) + '.pdf' - with open(out_filename, 'wb') as outfile: - writer.write(outfile) - out_filenames.append(out_filename) - - shutil.make_archive(in_filename.rsplit('.', 1)[0] + '_splitted', 'zip', "/tmp/split_pdf") - zip_filename = in_filename.rsplit('.', 1)[0] + "_splitted.zip" + logging.debug(in_filename) + logging.debug(os.path.splitext(pdf_file.filename)[0]) + + shutil.make_archive(in_filename + '_splitted', 'zip', os.path.dirname(filename) + "/split_pdf") + zip_filename = in_filename + "_splitted.zip" os.rename("/app/" + zip_filename, "/app/split/" + zip_filename) for temp_file in out_filenames: @@ -79,35 +88,24 @@ def merge_to_pdf(): return redirect(request.url) if pdf_file_1: - filename_1 = os.path.join(app.config['UPLOAD_FOLDER'], pdf_file_1.filename) + filename_1 = os.path.join(os.path.dirname(os.path.realpath(__file__)), app.config['UPLOAD_FOLDER'], pdf_file_1.filename) pdf_file_1.save(filename_1) if pdf_file_2: - filename_2 = os.path.join(app.config['UPLOAD_FOLDER'], pdf_file_2.filename) + filename_2 = os.path.join(os.path.dirname(os.path.realpath(__file__)), app.config['UPLOAD_FOLDER'], pdf_file_2.filename) pdf_file_2.save(filename_2) if pdf_file_1 and pdf_file_2: + logging.debug(filename_1) + logging.debug(filename_2) - with open(filename_1, 'rb') as pdf_file_1, open(filename_2, 'rb') as pdf_file_2: - pdf_reader_1 = PdfReader(pdf_file_1) - pdf_reader_2 = PdfReader(pdf_file_2) + # Use pdf_utils Module to split File + out_path = pdf_util(filename_1).merge_pdf_with(filename_2) + logging.debug(out_path) + os.rename(out_path, "/app/merge/merger.pdf") - Path("/tmp/merge_pdf").mkdir(parents=True, exist_ok=True) - writer = PdfWriter() - - for page_num in range(len(pdf_reader_1.pages)): - writer.add_page(pdf_reader_1.pages[page_num]) - - for page_num in range(len(pdf_reader_2.pages)): - writer.add_page(pdf_reader_2.pages[page_num]) - - out_filename = '/app/merge/merger.pdf' - with open(out_filename, 'wb') as outfile: - writer.write(outfile) - - response = jsonify({"url": '/merge/merger.pdf', "name": 'merge.pdf'}) - # response.headers.add("Access-Control-Allow-Origin", "*") - return response + response = jsonify({"url": '/merge/merger.pdf', "name": os.path.splitext(os.path.basename(out_path))[0]}) + return response if __name__ == '__main__': diff --git a/init.sh b/init.sh index e0c81f7..9af698b 100755 --- a/init.sh +++ b/init.sh @@ -1,6 +1,9 @@ #!/bin/bash -# (cd /app/ && gunicorn --access-logfile '-' --error-logfile '-' -w 4 -b 0.0.0.0:8000 wsgi:app) # Dev (Logging to console) -(cd /app/ && gunicorn -w 4 -b 0.0.0.0:8000 wsgi:app) +# (cd /app/ && pytest tests/test_pdf_util.py) +(cd /app/ && pytest) + +# (cd /app/ && gunicorn -w 4 -b 0.0.0.0:8000 wsgi:app) +(cd /app/ && gunicorn --access-logfile '-' --error-logfile '-' -w 4 -b 0.0.0.0:8000 wsgi:app) # Dev (Logging to console) /bin/bash \ No newline at end of file diff --git a/pdf_util/__init__.py b/pdf_util/__init__.py new file mode 100644 index 0000000..203562b --- /dev/null +++ b/pdf_util/__init__.py @@ -0,0 +1 @@ +# __init__.py \ No newline at end of file diff --git a/pdf_util/pdf_project_manager.py b/pdf_util/pdf_project_manager.py new file mode 100644 index 0000000..c4b199c --- /dev/null +++ b/pdf_util/pdf_project_manager.py @@ -0,0 +1,9 @@ +from pypdf import PdfReader, PdfWriter +import uuid +import os + +class pdf_project_manager: + def __init__(self): + self.uuid = str(uuid.uuid4()) + os.makedirs("/app/projects/" + self.uuid, exist_ok=True) + diff --git a/pdf_util/pdf_util.py b/pdf_util/pdf_util.py new file mode 100644 index 0000000..ecf88cf --- /dev/null +++ b/pdf_util/pdf_util.py @@ -0,0 +1,46 @@ +import os +from pypdf import PdfReader, PdfWriter + +class pdf_util: + def __init__(self, file_path): + self.file_path = file_path + self.file_name = os.path.basename(file_path) + self.file_name_wo_extension = os.path.splitext(os.path.basename(file_path))[0] + + + def split_pdf(self): + out_filenames = [] + os.makedirs(os.path.dirname(self.file_path) + "/split_pdf", exist_ok=True) + with open(self.file_path, 'rb') as pdf_file: + pdf_reader = PdfReader(pdf_file) + num_pages = len(pdf_reader.pages) + for page_num in range(num_pages): + + writer = PdfWriter() + writer.add_page(pdf_reader.pages[page_num]) + + out_filename = os.path.dirname(self.file_path) + '/split_pdf/' + self.file_name_wo_extension + '_' + str(page_num + 1) + '.pdf' + with open(out_filename, 'wb') as outfile: + writer.write(outfile) + out_filenames.append(out_filename) + + return out_filenames + + + def merge_pdf_with(self, merge_file_path, merged_name="merged"): + os.makedirs(os.path.dirname(self.file_path) + "/merge_pdf", exist_ok=True) + pdf_reader_1 = PdfReader(self.file_path) + pdf_reader_2 = PdfReader(merge_file_path) + writer = PdfWriter() + + for page_num in range(len(pdf_reader_1.pages)): + writer.add_page(pdf_reader_1.pages[page_num]) + + for page_num in range(len(pdf_reader_2.pages)): + writer.add_page(pdf_reader_2.pages[page_num]) + + out_path = os.path.dirname(self.file_path) + "/merge_pdf" + '/merger.pdf' + with open(out_path, 'wb') as outfile: + writer.write(outfile) + + return out_path diff --git a/requirements.txt b/requirements.txt index 5710153..bdcffa7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,6 @@ flask -PyPDF2 -gunicorn \ No newline at end of file +gunicorn +pypdf + +# For testing +pytest \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..203562b --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +# __init__.py \ No newline at end of file diff --git a/tests/sample_pdfs/sample_10_page.pdf b/tests/sample_pdfs/sample_10_page.pdf new file mode 100644 index 0000000000000000000000000000000000000000..99d31cef1efdbb90fba2231a19974f4f74229be0 GIT binary patch literal 54836 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

`kx>?2G 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 literal 0 HcmV?d00001 diff --git a/tests/sample_pdfs/sample_2_page.pdf b/tests/sample_pdfs/sample_2_page.pdf new file mode 100644 index 0000000..dbf091d --- /dev/null +++ b/tests/sample_pdfs/sample_2_page.pdf @@ -0,0 +1,198 @@ +%PDF-1.3 +%âãÏÓ + +1 0 obj +<< +/Type /Catalog +/Outlines 2 0 R +/Pages 3 0 R +>> +endobj + +2 0 obj +<< +/Type /Outlines +/Count 0 +>> +endobj + +3 0 obj +<< +/Type /Pages +/Count 2 +/Kids [ 4 0 R 6 0 R ] +>> +endobj + +4 0 obj +<< +/Type /Page +/Parent 3 0 R +/Resources << +/Font << +/F1 9 0 R +>> +/ProcSet 8 0 R +>> +/MediaBox [0 0 612.0000 792.0000] +/Contents 5 0 R +>> +endobj + +5 0 obj +<< /Length 1074 >> +stream +2 J +BT +0 0 0 rg +/F1 0027 Tf +57.3750 722.2800 Td +( A Simple PDF File ) Tj +ET +BT +/F1 0010 Tf +69.2500 688.6080 Td +( This is a small demonstration .pdf file - ) Tj +ET +BT +/F1 0010 Tf +69.2500 664.7040 Td +( just for use in the Virtual Mechanics tutorials. More text. And more ) Tj +ET +BT +/F1 0010 Tf +69.2500 652.7520 Td +( text. And more text. And more text. And more text. ) Tj +ET +BT +/F1 0010 Tf +69.2500 628.8480 Td +( And more text. And more text. And more text. And more text. And more ) Tj +ET +BT +/F1 0010 Tf +69.2500 616.8960 Td +( text. And more text. Boring, zzzzz. And more text. And more text. And ) Tj +ET +BT +/F1 0010 Tf +69.2500 604.9440 Td +( more text. And more text. And more text. And more text. And more text. ) Tj +ET +BT +/F1 0010 Tf +69.2500 592.9920 Td +( And more text. And more text. ) Tj +ET +BT +/F1 0010 Tf +69.2500 569.0880 Td +( And more text. And more text. And more text. And more text. And more ) Tj +ET +BT +/F1 0010 Tf +69.2500 557.1360 Td +( text. And more text. And more text. Even more. Continued on page 2 ...) Tj +ET +endstream +endobj + +6 0 obj +<< +/Type /Page +/Parent 3 0 R +/Resources << +/Font << +/F1 9 0 R +>> +/ProcSet 8 0 R +>> +/MediaBox [0 0 612.0000 792.0000] +/Contents 7 0 R +>> +endobj + +7 0 obj +<< /Length 676 >> +stream +2 J +BT +0 0 0 rg +/F1 0027 Tf +57.3750 722.2800 Td +( Simple PDF File 2 ) Tj +ET +BT +/F1 0010 Tf +69.2500 688.6080 Td +( ...continued from page 1. Yet more text. And more text. And more text. ) Tj +ET +BT +/F1 0010 Tf +69.2500 676.6560 Td +( And more text. And more text. And more text. And more text. And more ) Tj +ET +BT +/F1 0010 Tf +69.2500 664.7040 Td +( text. Oh, how boring typing this stuff. But not as boring as watching ) Tj +ET +BT +/F1 0010 Tf +69.2500 652.7520 Td +( paint dry. And more text. And more text. And more text. And more text. ) Tj +ET +BT +/F1 0010 Tf +69.2500 640.8000 Td +( Boring. More, a little more text. The end, and just as well. ) Tj +ET +endstream +endobj + +8 0 obj +[/PDF /Text] +endobj + +9 0 obj +<< +/Type /Font +/Subtype /Type1 +/Name /F1 +/BaseFont /Helvetica +/Encoding /WinAnsiEncoding +>> +endobj + +10 0 obj +<< +/Creator (Rave \(http://www.nevrona.com/rave\)) +/Producer (Nevrona Designs) +/CreationDate (D:20060301072826) +>> +endobj + +xref +0 11 +0000000000 65535 f +0000000019 00000 n +0000000093 00000 n +0000000147 00000 n +0000000222 00000 n +0000000390 00000 n +0000001522 00000 n +0000001690 00000 n +0000002423 00000 n +0000002456 00000 n +0000002574 00000 n + +trailer +<< +/Size 11 +/Root 1 0 R +/Info 10 0 R +>> + +startxref +2714 +%%EOF diff --git a/tests/test_pdf_project_manager.py b/tests/test_pdf_project_manager.py new file mode 100644 index 0000000..414c5f3 --- /dev/null +++ b/tests/test_pdf_project_manager.py @@ -0,0 +1,15 @@ +import pytest +import os +from pdf_util.pdf_project_manager import pdf_project_manager + +def test_basic_object_creation(): + test_pdf_project_manager = pdf_project_manager() + print(test_pdf_project_manager.uuid) + assert len(test_pdf_project_manager.uuid) == 36 + + +def test_folder_creation(): + test_pdf_project_manager = pdf_project_manager() + print(test_pdf_project_manager.uuid) + assert os.path.isdir('/app/projects/' + test_pdf_project_manager.uuid) + diff --git a/tests/test_pdf_util.py b/tests/test_pdf_util.py new file mode 100644 index 0000000..0d50ce3 --- /dev/null +++ b/tests/test_pdf_util.py @@ -0,0 +1,58 @@ +import pytest +import os +from pdf_util.pdf_util import pdf_util + +def test_split_pages(): + # Single Pages + test_file = pdf_util("/app/tests/sample_pdfs/sample_1_page.pdf").split_pdf() + print(test_file) + + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_1_page_1.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_1_page_1.pdf").st_size == 69339 + + # Two Pages + test_file = pdf_util("/app/tests/sample_pdfs/sample_2_page.pdf").split_pdf() + print(test_file) + + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_2_page_1.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_2_page_1.pdf").st_size == 1804 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_2_page_2.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_2_page_2.pdf").st_size == 1405 + + # Ten Pages + test_file = pdf_util("/app/tests/sample_pdfs/sample_10_page.pdf").split_pdf() + print(test_file) + + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_1.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_1.pdf").st_size == 3167 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_2.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_2.pdf").st_size == 2888 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_3.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_3.pdf").st_size == 6670 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_4.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_4.pdf").st_size == 3043 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_5.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_5.pdf").st_size == 9968 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_6.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_6.pdf").st_size == 5367 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_7.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_7.pdf").st_size == 10093 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_8.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_8.pdf").st_size == 8578 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_9.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_9.pdf").st_size == 30188 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_10.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_10.pdf").st_size == 3789 + + +def test_merge_with(): + # Write test code to verify the behavior of the merge_with method + pass + +def test_rotate_pages(): + # Write test code to verify the behavior of the rotate_pages method + pass + +def test_ocr_pages(): + # Write test code to verify the behavior of the rotate_pages method + pass -- 2.45.2 From 6cb54c31ba5fbece491cbff22dd8b12ecbe525cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20M=C3=BCller?= Date: Fri, 5 Jan 2024 17:40:16 +0100 Subject: [PATCH 2/7] Chnage logging output --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index 7082a6e..6770ec2 100644 --- a/app.py +++ b/app.py @@ -14,7 +14,7 @@ logging.basicConfig( # level=logging.ERROR, # level=logging.INFO, level=logging.DEBUG, - format="Start: " + str(dt.datetime.now()).replace(" ", "_") + " | %(asctime)s [%(levelname)s] %(message)s", + format=str(dt.datetime.now()).replace(" ", "_") + " | %(asctime)s [%(levelname)s] %(message)s", handlers=[ logging.FileHandler("/var/log/" + str(dt.datetime.today().strftime('%Y-%m-%d')) + "_-_cron.log"), logging.StreamHandler(sys.stdout) -- 2.45.2 From 376fafa171e1a27a6fcd5ebd47c9f52efdf0d613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20M=C3=BCller?= Date: Fri, 5 Jan 2024 17:43:01 +0100 Subject: [PATCH 3/7] added logging added split_pdf and merge_pdf with location parameter --- pdf_util/pdf_project_manager.py | 41 +++++++++++++++++++++++++++- pdf_util/pdf_util.py | 48 +++++++++++++++++++++++++++------ 2 files changed, 80 insertions(+), 9 deletions(-) diff --git a/pdf_util/pdf_project_manager.py b/pdf_util/pdf_project_manager.py index c4b199c..312eff8 100644 --- a/pdf_util/pdf_project_manager.py +++ b/pdf_util/pdf_project_manager.py @@ -1,9 +1,48 @@ from pypdf import PdfReader, PdfWriter import uuid import os +import shutil + +import datetime as dt +import logging +import sys + + +from pdf_util.pdf_util import pdf_util + + +# Setup Logging +logging.basicConfig( + # level=logging.ERROR, + # level=logging.INFO, + level=logging.DEBUG, + format=str(dt.datetime.now()).replace(" ", "_") + " | %(asctime)s [%(levelname)s] %(message)s", + handlers=[ + logging.FileHandler("/var/log/" + str(dt.datetime.today().strftime('%Y-%m-%d')) + "_-_pdf_project_manager.log"), + logging.StreamHandler(sys.stdout) + ] +) + +base_path = "/app/projects/" class pdf_project_manager: def __init__(self): self.uuid = str(uuid.uuid4()) - os.makedirs("/app/projects/" + self.uuid, exist_ok=True) + self.pdf_init = False + self.project_name = "" + os.makedirs(base_path + self.uuid, exist_ok=True) + + """ + def add_pdf(self, pdf_path): + if self.pdf_init: + shutil.copyfile(pdf_path, base_path + self.uuid + "/complete.pdf") + else: + shutil.copyfile(pdf_path, base_path + self.uuid + "/tmp.pdf") + pdf_util(base_path + self.uuid + "/complete.pdf").merge_pdf_with_and_location(base_path + self.uuid + "/tmp.pdf", base_path + self.uuid + "/tmp_complete.pdf") + shutil.copyfile(base_path + self.uuid + "/tmp_complete.pdf", base_path + self.uuid + "/complete.pdf") + os.remove(base_path + self.uuid + "/tmp_complete.pdf") + os.remove(base_path + self.uuid + "/tmp.pdf") + + # Splitt files in all single Pages in Subdirectory... + """ \ No newline at end of file diff --git a/pdf_util/pdf_util.py b/pdf_util/pdf_util.py index ecf88cf..15e2d0f 100644 --- a/pdf_util/pdf_util.py +++ b/pdf_util/pdf_util.py @@ -1,6 +1,23 @@ import os from pypdf import PdfReader, PdfWriter +import datetime as dt +import logging +import sys + +# Setup Logging +logging.basicConfig( + # level=logging.ERROR, + # level=logging.INFO, + level=logging.DEBUG, + format=str(dt.datetime.now()).replace(" ", "_") + " | %(asctime)s [%(levelname)s] %(message)s", + handlers=[ + logging.FileHandler("/var/log/" + str(dt.datetime.today().strftime('%Y-%m-%d')) + "_-_pdf_util.log"), + logging.StreamHandler(sys.stdout) + ] +) + + class pdf_util: def __init__(self, file_path): self.file_path = file_path @@ -8,9 +25,9 @@ class pdf_util: self.file_name_wo_extension = os.path.splitext(os.path.basename(file_path))[0] - def split_pdf(self): + def split_pdf_with_location(self, output_filepath, no_names=False): out_filenames = [] - os.makedirs(os.path.dirname(self.file_path) + "/split_pdf", exist_ok=True) + os.makedirs(os.path.dirname(output_filepath), exist_ok=True) with open(self.file_path, 'rb') as pdf_file: pdf_reader = PdfReader(pdf_file) num_pages = len(pdf_reader.pages) @@ -19,16 +36,25 @@ class pdf_util: writer = PdfWriter() writer.add_page(pdf_reader.pages[page_num]) - out_filename = os.path.dirname(self.file_path) + '/split_pdf/' + self.file_name_wo_extension + '_' + str(page_num + 1) + '.pdf' + if no_names: + out_filename = os.path.dirname(output_filepath) + '/' + str(page_num + 1) + '.pdf' + else: + out_filename = os.path.dirname(output_filepath) + '/' + self.file_name_wo_extension + '_' + str(page_num + 1) + '.pdf' + with open(out_filename, 'wb') as outfile: writer.write(outfile) out_filenames.append(out_filename) return out_filenames + # Deprecate when pdf_project_manager takes effect + def split_pdf(self): + os.makedirs(os.path.dirname(self.file_path) + "/split_pdf", exist_ok=True) + return self.split_pdf_with_location(os.path.dirname(self.file_path) + "/split_pdf/", False) - def merge_pdf_with(self, merge_file_path, merged_name="merged"): - os.makedirs(os.path.dirname(self.file_path) + "/merge_pdf", exist_ok=True) + + def merge_pdf_with_and_location(self, merge_file_path, output_filepath): + os.makedirs(os.path.dirname(output_filepath), exist_ok=True) pdf_reader_1 = PdfReader(self.file_path) pdf_reader_2 = PdfReader(merge_file_path) writer = PdfWriter() @@ -39,8 +65,14 @@ class pdf_util: for page_num in range(len(pdf_reader_2.pages)): writer.add_page(pdf_reader_2.pages[page_num]) - out_path = os.path.dirname(self.file_path) + "/merge_pdf" + '/merger.pdf' - with open(out_path, 'wb') as outfile: + with open(output_filepath, 'wb') as outfile: writer.write(outfile) - return out_path + return output_filepath + + # Deprecate when pdf_project_manager takes effect + def merge_pdf_with(self, merge_file_path, merged_name="merged"): + os.makedirs(os.path.dirname(self.file_path) + "/merge_pdf", exist_ok=True) + return self.merge_pdf_with_and_location(merge_file_path, os.path.dirname(self.file_path) + "/merge_pdf" + '/merger.pdf') + + -- 2.45.2 From f4b8029593c176d3dcfba8eadac9b3325ebb57b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20M=C3=BCller?= Date: Fri, 5 Jan 2024 17:43:28 +0100 Subject: [PATCH 4/7] added tests for function call with location --- tests/test_pdf_project_manager.py | 11 +++ tests/test_pdf_util.py | 135 ++++++++++++++++++++++++++++-- 2 files changed, 138 insertions(+), 8 deletions(-) diff --git a/tests/test_pdf_project_manager.py b/tests/test_pdf_project_manager.py index 414c5f3..cda9ffe 100644 --- a/tests/test_pdf_project_manager.py +++ b/tests/test_pdf_project_manager.py @@ -13,3 +13,14 @@ def test_folder_creation(): print(test_pdf_project_manager.uuid) assert os.path.isdir('/app/projects/' + test_pdf_project_manager.uuid) + +def test_Projecttest_1(): + + """ + test_project = pdf_project_manager() + print(test_pdf_project_manager.uuid) + assert os.path.isdir('/app/projects/' + test_pdf_project_manager.uuid) + """ + + assert True + diff --git a/tests/test_pdf_util.py b/tests/test_pdf_util.py index 0d50ce3..159ed5d 100644 --- a/tests/test_pdf_util.py +++ b/tests/test_pdf_util.py @@ -1,19 +1,18 @@ import pytest import os +import shutil from pdf_util.pdf_util import pdf_util -def test_split_pages(): +def test_split_pdf(): # Single Pages test_file = pdf_util("/app/tests/sample_pdfs/sample_1_page.pdf").split_pdf() print(test_file) - print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_1_page_1.pdf").st_size) assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_1_page_1.pdf").st_size == 69339 # Two Pages test_file = pdf_util("/app/tests/sample_pdfs/sample_2_page.pdf").split_pdf() print(test_file) - print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_2_page_1.pdf").st_size) assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_2_page_1.pdf").st_size == 1804 print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_2_page_2.pdf").st_size) @@ -22,7 +21,6 @@ def test_split_pages(): # Ten Pages test_file = pdf_util("/app/tests/sample_pdfs/sample_10_page.pdf").split_pdf() print(test_file) - print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_1.pdf").st_size) assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_1.pdf").st_size == 3167 print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_2.pdf").st_size) @@ -43,11 +41,132 @@ def test_split_pages(): assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_9.pdf").st_size == 30188 print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_10.pdf").st_size) assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_10.pdf").st_size == 3789 - -def test_merge_with(): - # Write test code to verify the behavior of the merge_with method - pass + shutil.rmtree("/app/tests/sample_pdfs/split_pdf/") + + +def test_split_pdf_and_location(): + # Single Pages + test_file = pdf_util("/app/tests/sample_pdfs/sample_1_page.pdf").split_pdf_with_location("/tmp/test_directory/", False) + print(test_file) + print(os.stat("/tmp/test_directory/sample_1_page_1.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_1_page_1.pdf").st_size == 69339 + + test_file = pdf_util("/app/tests/sample_pdfs/sample_1_page.pdf").split_pdf_with_location("/tmp/test_directory/", True) + print(test_file) + print(os.stat("/tmp/test_directory/1.pdf").st_size) + assert os.stat("/tmp/test_directory/1.pdf").st_size == 69339 + + shutil.rmtree("/tmp/test_directory/") + + + # Two Pages + test_file = pdf_util("/app/tests/sample_pdfs/sample_2_page.pdf").split_pdf_with_location("/tmp/test_directory/", False) + print(test_file) + print(os.stat("/tmp/test_directory/sample_2_page_1.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_2_page_1.pdf").st_size == 1804 + print(os.stat("/tmp/test_directory/sample_2_page_2.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_2_page_2.pdf").st_size == 1405 + + # Two Pages + test_file = pdf_util("/app/tests/sample_pdfs/sample_2_page.pdf").split_pdf_with_location("/tmp/test_directory/", True) + print(test_file) + print(os.stat("/tmp/test_directory/1.pdf").st_size) + assert os.stat("/tmp/test_directory/1.pdf").st_size == 1804 + print(os.stat("/tmp/test_directory/2.pdf").st_size) + assert os.stat("/tmp/test_directory/2.pdf").st_size == 1405 + + shutil.rmtree("/tmp/test_directory/") + + + # Ten Pages + test_file = pdf_util("/app/tests/sample_pdfs/sample_10_page.pdf").split_pdf_with_location("/tmp/test_directory/", False) + print(test_file) + print(os.stat("/tmp/test_directory/sample_10_page_1.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_1.pdf").st_size == 3167 + print(os.stat("/tmp/test_directory/sample_10_page_2.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_2.pdf").st_size == 2888 + print(os.stat("/tmp/test_directory/sample_10_page_3.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_3.pdf").st_size == 6670 + print(os.stat("/tmp/test_directory/sample_10_page_4.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_4.pdf").st_size == 3043 + print(os.stat("/tmp/test_directory/sample_10_page_5.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_5.pdf").st_size == 9968 + print(os.stat("/tmp/test_directory/sample_10_page_6.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_6.pdf").st_size == 5367 + print(os.stat("/tmp/test_directory/sample_10_page_7.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_7.pdf").st_size == 10093 + print(os.stat("/tmp/test_directory/sample_10_page_8.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_8.pdf").st_size == 8578 + print(os.stat("/tmp/test_directory/sample_10_page_9.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_9.pdf").st_size == 30188 + print(os.stat("/tmp/test_directory/sample_10_page_10.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_10.pdf").st_size == 3789 + + test_file = pdf_util("/app/tests/sample_pdfs/sample_10_page.pdf").split_pdf_with_location("/tmp/test_directory/", True) + print(test_file) + print(os.stat("/tmp/test_directory/1.pdf").st_size) + assert os.stat("/tmp/test_directory/1.pdf").st_size == 3167 + print(os.stat("/tmp/test_directory/2.pdf").st_size) + assert os.stat("/tmp/test_directory/2.pdf").st_size == 2888 + print(os.stat("/tmp/test_directory/3.pdf").st_size) + assert os.stat("/tmp/test_directory/3.pdf").st_size == 6670 + print(os.stat("/tmp/test_directory/4.pdf").st_size) + assert os.stat("/tmp/test_directory/4.pdf").st_size == 3043 + print(os.stat("/tmp/test_directory/5.pdf").st_size) + assert os.stat("/tmp/test_directory/5.pdf").st_size == 9968 + print(os.stat("/tmp/test_directory/6.pdf").st_size) + assert os.stat("/tmp/test_directory/6.pdf").st_size == 5367 + print(os.stat("/tmp/test_directory/7.pdf").st_size) + assert os.stat("/tmp/test_directory/7.pdf").st_size == 10093 + print(os.stat("/tmp/test_directory/8.pdf").st_size) + assert os.stat("/tmp/test_directory/8.pdf").st_size == 8578 + print(os.stat("/tmp/test_directory/9.pdf").st_size) + assert os.stat("/tmp/test_directory/9.pdf").st_size == 30188 + print(os.stat("/tmp/test_directory/10.pdf").st_size) + assert os.stat("/tmp/test_directory/10.pdf").st_size == 3789 + + shutil.rmtree("/tmp/test_directory/") + + +def test_merge_pdf_with(): + test_file = pdf_util("/app/tests/sample_pdfs/sample_1_page.pdf").merge_pdf_with("/app/tests/sample_pdfs/sample_2_page.pdf") + print(test_file) + print(os.stat("/app/tests/sample_pdfs/merge_pdf/merger.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/merge_pdf/merger.pdf").st_size == 71761 + + test_file = pdf_util("/app/tests/sample_pdfs/sample_1_page.pdf").merge_pdf_with("/app/tests/sample_pdfs/sample_10_page.pdf") + print(test_file) + print(os.stat("/app/tests/sample_pdfs/merge_pdf/merger.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/merge_pdf/merger.pdf").st_size == 115044 + + test_file = pdf_util("/app/tests/sample_pdfs/sample_2_page.pdf").merge_pdf_with("/app/tests/sample_pdfs/sample_10_page.pdf") + print(test_file) + print(os.stat("/app/tests/sample_pdfs/merge_pdf/merger.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/merge_pdf/merger.pdf").st_size == 48427 + + shutil.rmtree("/app/tests/sample_pdfs/merge_pdf/") + + +def test_merge_pdf_with_and_location(): + test_file = pdf_util("/app/tests/sample_pdfs/sample_1_page.pdf").merge_pdf_with_and_location("/app/tests/sample_pdfs/sample_2_page.pdf", "/tmp/test_directory/merged_file.pdf") + print(test_file) + print(os.stat("/tmp/test_directory/merged_file.pdf").st_size) + assert os.stat("/tmp/test_directory/merged_file.pdf").st_size == 71761 + shutil.rmtree("/tmp/test_directory/") + + test_file = pdf_util("/app/tests/sample_pdfs/sample_1_page.pdf").merge_pdf_with_and_location("/app/tests/sample_pdfs/sample_10_page.pdf", "/tmp/test_directory/merge_pdf/merger.pdf") + print(test_file) + print(os.stat("/tmp/test_directory/merge_pdf/merger.pdf").st_size) + assert os.stat("/tmp/test_directory/merge_pdf/merger.pdf").st_size == 115044 + shutil.rmtree("/tmp/test_directory/") + + test_file = pdf_util("/app/tests/sample_pdfs/sample_2_page.pdf").merge_pdf_with_and_location("/app/tests/sample_pdfs/sample_10_page.pdf", "/tmp/test_directory/merge_pdf/bigfile.pdf") + print(test_file) + print(os.stat("/tmp/test_directory/merge_pdf/bigfile.pdf").st_size) + assert os.stat("/tmp/test_directory/merge_pdf/bigfile.pdf").st_size == 48427 + + shutil.rmtree("/tmp/test_directory/") def test_rotate_pages(): # Write test code to verify the behavior of the rotate_pages method -- 2.45.2 From 6b74b8902936cecba69e00e611c6d8ca2e0f67c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20M=C3=BCller?= Date: Mon, 22 Jan 2024 16:47:35 +0100 Subject: [PATCH 5/7] backend improvements - move_page - merge_all_single_pages - constructor with optional parameter --- docker-compose.yaml | 2 +- init.sh | 2 +- pdf_util/pdf_project_manager.py | 71 ++++++++++++--- pdf_util/pdf_util.py | 19 ++-- tests/test_pdf_project_manager.py | 104 ++++++++++++++++++++-- tests/test_pdf_util.py | 140 ++++++++++++++++++++++++++++-- 6 files changed, 300 insertions(+), 38 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 115747c..8b0d2c4 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -24,6 +24,6 @@ services: ./init.sh " ports: - - 8001:8000 + - 8002:8000 diff --git a/init.sh b/init.sh index 9af698b..0f7b310 100755 --- a/init.sh +++ b/init.sh @@ -1,7 +1,7 @@ #!/bin/bash # (cd /app/ && pytest tests/test_pdf_util.py) -(cd /app/ && pytest) +(cd /app/ && pytest -o log_cli=true) # (cd /app/ && gunicorn -w 4 -b 0.0.0.0:8000 wsgi:app) (cd /app/ && gunicorn --access-logfile '-' --error-logfile '-' -w 4 -b 0.0.0.0:8000 wsgi:app) # Dev (Logging to console) diff --git a/pdf_util/pdf_project_manager.py b/pdf_util/pdf_project_manager.py index 312eff8..c1cb0ee 100644 --- a/pdf_util/pdf_project_manager.py +++ b/pdf_util/pdf_project_manager.py @@ -1,15 +1,16 @@ -from pypdf import PdfReader, PdfWriter import uuid import os import shutil +import traceback +import glob import datetime as dt import logging import sys - from pdf_util.pdf_util import pdf_util +base_path = "/app/projects/" # Setup Logging logging.basicConfig( @@ -23,26 +24,74 @@ logging.basicConfig( ] ) -base_path = "/app/projects/" class pdf_project_manager: - def __init__(self): - self.uuid = str(uuid.uuid4()) - self.pdf_init = False - self.project_name = "" + def __init__(self, uuid4=None): + if uuid4 is not None: + self.uuid = uuid4 + else: + self.uuid = str(uuid.uuid4()) + + try: + self.pdf_init = os.path.isfile('/app/projects/' + self.uuid + '/complete.pdf') + except Exception as e: + logging.warning("Error looking up file: " + str(e)) + logging.warning("Stacktrace: " + str(traceback.format_exc())) + self.pdf_init = False + os.makedirs(base_path + self.uuid, exist_ok=True) + self.pdf_handler = None + def merge_all_single_pages(self): + listing = glob.glob(base_path + self.uuid + '/splitted/*.pdf') + listing.sort() + shutil.copyfile(listing.pop(0), base_path + self.uuid + "/complete.pdf") + + for pdf_file in listing: + print(pdf_file) + pdf_util(base_path + self.uuid + "/complete.pdf").merge_pdf_with_and_location(pdf_file, base_path + self.uuid + "/tmp_complete.pdf") + shutil.copyfile(base_path + self.uuid + "/tmp_complete.pdf", base_path + self.uuid + "/complete.pdf") + os.remove(base_path + self.uuid + "/tmp_complete.pdf") - """ def add_pdf(self, pdf_path): - if self.pdf_init: + if not self.pdf_init: shutil.copyfile(pdf_path, base_path + self.uuid + "/complete.pdf") + self.pdf_handler = pdf_util(base_path + self.uuid + "/complete.pdf") + self.pdf_init = True else: shutil.copyfile(pdf_path, base_path + self.uuid + "/tmp.pdf") pdf_util(base_path + self.uuid + "/complete.pdf").merge_pdf_with_and_location(base_path + self.uuid + "/tmp.pdf", base_path + self.uuid + "/tmp_complete.pdf") shutil.copyfile(base_path + self.uuid + "/tmp_complete.pdf", base_path + self.uuid + "/complete.pdf") os.remove(base_path + self.uuid + "/tmp_complete.pdf") os.remove(base_path + self.uuid + "/tmp.pdf") + self.pdf_handler = pdf_util(base_path + self.uuid + "/complete.pdf") - # Splitt files in all single Pages in Subdirectory... - """ \ No newline at end of file + self.pdf_handler.split_pdf_with_location(base_path + self.uuid + '/splitted/', True, True) + + def move_page(self, from_location, to_location): + try: + if from_location <= 0 or to_location <= 0: + raise ValueError("Pagenumber smaller/equal Zero") + + if from_location < to_location: + shutil.move(base_path + self.uuid + '/splitted/' + str(from_location).zfill(4) + '.pdf', base_path + self.uuid + '/splitted/tmp.pdf') + for num in range(from_location, to_location): + print(num) + shutil.move(base_path + self.uuid + '/splitted/' + str(num + 1).zfill(4) + '.pdf', base_path + self.uuid + '/splitted/' + str(num).zfill(4) + '.pdf') + shutil.move(base_path + self.uuid + '/splitted/tmp.pdf', base_path + self.uuid + '/splitted/' + str(to_location).zfill(4) + '.pdf') + + elif from_location > to_location: + shutil.move(base_path + self.uuid + '/splitted/' + str(from_location).zfill(4) + '.pdf', base_path + self.uuid + '/splitted/tmp.pdf') + for num in reversed(range(to_location, from_location)): + print(num) + print("move: " + str(num).zfill(4) + " | to: " + str(num + 1).zfill(4)) + shutil.move(base_path + self.uuid + '/splitted/' + str(num).zfill(4) + '.pdf', base_path + self.uuid + '/splitted/' + str(num + 1).zfill(4) + '.pdf') + shutil.move(base_path + self.uuid + '/splitted/tmp.pdf', base_path + self.uuid + '/splitted/' + str(to_location).zfill(4) + '.pdf') + else: + raise ValueError("from_location and to_location are the same") + + self.merge_all_single_pages() + + except Exception as e: + logging.error("Error while moving page: " + str(e)) + logging.error("Stacktrace: " + str(traceback.format_exc())) diff --git a/pdf_util/pdf_util.py b/pdf_util/pdf_util.py index 15e2d0f..9364990 100644 --- a/pdf_util/pdf_util.py +++ b/pdf_util/pdf_util.py @@ -24,22 +24,24 @@ class pdf_util: self.file_name = os.path.basename(file_path) self.file_name_wo_extension = os.path.splitext(os.path.basename(file_path))[0] - - def split_pdf_with_location(self, output_filepath, no_names=False): + def split_pdf_with_location(self, output_filepath, no_names=False, int_padding=False): out_filenames = [] os.makedirs(os.path.dirname(output_filepath), exist_ok=True) with open(self.file_path, 'rb') as pdf_file: pdf_reader = PdfReader(pdf_file) num_pages = len(pdf_reader.pages) for page_num in range(num_pages): - writer = PdfWriter() writer.add_page(pdf_reader.pages[page_num]) + str_page_num = str(page_num + 1) + if int_padding: + str_page_num = str_page_num.zfill(4) + if no_names: - out_filename = os.path.dirname(output_filepath) + '/' + str(page_num + 1) + '.pdf' + out_filename = os.path.dirname(output_filepath) + '/' + str_page_num + '.pdf' else: - out_filename = os.path.dirname(output_filepath) + '/' + self.file_name_wo_extension + '_' + str(page_num + 1) + '.pdf' + out_filename = os.path.dirname(output_filepath) + '/' + self.file_name_wo_extension + '_' + str_page_num + '.pdf' with open(out_filename, 'wb') as outfile: writer.write(outfile) @@ -48,10 +50,9 @@ class pdf_util: return out_filenames # Deprecate when pdf_project_manager takes effect - def split_pdf(self): + def split_pdf(self, int_padding=False): os.makedirs(os.path.dirname(self.file_path) + "/split_pdf", exist_ok=True) - return self.split_pdf_with_location(os.path.dirname(self.file_path) + "/split_pdf/", False) - + return self.split_pdf_with_location(os.path.dirname(self.file_path) + "/split_pdf/", False, int_padding) def merge_pdf_with_and_location(self, merge_file_path, output_filepath): os.makedirs(os.path.dirname(output_filepath), exist_ok=True) @@ -74,5 +75,3 @@ class pdf_util: def merge_pdf_with(self, merge_file_path, merged_name="merged"): os.makedirs(os.path.dirname(self.file_path) + "/merge_pdf", exist_ok=True) return self.merge_pdf_with_and_location(merge_file_path, os.path.dirname(self.file_path) + "/merge_pdf" + '/merger.pdf') - - diff --git a/tests/test_pdf_project_manager.py b/tests/test_pdf_project_manager.py index cda9ffe..573eed0 100644 --- a/tests/test_pdf_project_manager.py +++ b/tests/test_pdf_project_manager.py @@ -1,26 +1,116 @@ -import pytest import os +import shutil from pdf_util.pdf_project_manager import pdf_project_manager + def test_basic_object_creation(): test_pdf_project_manager = pdf_project_manager() print(test_pdf_project_manager.uuid) assert len(test_pdf_project_manager.uuid) == 36 + shutil.rmtree('/app/projects/' + test_pdf_project_manager.uuid) + def test_folder_creation(): test_pdf_project_manager = pdf_project_manager() print(test_pdf_project_manager.uuid) assert os.path.isdir('/app/projects/' + test_pdf_project_manager.uuid) + shutil.rmtree('/app/projects/' + test_pdf_project_manager.uuid) -def test_Projecttest_1(): - """ - test_project = pdf_project_manager() +def test_merge_all_single_pages(): + test_pdf_project_manager = pdf_project_manager() print(test_pdf_project_manager.uuid) assert os.path.isdir('/app/projects/' + test_pdf_project_manager.uuid) - """ - - assert True + test_pdf_project_manager.add_pdf("/app/tests/sample_pdfs/sample_10_page.pdf") + print(os.path.isfile('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf')) + assert os.path.isfile('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf') + + print(os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf').st_size) + assert os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf').st_size == os.stat("/app/tests/sample_pdfs/sample_10_page.pdf").st_size + + os.remove('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf') + assert not os.path.isfile('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf') + + test_pdf_project_manager.merge_all_single_pages() + print(os.path.isfile('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf')) + assert os.path.isfile('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf') + + print(os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf').st_size) + assert os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf').st_size == 81291 + + shutil.rmtree('/app/projects/' + test_pdf_project_manager.uuid) + + +def test_add_multiple_pdfs(): + test_pdf_project_manager = pdf_project_manager() + print(test_pdf_project_manager.uuid) + assert os.path.isdir('/app/projects/' + test_pdf_project_manager.uuid) + + test_pdf_project_manager.add_pdf("/app/tests/sample_pdfs/sample_10_page.pdf") + print(os.path.isfile('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf')) + assert os.path.isfile('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf') + + print(os.path.isdir('/app/projects/' + test_pdf_project_manager.uuid + '/splitted')) + assert os.path.isdir('/app/projects/' + test_pdf_project_manager.uuid + '/splitted') + + print(os.path.isfile('/app/projects/' + test_pdf_project_manager.uuid + '/splitted/0001.pdf')) + assert os.path.isfile('/app/projects/' + test_pdf_project_manager.uuid + '/splitted/0001.pdf') + + print(os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/splitted/0001.pdf').st_size) + assert os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/splitted/0001.pdf').st_size == 3167 + + test_pdf_project_manager.add_pdf("/app/tests/sample_pdfs/sample_2_page.pdf") + print(os.path.isfile('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf')) + assert os.path.isfile('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf') + + print(os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/splitted/0001.pdf').st_size) + assert os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/splitted/0001.pdf').st_size == 3167 + + print(os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/splitted/0011.pdf').st_size) + assert os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/splitted/0011.pdf').st_size == 1804 + + print(os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf').st_size) + assert os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf').st_size == 48408 + + shutil.rmtree('/app/projects/' + test_pdf_project_manager.uuid) + + +def test_move_pages(): + test_pdf_project_manager = pdf_project_manager() + print(test_pdf_project_manager.uuid) + assert os.path.isdir('/app/projects/' + test_pdf_project_manager.uuid) + + test_pdf_project_manager.add_pdf("/app/tests/sample_pdfs/sample_10_page.pdf") + test_pdf_project_manager.add_pdf("/app/tests/sample_pdfs/sample_2_page.pdf") + print(os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf').st_size) + assert os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf').st_size == 48408 + + os.remove('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf') + assert not os.path.isfile('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf') + test_pdf_project_manager.move_page(1, 4) + print(os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/splitted/0004.pdf').st_size) + assert os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/splitted/0004.pdf').st_size == 3167 + print(os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf').st_size) + assert os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf').st_size == 83909 + + os.remove('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf') + assert not os.path.isfile('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf') + test_pdf_project_manager.move_page(4, 1) + print(os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/splitted/0001.pdf').st_size) + assert os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/splitted/0001.pdf').st_size == 3167 + print(os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf').st_size) + assert os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf').st_size == 83908 + + os.remove('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf') + assert not os.path.isfile('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf') + + test_pdf_project_manager.move_page(1, 12) + test_pdf_project_manager.move_page(12, 2) + assert os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/splitted/0002.pdf').st_size == 3167 + print(os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf').st_size) + assert os.stat('/app/projects/' + test_pdf_project_manager.uuid + '/complete.pdf').st_size == 83909 + + shutil.rmtree('/app/projects/' + test_pdf_project_manager.uuid) diff --git a/tests/test_pdf_util.py b/tests/test_pdf_util.py index 159ed5d..fb33e8d 100644 --- a/tests/test_pdf_util.py +++ b/tests/test_pdf_util.py @@ -1,15 +1,15 @@ -import pytest import os import shutil from pdf_util.pdf_util import pdf_util + def test_split_pdf(): # Single Pages test_file = pdf_util("/app/tests/sample_pdfs/sample_1_page.pdf").split_pdf() print(test_file) print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_1_page_1.pdf").st_size) assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_1_page_1.pdf").st_size == 69339 - + # Two Pages test_file = pdf_util("/app/tests/sample_pdfs/sample_2_page.pdf").split_pdf() print(test_file) @@ -17,7 +17,7 @@ def test_split_pdf(): assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_2_page_1.pdf").st_size == 1804 print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_2_page_2.pdf").st_size) assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_2_page_2.pdf").st_size == 1405 - + # Ten Pages test_file = pdf_util("/app/tests/sample_pdfs/sample_10_page.pdf").split_pdf() print(test_file) @@ -45,20 +45,61 @@ def test_split_pdf(): shutil.rmtree("/app/tests/sample_pdfs/split_pdf/") +def test_split_pdf_int_padding(): + # Single Pages + test_file = pdf_util("/app/tests/sample_pdfs/sample_1_page.pdf").split_pdf(True) + print(test_file) + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_1_page_0001.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_1_page_0001.pdf").st_size == 69339 + + # Two Pages + test_file = pdf_util("/app/tests/sample_pdfs/sample_2_page.pdf").split_pdf(True) + print(test_file) + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_2_page_0001.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_2_page_0001.pdf").st_size == 1804 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_2_page_0002.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_2_page_0002.pdf").st_size == 1405 + + # Ten Pages + test_file = pdf_util("/app/tests/sample_pdfs/sample_10_page.pdf").split_pdf(True) + print(test_file) + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0001.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0001.pdf").st_size == 3167 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0002.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0002.pdf").st_size == 2888 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0003.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0003.pdf").st_size == 6670 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0004.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0004.pdf").st_size == 3043 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0005.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0005.pdf").st_size == 9968 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0006.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0006.pdf").st_size == 5367 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0007.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0007.pdf").st_size == 10093 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0008.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0008.pdf").st_size == 8578 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0009.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0009.pdf").st_size == 30188 + print(os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0010.pdf").st_size) + assert os.stat("/app/tests/sample_pdfs/split_pdf/sample_10_page_0010.pdf").st_size == 3789 + + shutil.rmtree("/app/tests/sample_pdfs/split_pdf/") + + def test_split_pdf_and_location(): # Single Pages test_file = pdf_util("/app/tests/sample_pdfs/sample_1_page.pdf").split_pdf_with_location("/tmp/test_directory/", False) print(test_file) print(os.stat("/tmp/test_directory/sample_1_page_1.pdf").st_size) assert os.stat("/tmp/test_directory/sample_1_page_1.pdf").st_size == 69339 - + test_file = pdf_util("/app/tests/sample_pdfs/sample_1_page.pdf").split_pdf_with_location("/tmp/test_directory/", True) print(test_file) print(os.stat("/tmp/test_directory/1.pdf").st_size) assert os.stat("/tmp/test_directory/1.pdf").st_size == 69339 - - shutil.rmtree("/tmp/test_directory/") + shutil.rmtree("/tmp/test_directory/") # Two Pages test_file = pdf_util("/app/tests/sample_pdfs/sample_2_page.pdf").split_pdf_with_location("/tmp/test_directory/", False) @@ -75,9 +116,8 @@ def test_split_pdf_and_location(): assert os.stat("/tmp/test_directory/1.pdf").st_size == 1804 print(os.stat("/tmp/test_directory/2.pdf").st_size) assert os.stat("/tmp/test_directory/2.pdf").st_size == 1405 - - shutil.rmtree("/tmp/test_directory/") + shutil.rmtree("/tmp/test_directory/") # Ten Pages test_file = pdf_util("/app/tests/sample_pdfs/sample_10_page.pdf").split_pdf_with_location("/tmp/test_directory/", False) @@ -129,6 +169,88 @@ def test_split_pdf_and_location(): shutil.rmtree("/tmp/test_directory/") +def test_split_pdf_and_location_int_padding(): + # Single Pages + test_file = pdf_util("/app/tests/sample_pdfs/sample_1_page.pdf").split_pdf_with_location("/tmp/test_directory/", False, True) + print(test_file) + print(os.stat("/tmp/test_directory/sample_1_page_0001.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_1_page_0001.pdf").st_size == 69339 + + test_file = pdf_util("/app/tests/sample_pdfs/sample_1_page.pdf").split_pdf_with_location("/tmp/test_directory/", True, True) + print(test_file) + print(os.stat("/tmp/test_directory/0001.pdf").st_size) + assert os.stat("/tmp/test_directory/0001.pdf").st_size == 69339 + + shutil.rmtree("/tmp/test_directory/") + + # Two Pages + test_file = pdf_util("/app/tests/sample_pdfs/sample_2_page.pdf").split_pdf_with_location("/tmp/test_directory/", False, True) + print(test_file) + print(os.stat("/tmp/test_directory/sample_2_page_0001.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_2_page_0001.pdf").st_size == 1804 + print(os.stat("/tmp/test_directory/sample_2_page_0002.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_2_page_0002.pdf").st_size == 1405 + + # Two Pages + test_file = pdf_util("/app/tests/sample_pdfs/sample_2_page.pdf").split_pdf_with_location("/tmp/test_directory/", True, True) + print(test_file) + print(os.stat("/tmp/test_directory/0001.pdf").st_size) + assert os.stat("/tmp/test_directory/0001.pdf").st_size == 1804 + print(os.stat("/tmp/test_directory/0002.pdf").st_size) + assert os.stat("/tmp/test_directory/0002.pdf").st_size == 1405 + + shutil.rmtree("/tmp/test_directory/") + + # Ten Pages + test_file = pdf_util("/app/tests/sample_pdfs/sample_10_page.pdf").split_pdf_with_location("/tmp/test_directory/", False, True) + print(test_file) + print(os.stat("/tmp/test_directory/sample_10_page_0001.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_0001.pdf").st_size == 3167 + print(os.stat("/tmp/test_directory/sample_10_page_0002.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_0002.pdf").st_size == 2888 + print(os.stat("/tmp/test_directory/sample_10_page_0003.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_0003.pdf").st_size == 6670 + print(os.stat("/tmp/test_directory/sample_10_page_0004.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_0004.pdf").st_size == 3043 + print(os.stat("/tmp/test_directory/sample_10_page_0005.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_0005.pdf").st_size == 9968 + print(os.stat("/tmp/test_directory/sample_10_page_0006.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_0006.pdf").st_size == 5367 + print(os.stat("/tmp/test_directory/sample_10_page_0007.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_0007.pdf").st_size == 10093 + print(os.stat("/tmp/test_directory/sample_10_page_0008.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_0008.pdf").st_size == 8578 + print(os.stat("/tmp/test_directory/sample_10_page_0009.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_0009.pdf").st_size == 30188 + print(os.stat("/tmp/test_directory/sample_10_page_0010.pdf").st_size) + assert os.stat("/tmp/test_directory/sample_10_page_0010.pdf").st_size == 3789 + + test_file = pdf_util("/app/tests/sample_pdfs/sample_10_page.pdf").split_pdf_with_location("/tmp/test_directory/", True, True) + print(test_file) + print(os.stat("/tmp/test_directory/0001.pdf").st_size) + assert os.stat("/tmp/test_directory/0001.pdf").st_size == 3167 + print(os.stat("/tmp/test_directory/0002.pdf").st_size) + assert os.stat("/tmp/test_directory/0002.pdf").st_size == 2888 + print(os.stat("/tmp/test_directory/0003.pdf").st_size) + assert os.stat("/tmp/test_directory/0003.pdf").st_size == 6670 + print(os.stat("/tmp/test_directory/0004.pdf").st_size) + assert os.stat("/tmp/test_directory/0004.pdf").st_size == 3043 + print(os.stat("/tmp/test_directory/0005.pdf").st_size) + assert os.stat("/tmp/test_directory/0005.pdf").st_size == 9968 + print(os.stat("/tmp/test_directory/0006.pdf").st_size) + assert os.stat("/tmp/test_directory/0006.pdf").st_size == 5367 + print(os.stat("/tmp/test_directory/0007.pdf").st_size) + assert os.stat("/tmp/test_directory/0007.pdf").st_size == 10093 + print(os.stat("/tmp/test_directory/0008.pdf").st_size) + assert os.stat("/tmp/test_directory/0008.pdf").st_size == 8578 + print(os.stat("/tmp/test_directory/0009.pdf").st_size) + assert os.stat("/tmp/test_directory/0009.pdf").st_size == 30188 + print(os.stat("/tmp/test_directory/0010.pdf").st_size) + assert os.stat("/tmp/test_directory/0010.pdf").st_size == 3789 + + shutil.rmtree("/tmp/test_directory/") + + def test_merge_pdf_with(): test_file = pdf_util("/app/tests/sample_pdfs/sample_1_page.pdf").merge_pdf_with("/app/tests/sample_pdfs/sample_2_page.pdf") print(test_file) @@ -168,10 +290,12 @@ def test_merge_pdf_with_and_location(): shutil.rmtree("/tmp/test_directory/") + def test_rotate_pages(): # Write test code to verify the behavior of the rotate_pages method pass + def test_ocr_pages(): # Write test code to verify the behavior of the rotate_pages method pass -- 2.45.2 From 337e638ff007ce3ffd495b75d868a1d755be942a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20M=C3=BCller?= Date: Tue, 23 Jan 2024 16:27:40 +0100 Subject: [PATCH 6/7] converted to vue.js --- Dockerfile | 2 +- app.py | 8 ++-- init.sh | 2 +- templates/index.html | 90 +++++++++++++++++++++++++++++++++++++------- 4 files changed, 83 insertions(+), 19 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8877906..9a228d4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ - FROM python:3.11-slim +# FROM python:3.11.3 # set the working directory WORKDIR /app diff --git a/app.py b/app.py index 6770ec2..1689f03 100644 --- a/app.py +++ b/app.py @@ -1,9 +1,9 @@ import shutil import os from flask import Flask, render_template, request, redirect, jsonify, send_from_directory -from pypdf import PdfReader, PdfWriter from pathlib import Path from pdf_util.pdf_util import pdf_util +# from pdf_util.pdf_project_manager import pdf_project_manager import datetime as dt import logging @@ -44,10 +44,10 @@ def send_merge(path): @app.route('/split_to_zip', methods=['POST']) def split_to_zip(): - if 'pdf' not in request.files: + if 'pdf_1' not in request.files: return redirect(request.url) - pdf_file = request.files['pdf'] + pdf_file = request.files['pdf_1'] if pdf_file.filename == '': return redirect(request.url) @@ -63,7 +63,7 @@ def split_to_zip(): logging.debug(in_filename) logging.debug(os.path.splitext(pdf_file.filename)[0]) - + shutil.make_archive(in_filename + '_splitted', 'zip', os.path.dirname(filename) + "/split_pdf") zip_filename = in_filename + "_splitted.zip" os.rename("/app/" + zip_filename, "/app/split/" + zip_filename) diff --git a/init.sh b/init.sh index 0f7b310..f1fe207 100755 --- a/init.sh +++ b/init.sh @@ -4,6 +4,6 @@ (cd /app/ && pytest -o log_cli=true) # (cd /app/ && gunicorn -w 4 -b 0.0.0.0:8000 wsgi:app) -(cd /app/ && gunicorn --access-logfile '-' --error-logfile '-' -w 4 -b 0.0.0.0:8000 wsgi:app) # Dev (Logging to console) +(cd /app/ && gunicorn --access-logfile '-' --error-logfile '-' -w 4 -b 0.0.0.0:8000 wsgi:app --log-level debug) # Dev (Logging to console) /bin/bash \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index b603233..ff4fc8a 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,19 +1,83 @@ - + - PDF Web Toolkit - - + + + PDF Tools -

PDF Splitter

-
- -

PDF Merger

-
-
- -
+
+

PDF Splitter

+
+ + +

PDF Merger

+
+
+ + +
{{ outputMessage }}
+
+ + + + - + \ No newline at end of file -- 2.45.2 From 93b7f68b5467b043cdb10fb120672d6f40973911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20M=C3=BCller?= Date: Tue, 23 Jan 2024 17:40:03 +0100 Subject: [PATCH 7/7] make use of pdf_project_manager --- app.py | 22 ++++++++++------------ templates/index.html | 11 ++++------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/app.py b/app.py index 1689f03..256cc17 100644 --- a/app.py +++ b/app.py @@ -1,9 +1,7 @@ import shutil import os from flask import Flask, render_template, request, redirect, jsonify, send_from_directory -from pathlib import Path -from pdf_util.pdf_util import pdf_util -# from pdf_util.pdf_project_manager import pdf_project_manager +from pdf_util.pdf_project_manager import pdf_project_manager import datetime as dt import logging @@ -57,20 +55,17 @@ def split_to_zip(): filename = os.path.join(app.config['UPLOAD_FOLDER'], pdf_file.filename) pdf_file.save(filename) - # Use pdf_utils Module to split File - out_filenames = pdf_util(filename).split_pdf() - logging.debug(out_filenames) + # Use pdf_project_manager to split File + pdf_project = pdf_project_manager() + pdf_project.add_pdf(filename) logging.debug(in_filename) logging.debug(os.path.splitext(pdf_file.filename)[0]) - shutil.make_archive(in_filename + '_splitted', 'zip', os.path.dirname(filename) + "/split_pdf") + shutil.make_archive(in_filename + '_splitted', 'zip', "/app/projects/" + pdf_project.uuid + '/splitted') zip_filename = in_filename + "_splitted.zip" os.rename("/app/" + zip_filename, "/app/split/" + zip_filename) - for temp_file in out_filenames: - Path.unlink(temp_file) - response = jsonify({"url": "/split/" + zip_filename, "name": zip_filename}) # response.headers.add("Access-Control-Allow-Origin", "*") return response @@ -99,8 +94,11 @@ def merge_to_pdf(): logging.debug(filename_1) logging.debug(filename_2) - # Use pdf_utils Module to split File - out_path = pdf_util(filename_1).merge_pdf_with(filename_2) + # Use pdf_project_manager to split File + pdf_project = pdf_project_manager() + pdf_project.add_pdf(filename_1) + pdf_project.add_pdf(filename_2) + out_path = "/app/projects/" + pdf_project.uuid + "/complete.pdf" logging.debug(out_path) os.rename(out_path, "/app/merge/merger.pdf") diff --git a/templates/index.html b/templates/index.html index ff4fc8a..4dd0ff5 100644 --- a/templates/index.html +++ b/templates/index.html @@ -16,26 +16,23 @@

- -
{{ outputMessage }}
- +