Compare commits
20 Commits
11387d34cc
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
91fd586a07 | ||
|
5d41b451cf | ||
|
155b28cb46 | ||
|
dbe862cf53 | ||
|
56bd031229 | ||
|
34314fe90c | ||
|
152efedf7a | ||
|
7a5698467f | ||
|
acf98f1387 | ||
|
6c99e372a9 | ||
|
464e8f87ab | ||
|
416c58ceb0 | ||
|
fbf34ffcdb | ||
|
8fec12133b | ||
|
53f40ef725 | ||
|
a96c18cb9c | ||
|
409c14dd56 | ||
|
7986ab6bab | ||
|
16d67c49f4 | ||
|
e8be969e98 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
.idea
|
.idea
|
||||||
csvmarp
|
csvmarp
|
||||||
|
csvconvertervenv
|
||||||
|
|
||||||
# ---> Python
|
# ---> Python
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
|
69
README.md
69
README.md
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
multiple-choice-quiz-csv-to-marp-markdown-slides-converter
|
multiple-choice-quiz-csv-to-marp-markdown-slides-converter
|
||||||
|
|
||||||
|
this tool can convert a csv file into a [Marp](https://marpit.marp.app/) markdown slides, which can subsequently be converted into a pdf file.
|
||||||
|
|
||||||
csv needs to have the following format
|
csv needs to have the following format
|
||||||
````
|
````
|
||||||
@@ -10,10 +11,74 @@ Kategorie 1: Zitate ( Wer hat's gesagt?),,,,
|
|||||||
````
|
````
|
||||||
Strings surrounded with quotation marks
|
Strings surrounded with quotation marks
|
||||||
|
|
||||||
|
The [marp-cli](https://github.com/marp-team/marp-cli) can be used to convert the md to pdf:
|
||||||
|
- Stand alone linux package https://github.com/marp-team/marp-cli/releases
|
||||||
|
- run as server with PORT=5000 marp -s ./slides
|
||||||
|
- requires wget for download
|
||||||
|
- requires chrome to convert to pdf
|
||||||
|
- docker container https://hub.docker.com/r/marpteam/marp-cli/
|
||||||
|
`docker run --rm --init -v $PWD:/home/marp/app/ -e LANG=$LANG marpteam/marp-cli slide-deck.md --pdf`
|
||||||
|
- Run marp cli in its own docker container in server mode!!!
|
||||||
|
- output folder as volumne, so that marp container can assess it
|
||||||
|
|
||||||
docker run --rm --init -v $PWD:/home/marp/app/ -e LANG=$LANG marpteam/marp-cli slide-deck.md --pdf
|
powerpoint export is also possible with --pptx
|
||||||
|
|
||||||
Script to run:
|
- use config
|
||||||
|
- run using cli command when in prod mdoe (inside docker)
|
||||||
|
- run using docker when in dev mode
|
||||||
|
|
||||||
|
Steps:
|
||||||
1. csv -> marp markdown converter
|
1. csv -> marp markdown converter
|
||||||
2. md -> pdf conversion using mark cli docker image
|
2. md -> pdf conversion using mark cli docker image
|
||||||
|
|
||||||
|
file upload based on
|
||||||
|
https://pythonise.com/series/learning-flask/flask-uploading-files
|
||||||
|
|
||||||
|
python docker wait for container
|
||||||
|
|
||||||
|
|
||||||
|
CLI binary:
|
||||||
|
|
||||||
|
convert file
|
||||||
|
./marp --pdf ./slides/slide-deck.md
|
||||||
|
./marp --pdf ./slides/slide-deck.md
|
||||||
|
/opt/marp --pdf /code/csv-to-marp-converter/
|
||||||
|
|
||||||
|
start server
|
||||||
|
./marp --pdf --server ./slides/
|
||||||
|
PORT=4100 /opt/marp-cli --server ./
|
||||||
|
|
||||||
|
|
||||||
|
use marp server:
|
||||||
|
- link or embed marp server path (http://localhost:8080/) from result html
|
||||||
|
- Dockerfile
|
||||||
|
- download latest marp binary
|
||||||
|
- start server (on correct port)
|
||||||
|
|
||||||
|
wget -O marp-cli.tar.gz https://github.com/marp-team/marp-cli/releases/download/v1.1.1/marp-cli-v1.1.1-linux.tar.gz
|
||||||
|
|
||||||
|
|
||||||
|
when running release version from
|
||||||
|
|
||||||
|
[ WARN ] The environment variable CHROME_PATH must be set to executable of a build of Chromium version 54.0 or later.
|
||||||
|
[ ERROR ] Failed converting Markdown. (You have to install Google Chrome, Chromium, or Microsoft Edge to convert slide deck with current options.)
|
||||||
|
|
||||||
|
|
||||||
|
when running cli with docker
|
||||||
|
docker run -v $PWD:/home/marp/app/ -e LANG=$LANG marpteam/marp-cli quiz-slides-template.md --pdf
|
||||||
|
|
||||||
|
[ ERROR ] Failed converting Markdown. (EACCES: permission denied, open
|
||||||
|
'/home/marp/app/quiz-slides-template.pdf')
|
||||||
|
|
||||||
|
chmod -R 0777 output/
|
||||||
|
|
||||||
|
docker run -v $PWD:/home/marp/app/ -e LANG=$LANG -e PORT=4100 -p 4100:4100 marpteam/marp-cli --server ./ -d
|
||||||
|
|
||||||
|
docker run -d -v /home/marcelo/git/docker-python-multi-service/csv-to-marp-converter/output:/home/marp/app/ -e LANG=$LANG -e PORT=4100 -p 4100:4100 marpteam/marp-cli --server ./
|
||||||
|
|
||||||
|
run on NAS:
|
||||||
|
sudo docker run -d -v /volume1/docker/dockerpython/docker-python-multi-service/csv-marp-converter-output:/home/marp/app/ -e LANG=$LANG -e PORT=4100 -p 4100:4100 --name marp marpteam/marp-cli --server -I ./
|
||||||
|
- Folder that is mounted should not be empty!!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -10,13 +10,15 @@ def get_parser():
|
|||||||
# required arg
|
# required arg
|
||||||
parser.add_argument("inputFile", help="name of the csv file to used as input, e.g. quiz.csv",
|
parser.add_argument("inputFile", help="name of the csv file to used as input, e.g. quiz.csv",
|
||||||
type=str)
|
type=str)
|
||||||
|
parser.add_argument('-pdf', '--pdf', action='store_true',
|
||||||
|
help="converts markdown file to pdf")
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def command_line_runner():
|
def command_line_runner():
|
||||||
parser = get_parser()
|
parser = get_parser()
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
converter.write_markdown_file(args.inputFile)
|
converter.convert(args.inputFile, args.pdf)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
7
config.yml
Normal file
7
config.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
auth:
|
||||||
|
username: admin
|
||||||
|
hashedPassword: pbkdf2:sha256:260000$2xn3u8v9EFHM7oj1$65a9126ae01129a8adc5ca74ec6c006388f3d7f0176d015dccf8e0bf1f5e2523
|
||||||
|
prod: False
|
||||||
|
basePath: './'
|
||||||
|
outputPath: './output'
|
||||||
|
uploadPath: './upload'
|
23
config_parser.py
Normal file
23
config_parser.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import yaml
|
||||||
|
import os
|
||||||
|
|
||||||
|
DEFAULT_CONFIG_FILE_NAME = "config.yml"
|
||||||
|
|
||||||
|
if os.environ['ENV'] == 'prod':
|
||||||
|
config_file_to_load = "config_prod.yml"
|
||||||
|
else:
|
||||||
|
config_file_to_load = DEFAULT_CONFIG_FILE_NAME
|
||||||
|
|
||||||
|
# BASE_FODLER = "/code/markdowntagebuch"
|
||||||
|
BASE_FODLER = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
path_to_config = os.path.join(BASE_FODLER, config_file_to_load)
|
||||||
|
print("opening config file " + path_to_config)
|
||||||
|
with open(path_to_config, "r") as ymlfile:
|
||||||
|
cfg = yaml.load(ymlfile, Loader=yaml.FullLoader)
|
||||||
|
|
||||||
|
credentials = cfg["auth"]
|
||||||
|
prod_mode = cfg["prod"]
|
||||||
|
base_path = cfg["basePath"]
|
||||||
|
output_path = cfg["outputPath"]
|
||||||
|
upload_path = cfg["uploadPath"]
|
7
config_prod.yml
Normal file
7
config_prod.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
auth:
|
||||||
|
username: admin
|
||||||
|
hashedPassword: pbkdf2:sha256:260000$2xn3u8v9EFHM7oj1$65a9126ae01129a8adc5ca74ec6c006388f3d7f0176d015dccf8e0bf1f5e2523
|
||||||
|
prod: True
|
||||||
|
basePath: './csv-to-marp-converter'
|
||||||
|
outputPath: './csv-to-marp-converter/output'
|
||||||
|
uploadPath: './csv-to-marp-converter/upload'
|
60
converter.py
60
converter.py
@@ -1,15 +1,12 @@
|
|||||||
import calendar
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import config_parser
|
||||||
|
|
||||||
|
|
||||||
# def file_exists(filename):
|
|
||||||
# if os.path.exists(filename):
|
|
||||||
# print('Note: Diary \'' + filename + '\' already exists. Will not be modified.')
|
|
||||||
# return True
|
|
||||||
# return False
|
|
||||||
NUMBER_OF_ANSWERS = 5
|
NUMBER_OF_ANSWERS = 5
|
||||||
|
|
||||||
|
OUTPUT_FODLER = config_parser.output_path
|
||||||
|
BASE_FODLER = config_parser.base_path
|
||||||
|
|
||||||
|
|
||||||
class QuestionAndAnswers:
|
class QuestionAndAnswers:
|
||||||
def __init__(self, question, answerArray, category):
|
def __init__(self, question, answerArray, category):
|
||||||
@@ -21,21 +18,37 @@ class QuestionAndAnswers:
|
|||||||
return "QuestionAndAnswers: Question: {}, Answers: {}, Category: {}".format(self.question, self.answerArray, self.category)
|
return "QuestionAndAnswers: Question: {}, Answers: {}, Category: {}".format(self.question, self.answerArray, self.category)
|
||||||
|
|
||||||
|
|
||||||
|
def convert(csv_path, output_pdf=False):
|
||||||
|
write_markdown_file(csv_path)
|
||||||
|
if (output_pdf):
|
||||||
|
to_pdf("slide-deck.md")
|
||||||
|
|
||||||
|
|
||||||
def write_markdown_file(csv_path):
|
def write_markdown_file(csv_path):
|
||||||
# csv_path = "quiz-example.csv"
|
# csv_path = "quiz-example.csv"
|
||||||
markdown_path = "slide-deck.md"
|
# OUTPUT_FODLER = "output"
|
||||||
|
|
||||||
|
if not os.path.exists(OUTPUT_FODLER):
|
||||||
|
os.makedirs(OUTPUT_FODLER)
|
||||||
|
|
||||||
|
markdown_filename = "slide-deck.md"
|
||||||
|
markdown_path = os.path.join(OUTPUT_FODLER, markdown_filename)
|
||||||
|
|
||||||
# read CSV
|
# read CSV
|
||||||
list_of_q_and_as = []
|
list_of_q_and_as = []
|
||||||
|
|
||||||
with open(csv_path, 'r', encoding='UTF-8') as csv_file:
|
with open(csv_path, 'r', encoding='UTF-8') as csv_file:
|
||||||
data = csv_file.readlines()
|
data = csv_file.readlines()
|
||||||
current_category = "bla"
|
current_category = ""
|
||||||
for line in data:
|
for line in data:
|
||||||
if line.startswith("Kategorie"):
|
if line.startswith("Kategorie"):
|
||||||
name_of_category = line.split(",")[0]
|
name_of_category = line.split(",")[0]
|
||||||
print("name_of_category")
|
# print("name_of_category")
|
||||||
print(name_of_category)
|
# print(name_of_category)
|
||||||
current_category = name_of_category
|
current_category = name_of_category
|
||||||
|
# do nothing if row is empty
|
||||||
|
elif line.split(",")[0] is "":
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
handle_q_and_a_row(line, list_of_q_and_as, current_category)
|
handle_q_and_a_row(line, list_of_q_and_as, current_category)
|
||||||
# break
|
# break
|
||||||
@@ -43,7 +56,11 @@ def write_markdown_file(csv_path):
|
|||||||
# static_part = ""
|
# static_part = ""
|
||||||
# copy template
|
# copy template
|
||||||
template_name = "quiz-slides-template.md"
|
template_name = "quiz-slides-template.md"
|
||||||
shutil.copyfile(template_name, markdown_path)
|
template_path = os.path.join(BASE_FODLER, template_name)
|
||||||
|
# print("template_path")
|
||||||
|
# print(template_path)
|
||||||
|
|
||||||
|
shutil.copyfile(template_path, markdown_path)
|
||||||
|
|
||||||
# with open(markdown_path, 'w', encoding='UTF-8') as markdown_file:
|
# with open(markdown_path, 'w', encoding='UTF-8') as markdown_file:
|
||||||
# append questions and answers to copy of template
|
# append questions and answers to copy of template
|
||||||
@@ -51,7 +68,7 @@ def write_markdown_file(csv_path):
|
|||||||
# write month headline
|
# write month headline
|
||||||
# markdown_file.writelines(static_part)
|
# markdown_file.writelines(static_part)
|
||||||
for q_and_a in list_of_q_and_as:
|
for q_and_a in list_of_q_and_as:
|
||||||
print(q_and_a)
|
# print(q_and_a)
|
||||||
write_q_and_a_to_markdown_file(markdown_file, q_and_a)
|
write_q_and_a_to_markdown_file(markdown_file, q_and_a)
|
||||||
|
|
||||||
markdown_file.close()
|
markdown_file.close()
|
||||||
@@ -73,22 +90,23 @@ def write_q_and_a_to_markdown_file(markdown_file, q_and_a):
|
|||||||
alpha = alphabet[i]
|
alpha = alphabet[i]
|
||||||
markdown_file.writelines(' - {}) {}'.format(alpha, q_and_a.answerArray[i]))
|
markdown_file.writelines(' - {}) {}'.format(alpha, q_and_a.answerArray[i]))
|
||||||
|
|
||||||
markdown_file.writelines('---')
|
markdown_file.writelines('\n')
|
||||||
|
markdown_file.writelines('---') # TODO leave out for the last page?
|
||||||
return markdown_file
|
return markdown_file
|
||||||
|
|
||||||
|
|
||||||
def handle_q_and_a_row(line, list_of_q_and_as, category):
|
def handle_q_and_a_row(line, list_of_q_and_as, category):
|
||||||
column_data = line.split(',')
|
column_data = line.split(',')
|
||||||
print(column_data)
|
# print(column_data) # causes UnicodeEncodeError: 'ascii' codec can't encode character '\xf6' in position 49: ordinal not in range(128) in linux
|
||||||
# check size of array
|
# check size of array
|
||||||
print(len(column_data))
|
# print(len(column_data))
|
||||||
if len(column_data) is NUMBER_OF_ANSWERS:
|
if len(column_data) is NUMBER_OF_ANSWERS:
|
||||||
question = column_data[0]
|
question = column_data[0]
|
||||||
answers = column_data[1:] # select all elements from list except first
|
answers = column_data[1:] # select all elements from list except first
|
||||||
# answers = column_data[-3:]
|
# answers = column_data[-3:]
|
||||||
q_and_a = QuestionAndAnswers(question, answers, category)
|
q_and_a = QuestionAndAnswers(question, answers, category)
|
||||||
list_of_q_and_as.append(q_and_a)
|
list_of_q_and_as.append(q_and_a)
|
||||||
print(q_and_a)
|
# print(q_and_a)
|
||||||
return list_of_q_and_as
|
return list_of_q_and_as
|
||||||
|
|
||||||
|
|
||||||
@@ -97,10 +115,14 @@ def handle_q_and_a_row(line, list_of_q_and_as, category):
|
|||||||
def to_pdf(filename):
|
def to_pdf(filename):
|
||||||
import docker
|
import docker
|
||||||
client = docker.from_env()
|
client = docker.from_env()
|
||||||
|
|
||||||
current_dir = os.getcwd()
|
current_dir = os.getcwd()
|
||||||
print(current_dir)
|
# OUTPUT_FODLER = "output"
|
||||||
|
output_folder_path = os.path.join(current_dir, OUTPUT_FODLER)
|
||||||
|
print(output_folder_path)
|
||||||
|
|
||||||
# https://docker-py.readthedocs.io/en/stable/containers.html
|
# https://docker-py.readthedocs.io/en/stable/containers.html
|
||||||
client.containers.run('marpteam/marp-cli', '{} --pdf'.format(filename), volumes=[current_dir + ':/home/marp/app/'],
|
client.containers.run('marpteam/marp-cli', '{} --pdf'.format(filename), volumes=[output_folder_path + ':/home/marp/app/'],
|
||||||
name="marp", detach=True, remove=True, init=True)
|
name="marp", detach=True, remove=True, init=True)
|
||||||
|
|
||||||
container = client.containers.get('marp')
|
container = client.containers.get('marp')
|
||||||
|
@@ -1,8 +1,23 @@
|
|||||||
certifi==2021.5.30
|
certifi==2021.5.30
|
||||||
chardet==4.0.0
|
chardet==4.0.0
|
||||||
|
click==8.0.1
|
||||||
|
colorama==0.4.4
|
||||||
docker==5.0.0
|
docker==5.0.0
|
||||||
idna==2.10
|
Flask==2.0.1
|
||||||
pywin32==227
|
Flask-Cors==3.0.10
|
||||||
|
Flask-HTTPAuth==4.3.0
|
||||||
|
idna==3.2
|
||||||
|
importlib-metadata==4.5.0
|
||||||
|
itsdangerous==2.0.1
|
||||||
|
Jinja2==3.0.1
|
||||||
|
MarkupSafe==2.0.1
|
||||||
requests==2.25.1
|
requests==2.25.1
|
||||||
|
six==1.16.0
|
||||||
|
typing-extensions==3.10.0.0
|
||||||
urllib3==1.26.5
|
urllib3==1.26.5
|
||||||
websocket-client==1.1.0
|
websocket-client==1.1.0
|
||||||
|
Werkzeug==2.0.1
|
||||||
|
zipp==3.4.1
|
||||||
|
PyYAML==5.4.1
|
||||||
|
# pywin32==301 # needed for docker error messages
|
||||||
|
# pywin32==227 # produces error during pip install while building docker image
|
||||||
|
119
rest_server.py
Normal file
119
rest_server.py
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from flask import Flask
|
||||||
|
from flask import render_template
|
||||||
|
from flask import request, send_from_directory
|
||||||
|
from flask_cors import CORS
|
||||||
|
from flask_httpauth import HTTPBasicAuth
|
||||||
|
from werkzeug.security import check_password_hash
|
||||||
|
from werkzeug.utils import redirect
|
||||||
|
|
||||||
|
import converter
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
auth = HTTPBasicAuth()
|
||||||
|
app.config["DEBUG"] = True
|
||||||
|
CORS(app)
|
||||||
|
|
||||||
|
URL_BASE_PATH = "/"
|
||||||
|
|
||||||
|
import config_parser
|
||||||
|
|
||||||
|
UPLOAD_FODLER = config_parser.upload_path
|
||||||
|
OUTPUT_FODLER = config_parser.output_path
|
||||||
|
|
||||||
|
|
||||||
|
@auth.verify_password
|
||||||
|
def verify_password(username, password):
|
||||||
|
if username == config_parser.credentials['username'] and \
|
||||||
|
check_password_hash(config_parser.credentials['hashedPassword'], password):
|
||||||
|
return username
|
||||||
|
|
||||||
|
|
||||||
|
@app.route(URL_BASE_PATH, methods=["GET", "POST"])
|
||||||
|
@auth.login_required
|
||||||
|
def upload_image():
|
||||||
|
if request.method == "POST":
|
||||||
|
if request.files:
|
||||||
|
csv_file = request.files["csv"]
|
||||||
|
print(csv_file)
|
||||||
|
|
||||||
|
if not valid_csv(csv_file.filename):
|
||||||
|
# TODO give some feedback to user
|
||||||
|
return redirect(request.url)
|
||||||
|
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
|
filename = secure_filename(csv_file.filename)
|
||||||
|
output_file_path = os.path.join(UPLOAD_FODLER, filename)
|
||||||
|
|
||||||
|
if not os.path.exists(UPLOAD_FODLER):
|
||||||
|
os.makedirs(UPLOAD_FODLER)
|
||||||
|
|
||||||
|
# TODO save to different path in Porduction!
|
||||||
|
csv_file.save(output_file_path)
|
||||||
|
|
||||||
|
# run converter
|
||||||
|
if config_parser.prod_mode:
|
||||||
|
converter.convert(output_file_path, False)
|
||||||
|
else:
|
||||||
|
converter.convert(output_file_path, True)
|
||||||
|
|
||||||
|
return render_template("return_result.html")
|
||||||
|
# return redirect(request.url)
|
||||||
|
|
||||||
|
return render_template("upload-file.html")
|
||||||
|
|
||||||
|
|
||||||
|
def valid_csv(filename):
|
||||||
|
# We only want files with a . in the filename
|
||||||
|
if not "." in filename:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Split the extension from the filename
|
||||||
|
ext = filename.rsplit(".", 1)[1]
|
||||||
|
return ext == 'csv'
|
||||||
|
|
||||||
|
|
||||||
|
def get_marp_url():
|
||||||
|
if config_parser.prod_mode:
|
||||||
|
return "https://marp.swaghausen.de"
|
||||||
|
else:
|
||||||
|
return "http://localhost:4100"
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/marp')
|
||||||
|
def redirect_to_marp():
|
||||||
|
return redirect(get_marp_url(), code=302)
|
||||||
|
# return get_marp_url()
|
||||||
|
|
||||||
|
|
||||||
|
@app.route(URL_BASE_PATH + 'download', methods=['GET'])
|
||||||
|
# @auth.login_required
|
||||||
|
def download_file():
|
||||||
|
"""Download the .md file."""
|
||||||
|
# print("sending file with name: " + filename)
|
||||||
|
current_dir = os.getcwd()
|
||||||
|
full_dir = os.path.join(current_dir, OUTPUT_FODLER)
|
||||||
|
print("full_dir: " + full_dir)
|
||||||
|
return send_from_directory(full_dir, "slide-deck.md", as_attachment=True)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route(URL_BASE_PATH + 'download-pdf', methods=['GET'])
|
||||||
|
# @auth.login_required
|
||||||
|
def download_pdf_file():
|
||||||
|
"""Download the .pdf file."""
|
||||||
|
# print("sending file with name: " + filename)
|
||||||
|
current_dir = os.getcwd()
|
||||||
|
full_dir = os.path.join(current_dir, OUTPUT_FODLER)
|
||||||
|
print("full_dir: " + full_dir)
|
||||||
|
return send_from_directory(full_dir, "slide-deck.pdf", as_attachment=True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
flask_options = dict(
|
||||||
|
host='0.0.0.0',
|
||||||
|
debug=True,
|
||||||
|
port=4003,
|
||||||
|
threaded=True,
|
||||||
|
)
|
||||||
|
app.run(**flask_options)
|
35
templates/base.html
Normal file
35
templates/base.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
||||||
|
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>{% block title %} {% endblock %}</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<h1 style="color: blue">{% block pageHeader %} {% endblock %}</h1>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<!-- <p>This is an HTML file served up by Flask</p>-->
|
||||||
|
<p>{% block content %} {% endblock %}</p>
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
|
||||||
|
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
|
||||||
|
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
|
||||||
|
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
16
templates/return_result.html
Normal file
16
templates/return_result.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block pageHeader %} CSV to marp Markdown Converter {% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Result</h2>
|
||||||
|
|
||||||
|
<!--<textarea rows="4" cols="50"> {{markdown_result}} </textarea>-->
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<!-- will call e.g. route /tagebuch/download/2021-02 -->
|
||||||
|
<a class="btn btn-primary" href="{{url_for('download_file')}}">Download Markdown File</a>
|
||||||
|
<a class="btn btn-primary" href="{{url_for('download_pdf_file')}}">Download PDF File</a>
|
||||||
|
<a class="btn btn-primary" href="{{url_for('redirect_to_marp')}}">Go To Marp Server</a>
|
||||||
|
</p>
|
||||||
|
{% endblock %}
|
33
templates/upload-file.html
Normal file
33
templates/upload-file.html
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block pageHeader %} CSV to marp Markdown Converter {% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<!--<div class="container">-->
|
||||||
|
<!-- <div class="row">-->
|
||||||
|
<!-- <div class="col">-->
|
||||||
|
|
||||||
|
<h2>Upload a CSV file</h2>
|
||||||
|
|
||||||
|
<form action="/" method="POST" enctype="multipart/form-data">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Select csv file</label>
|
||||||
|
<!-- <div class="custom-file">-->
|
||||||
|
<!-- <input type="file" class="custom-file-input" name="csv" id="csv">-->
|
||||||
|
<!-- <label class="custom-file-label" for="csv">Select csv...</label>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<div class="file-upload-wrapper">
|
||||||
|
<input type="file" id="input-file-max-fs" class="file-upload" name="csv" data-max-file-size="2M"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">Upload</button>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
24
test_data/quiz-example-with-category-and-empty-rows.csv
Normal file
24
test_data/quiz-example-with-category-and-empty-rows.csv
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
Kategorie 1: Zitate ( Wer hat's gesagt?),,,,
|
||||||
|
(1) Probleme sind nur Dorninge Chancen,Jan Böhmermann,Christian Lindner,Elon Musk,Pable Escobar
|
||||||
|
(2) Niemand hat die Absicht eine Mauer zu errichten,Erich Honecker,Lothar de Maizière,Walter Ulbricht,Donald Trump
|
||||||
|
(3) Auf den Alkohol – den Ursprung und die Lösung sämtlicher Lebensprobleme,Boris Jelzin,Homer Simpson,Prinz Harry,Charlie Sheen
|
||||||
|
(4) Ich so zu mein homie: ich komme später, ich steck noch im Verkehr Er: mit dem Auto? Ich: nein in 1 bitch. Geschlechtsverkehr!,Moneyboy,Oliver Pocher,Manny Marc,Dieter Bohlen
|
||||||
|
(5) Aus großer Macht folgt große Verantwortung.,Neil Armstrong,Jesus Christus,Chuck Norris,Ben Parker aus Spiderman
|
||||||
|
(6) Wenn ich über steuer- und erbrechtliche Anerkennung von homosexuellen Paaren diskutiere, dann kann ich gleich über Teufelsanbetung diskutieren.,Rainer Maria Woelki,Papst Benedikt XVI,Friedrich Merz,Edmund Stoiber
|
||||||
|
(7) Mir hat auch niemand gesagt, wie man Kapitalist wird.,Dagobert Duck,Christian Lindner,Jeff Bezos,Queen Elisabeth II.
|
||||||
|
(8) Geh dein Weg, leb dein Leben, sei du selbst, Fick deine Mutter,Money Boy,Farid Bang,KIZ (wer von denen?),Immanuel Kant
|
||||||
|
(9) Man muss Gesetze kompliziert machen. Dann fällt es nicht so auf.,Horst Seehofer,Wladimir Putin,Erich Honecker,Boris Johnson
|
||||||
|
(10) Chef sein ist wie ein Wecker. Keiner will ihn, jeder hasst ihn, aber wenn er nicht da ist, dann machen alle Schnarch.,Paul Ditter,Bernd Stromberg,Markus Stockschläder,Jürgen Klopp
|
||||||
|
,,,,
|
||||||
|
Kategorie 2: Unnützes Wissen,,,,
|
||||||
|
(1) Was wird als Ursache für die beschleunigte Expansion des Universums vermutet?,Antimaterie,Dunkle Materie,Dunkle Energie,Schwarze Magie
|
||||||
|
(2) Welches historische Ereignis fand im Jahr 216 v. Chr. statt?,Die Geburt Christi,Schlacht von Cannae,Der 1. Kreuzzug,Beginn des Maya Kalenders
|
||||||
|
(3) Welches Konzept wurde mit Java Version 5 eingeführt,Lambda Ausdrücke ,polymorphe Methodenaufrufe,Annotation Processing,Dynamische Typisierung
|
||||||
|
(4) Wer oder Was ist Shimano?,Eine Japanisches Nudelgericht,Ein Sumowrestler,Ein Hersteller von Fahrradschaltungen,Ein Beuteltier
|
||||||
|
(5) Wie schnell können Hummeln fliegen?,20 km/h,5 km/h,1 km/h,20 m/s
|
||||||
|
(6) Was ist eine Boomer Remover?,"KI, die Jobs übernimmt",Ein Bombenentschärfer,Ein Geologe,Ein Musiker
|
||||||
|
(7) Was ist kein Boomer?,Ein Superinfizierter aus L4D,Ein wirtschaftlich orientierter Spieler in AoE,Eine nervige Person der Jahrgänge 50-65,Ein Vulkan
|
||||||
|
(8) Welches Element schmilzt erst bei 3422 °C ?,Titan,Wolfram,Plutonium,Kupfer
|
||||||
|
(9) Wer oder was ist ein Monopson?,Eine vom aussterben bedrohte Eichhörnchenart,Ein Nachfragemonopol in der BWL,Ein Elementarteilchen,Ein Rugbyspieler
|
||||||
|
(10) Was ist die 42?,"Die Antwort auf den Sinn des Lebens, Universums und des Restes",Eine Primzahl,Die Dauer des Maya-Kalenders,Die Luftlinie zwischen Bremen und Hamburg [km]
|
||||||
|
,,,,
|
Can't render this file because it has a wrong number of fields in line 5.
|
61
tests.py
61
tests.py
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
import shutil
|
from time import sleep
|
||||||
|
|
||||||
import converter
|
import converter
|
||||||
|
|
||||||
@@ -10,42 +10,55 @@ class TestDiary(unittest.TestCase):
|
|||||||
|
|
||||||
# BASE_FODLER = "test_data"
|
# BASE_FODLER = "test_data"
|
||||||
|
|
||||||
# def test_basic_csv_input(self):
|
def test_basic_csv_input(self):
|
||||||
# # python command_line 2021-01
|
# python command_line 2021-01
|
||||||
# BASE_FODLER = "test_data"
|
BASE_FODLER = "test_data"
|
||||||
# converter.write_markdown_file('quiz-example.csv')
|
OUTPUT_FODLER = "output"
|
||||||
# # assert file was created
|
|
||||||
# assert(os.path.exists(os.path.join(BASE_FODLER, "slide-deck.md")))
|
file_path = os.path.join(BASE_FODLER, "quiz-example.csv")
|
||||||
|
converter.write_markdown_file(file_path)
|
||||||
|
# assert file was created
|
||||||
|
assert (os.path.exists(os.path.join(OUTPUT_FODLER, "slide-deck.md")))
|
||||||
|
|
||||||
def test_csv_input_with_categories(self):
|
def test_csv_input_with_categories(self):
|
||||||
# python command_line 2021-01
|
# python command_line 2021-01
|
||||||
BASE_FODLER = "test_data"
|
BASE_FODLER = "test_data"
|
||||||
converter.write_markdown_file('quiz-example-with-category.csv')
|
OUTPUT_FODLER = "output"
|
||||||
|
|
||||||
|
file_path = os.path.join(BASE_FODLER, "quiz-example-with-category.csv")
|
||||||
|
# converter.write_markdown_file('test_data/quiz-example-with-category.csv')
|
||||||
|
converter.write_markdown_file(file_path)
|
||||||
# assert file was created
|
# assert file was created
|
||||||
assert(os.path.exists(os.path.join(BASE_FODLER, "slide-deck.md")))
|
assert (os.path.exists(os.path.join(OUTPUT_FODLER, "slide-deck.md")))
|
||||||
|
|
||||||
|
def test_csv_input_with_categories_and_empty_rows(self):
|
||||||
|
# python command_line 2021-01
|
||||||
|
BASE_FODLER = "test_data"
|
||||||
|
OUTPUT_FODLER = "output"
|
||||||
|
|
||||||
|
file_path = os.path.join(BASE_FODLER, "quiz-example-with-category-and-empty-rows.csv")
|
||||||
|
converter.write_markdown_file(file_path)
|
||||||
|
# assert file was created
|
||||||
|
assert (os.path.exists(os.path.join(OUTPUT_FODLER, "slide-deck.md")))
|
||||||
|
|
||||||
def test_to_pdf(self):
|
def test_to_pdf(self):
|
||||||
# python command_line 2021-01
|
# python command_line 2021-01
|
||||||
BASE_FODLER = "test_data"
|
OUTPUT_FODLER = "output"
|
||||||
converter.to_pdf("slide-deck.md")
|
converter.to_pdf("slide-deck.md")
|
||||||
|
sleep(5) # wait for docker container to finish conversion
|
||||||
|
|
||||||
# assert file was created
|
# assert file was created
|
||||||
assert(os.path.exists(os.path.join(BASE_FODLER, "slide-deck.md")))
|
assert (os.path.exists(os.path.join(OUTPUT_FODLER, "slide-deck.pdf")))
|
||||||
|
|
||||||
# def test_create_month_file_january(self):
|
def setUp(self):
|
||||||
# # python command_line 2021-01
|
print("test started...")
|
||||||
# BASE_FODLER = "test_data"
|
BASE_FODLER = "test_data"
|
||||||
# diary.parse_input('2021-01', BASE_FODLER)
|
OUTPUT_FODLER = "output"
|
||||||
# # assert file was created
|
|
||||||
# assert(os.path.exists(os.path.join(BASE_FODLER, "01_2021.md")))
|
|
||||||
|
|
||||||
|
if not os.path.exists(BASE_FODLER):
|
||||||
# def setUp(self):
|
os.makedirs(BASE_FODLER)
|
||||||
# print("test started...")
|
if not os.path.exists(OUTPUT_FODLER):
|
||||||
# BASE_FODLER = "test_data"
|
os.makedirs(OUTPUT_FODLER)
|
||||||
# # bla = os.path.join(BASE_FODLER, "12_2020.md")
|
|
||||||
# if not os.path.exists(BASE_FODLER):
|
|
||||||
# os.makedirs(BASE_FODLER)
|
|
||||||
|
|
||||||
# def tearDown(self):
|
# def tearDown(self):
|
||||||
# # pass
|
# # pass
|
||||||
|
Reference in New Issue
Block a user