Compare commits

..

16 Commits

Author SHA1 Message Date
dechert
91fd586a07 fix 2021-06-21 12:25:54 +02:00
dechert
5d41b451cf cleanup 2021-06-21 12:22:15 +02:00
dechert
155b28cb46 Merge remote-tracking branch 'origin/master' 2021-06-21 12:12:54 +02:00
dechert
dbe862cf53 use basic auth 2021-06-21 12:12:44 +02:00
dechert
56bd031229 notes on nas deployment 2021-06-18 16:36:32 +02:00
dechert
34314fe90c button to redirect to marp server 2021-06-18 15:50:40 +02:00
dechert
152efedf7a disable pdf export via rest in prod mode 2021-06-18 15:24:13 +02:00
dechert
7a5698467f update readme 2021-06-18 11:53:20 +02:00
dechert
acf98f1387 path to pdf 2021-06-17 20:37:29 +02:00
dechert
6c99e372a9 fix path for downlaod of md file 2021-06-17 19:42:40 +02:00
dechert
464e8f87ab base url and requirements inside docker 2021-06-17 19:16:29 +02:00
dechert
416c58ceb0 pywin version 2021-06-17 18:44:33 +02:00
dechert
fbf34ffcdb prod config for docker specific path 2021-06-17 18:40:31 +02:00
dechert
8fec12133b create folder if not exist 2021-06-16 19:32:58 +02:00
dechert
53f40ef725 complete requirements 2 2021-06-16 19:16:25 +02:00
dechert
a96c18cb9c complete requirements 2021-06-16 19:10:50 +02:00
9 changed files with 179 additions and 77 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
.idea .idea
csvmarp csvmarp
csvconvertervenv
# ---> Python # ---> Python
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files

View File

@@ -11,11 +11,74 @@ Kategorie 1: Zitate ( Wer hat's gesagt?),,,,
```` ````
Strings surrounded with quotation marks Strings surrounded with quotation marks
docker run --rm --init -v $PWD:/home/marp/app/ -e LANG=$LANG marpteam/marp-cli slide-deck.md --pdf 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
powerpoint export is also possible with --pptx powerpoint export is also possible with --pptx
- use config
- run using cli command when in prod mdoe (inside docker)
- run using docker when in dev mode
Script to run: 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!!

7
config.yml Normal file
View 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
View 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
View 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'

View File

@@ -1,13 +1,11 @@
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
BASE_FODLER = "test_data"
OUTPUT_FODLER = config_parser.output_path
BASE_FODLER = config_parser.base_path
class QuestionAndAnswers: class QuestionAndAnswers:
@@ -28,7 +26,11 @@ def convert(csv_path, output_pdf=False):
def write_markdown_file(csv_path): def write_markdown_file(csv_path):
# csv_path = "quiz-example.csv" # csv_path = "quiz-example.csv"
OUTPUT_FODLER = "output" # OUTPUT_FODLER = "output"
if not os.path.exists(OUTPUT_FODLER):
os.makedirs(OUTPUT_FODLER)
markdown_filename = "slide-deck.md" markdown_filename = "slide-deck.md"
markdown_path = os.path.join(OUTPUT_FODLER, markdown_filename) markdown_path = os.path.join(OUTPUT_FODLER, markdown_filename)
@@ -41,8 +43,8 @@ def write_markdown_file(csv_path):
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 # do nothing if row is empty
elif line.split(",")[0] is "": elif line.split(",")[0] is "":
@@ -54,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
@@ -62,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()
@@ -91,16 +97,16 @@ def write_q_and_a_to_markdown_file(markdown_file, q_and_a):
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
@@ -109,11 +115,11 @@ 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()
print(current_dir)
OUTPUT_FODLER = "output" current_dir = os.getcwd()
# OUTPUT_FODLER = "output"
output_folder_path = os.path.join(current_dir, OUTPUT_FODLER) 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=[output_folder_path + ':/home/marp/app/'], client.containers.run('marpteam/marp-cli', '{} --pdf'.format(filename), volumes=[output_folder_path + ':/home/marp/app/'],

View File

@@ -1,10 +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
six==1.16.0 # needed for docker to run properly Werkzeug==2.0.1
Flask-Cors==3.0.10 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

View File

@@ -3,41 +3,35 @@ import os
from flask import Flask from flask import Flask
from flask import render_template from flask import render_template
from flask import request, send_from_directory from flask import request, send_from_directory
from flask import url_for
from flask_cors import CORS from flask_cors import CORS
# from flask_httpauth import HTTPBasicAuth from flask_httpauth import HTTPBasicAuth
# from werkzeug.security import check_password_hash from werkzeug.security import check_password_hash
# import main
from werkzeug.utils import redirect from werkzeug.utils import redirect
# import config_parser
# from md_tagebuch import diary
import converter import converter
app = Flask(__name__) app = Flask(__name__)
# auth = HTTPBasicAuth() auth = HTTPBasicAuth()
app.config["DEBUG"] = True app.config["DEBUG"] = True
CORS(app) CORS(app)
URL_BASE_PATH = "/" URL_BASE_PATH = "/"
# URL_BASE_PATH = "/tagebuch"
# @auth.verify_password import config_parser
# def verify_password(username, password):
# if username == config_parser.credentials['username'] and \
# check_password_hash(config_parser.credentials['hashedPassword'], password):
# return username
OUTPUT_FODLER = "output" UPLOAD_FODLER = config_parser.upload_path
UPLOAD_FODLER = "upload" OUTPUT_FODLER = config_parser.output_path
# @app.route(URL_BASE_PATH + 'home', methods=['GET'])
# # @auth.login_required @auth.verify_password
# def api_show_form(): def verify_password(username, password):
# return render_template("index.html") 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"]) @app.route(URL_BASE_PATH, methods=["GET", "POST"])
@auth.login_required
def upload_image(): def upload_image():
if request.method == "POST": if request.method == "POST":
if request.files: if request.files:
@@ -51,10 +45,18 @@ def upload_image():
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
filename = secure_filename(csv_file.filename) filename = secure_filename(csv_file.filename)
output_file_path = os.path.join(UPLOAD_FODLER, 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) csv_file.save(output_file_path)
# run converter # run converter
converter.convert(output_file_path, True) 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 render_template("return_result.html")
# return redirect(request.url) # return redirect(request.url)
@@ -63,7 +65,6 @@ def upload_image():
def valid_csv(filename): def valid_csv(filename):
# We only want files with a . in the filename # We only want files with a . in the filename
if not "." in filename: if not "." in filename:
return False return False
@@ -73,37 +74,17 @@ def valid_csv(filename):
return ext == 'csv' return ext == 'csv'
def get_marp_url():
if config_parser.prod_mode:
return "https://marp.swaghausen.de"
else:
return "http://localhost:4100"
#
# @app.route(URL_BASE_PATH, methods=['POST', 'GET']) @app.route('/marp')
# # @auth.login_required def redirect_to_marp():
# def tagebuch(): return redirect(get_marp_url(), code=302)
# if request.method == "POST": # return get_marp_url()
# user_input = request.form['yearmonth']
# return redirect(url_for("return_result", yearmonth=user_input))
# else:
# return render_template("tagebuch.html")
#
#
# @app.route(URL_BASE_PATH + "<yearmonth>")
# # @auth.login_required
# def return_result(yearmonth):
# print(os.getcwd())
# filename = "slide-deck.md"
#
# print("filename to be served: " + filename)
# data = "test"
# full_file_path = os.path.join(OUTPUT_FODLER, filename)
#
# if os.path.exists(full_file_path):
# with open(full_file_path, 'r', encoding='UTF-8') as open_file:
# data = open_file.read()
# # return data
# # print(data)
# # open_file.close()
# # return f"<h1>{yearmonth}</h1>"
# # return "bla"
# return render_template("tagebuch_output.html", markdown_result=data, markdown_filename=filename)
@app.route(URL_BASE_PATH + 'download', methods=['GET']) @app.route(URL_BASE_PATH + 'download', methods=['GET'])
@@ -112,9 +93,9 @@ def download_file():
"""Download the .md file.""" """Download the .md file."""
# print("sending file with name: " + filename) # print("sending file with name: " + filename)
current_dir = os.getcwd() current_dir = os.getcwd()
full_dir = os.path.join(current_dir, "output") full_dir = os.path.join(current_dir, OUTPUT_FODLER)
print("full_dir: " + full_dir) print("full_dir: " + full_dir)
return send_from_directory(full_dir, "slide-deck.md", as_attachment=True)\ return send_from_directory(full_dir, "slide-deck.md", as_attachment=True)
@app.route(URL_BASE_PATH + 'download-pdf', methods=['GET']) @app.route(URL_BASE_PATH + 'download-pdf', methods=['GET'])
@@ -123,7 +104,7 @@ def download_pdf_file():
"""Download the .pdf file.""" """Download the .pdf file."""
# print("sending file with name: " + filename) # print("sending file with name: " + filename)
current_dir = os.getcwd() current_dir = os.getcwd()
full_dir = os.path.join(current_dir, "output") full_dir = os.path.join(current_dir, OUTPUT_FODLER)
print("full_dir: " + full_dir) print("full_dir: " + full_dir)
return send_from_directory(full_dir, "slide-deck.pdf", as_attachment=True) return send_from_directory(full_dir, "slide-deck.pdf", as_attachment=True)

View File

@@ -11,5 +11,6 @@
<!-- will call e.g. route /tagebuch/download/2021-02 --> <!-- 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_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('download_pdf_file')}}">Download PDF File</a>
<a class="btn btn-primary" href="{{url_for('redirect_to_marp')}}">Go To Marp Server</a>
</p> </p>
{% endblock %} {% endblock %}