Heroku の Flask で PDF,XLSX,ZIP を作成

JSON をアップロードして、PDF,XLSX,ZIP を作成します。

フォルダーの構造

```text
.
├── json_upload.py
├── json_read.py
├── dict_to_pdf.py
├── dict_to_xlsx.py
├── zip_gen.py
├── Procfile
├── requirements.txt
└── static
    ├── data
    └── upload.html
```

```py:json_upload.py
# -*- coding: utf-8 -*-
#
#	json_upload.py
#
#				   Nov/02/2018
#
# ------------------------------------------------------------------
from flask import Flask, request, make_response, jsonify
import os
import sys
import werkzeug

from json_read import json_read_proc
# ------------------------------------------------------------------
# flask
app = Flask(__name__)

# limit upload file size : 1MB
app.config['MAX_CONTENT_LENGTH'] = 1 * 1024 * 1024
#
#
UPLOAD_DIR="./static/data"

# ------------------------------------------------------------------
@app.route('/')
def welcome():
	return app.send_static_file('upload.html')
# ------------------------------------------------------------------
@app.route('/hello')
def hello():
	str_out = "<h2>"
	str_out += "皆さん、こんにちは<p />"
	str_out += "</h2>"
	str_out += "Nov/02/2018 PM 16:23<p />"
	return str_out

# ------------------------------------------------------------------
@app.route('/upload', methods=['POST'])
def upload_multipart():
	str_out = ""
	sys.stderr.write("*** upload_multipart *** start ***\n")
	if 'uploadFile' not in request.files:
		make_response(jsonify({'result':'uploadFile is required.'}))
#
	upload_files = request.files.getlist('uploadFile_aa')
	sys.stderr.write("len(upload_files) = %d\n" % len(upload_files))
	for file in upload_files:
		fileName = file.filename
		sys.stderr.write("fileName = " + fileName + "\n")
		file_path = os.path.join(UPLOAD_DIR, fileName)
		file.save(file_path)
		try:
			str_out += json_read_proc(UPLOAD_DIR, fileName)
		except Exception as ee:
			sys.stderr.write("*** error *** in json_read_proc ***\n")
			sys.stderr.write(str(ee) + "\n")
#
	sys.stderr.write("*** upload_multipart *** end ***\n")
#
	return str_out
#
# ------------------------------------------------------------------
@app.errorhandler(werkzeug.exceptions.RequestEntityTooLarge)
def handle_over_max_file_size(error):
	print("werkzeug.exceptions.RequestEntityTooLarge")
	return 'result : file size is overed.'

# ------------------------------------------------------------------
# main
if __name__ == "__main__":
	print(app.url_map)
	app.run(host='0.0.0.0', port=3000)
#
# ------------------------------------------------------------------
```

```py:json_read.py
# -*- coding: utf-8 -*-
#
#	json_read.py
#
#					  Nov/02/2018
#
# ------------------------------------------------------------------
import os
import sys
import json
#
from dict_to_pdf import dict_to_pdf_proc
from dict_to_xlsx import dict_to_xlsx_proc
from zip_gen import zip_gen_proc
# ------------------------------------------------------------------
# [6]:
def json_read_proc(upload_dir,file_json):
#
	str_out = ""
	path_json = os.path.join(upload_dir,file_json)
#
	json_str = file_to_str_proc(path_json)
	dict_aa = json.loads(json_str)
	try:
		str_out = table_gen_proc(dict_aa)
	except Exception as ee:
		sys.stderr.write("*** error *** read_line_proc ***\n")
		sys.stderr.write(str (ee))
#
	file_pdf = file_json.replace("json","pdf")
	path_pdf = os.path.join(upload_dir,file_pdf)
	try:
		dict_to_pdf_proc(path_pdf,dict_aa)
	except Exception as ee:
		sys.stderr.write("*** error *** dict_to_pdf_proc ***\n")
		sys.stderr.write(str (ee))
#
	file_xlsx = file_json.replace("json","xlsx")
	path_xlsx = os.path.join(upload_dir,file_xlsx)
#
	try:
		dict_to_xlsx_proc(path_xlsx,dict_aa)
	except Exception as ee:
		sys.stderr.write("*** error *** dict_to_xlsx_proc ***\n")
		sys.stderr.write(str (ee))
#
	file_zip = file_json.replace("json","zip")
	path_zip = os.path.join(upload_dir,file_zip)
	try:
		zip_gen_proc(upload_dir,file_zip,file_json,file_pdf,file_xlsx)
	except Exception as ee:
		sys.stderr.write("*** error *** zip_gen_proc ***\n")
		sys.stderr.write(str (ee))
#
	str_out += "<blockquote>"
	str_out += '<a href="' + path_pdf + '">' + file_pdf + '</a><p />'
	str_out += '<a href="' + path_xlsx + '">' + file_xlsx + '</a><p />'
	str_out += '<a href="' + path_zip + '">' + file_zip + '</a><p />'
	str_out += "</blockquote>"
#
	return	str_out
#
# ------------------------------------------------------------------
# [6-2]:
def file_to_str_proc(file_in):
	str_out = ""
	try:
		fp_in = open(file_in,encoding='utf-8')
		str_out = fp_in.read()
		fp_in.close()
	except Exception as ee:
		sys.stderr.write("*** error *** file_to_str_proc ***\n")
		sys.stderr.write(str (ee))
#
	return	str_out
# ------------------------------------------------------------------
# [6-4]:
def table_gen_proc(dict_aa):
	str_out = ""
	str_out += "<table border=1px>"
	count = 0
	for key in dict_aa.keys():
		str_out += "<tr>"
		str_out += "<td>%d</td>" % count
		str_out += "<td>" + key + "<td />"
#
		unit_aa =  dict_aa[key]
		for col in unit_aa.values():
			str_out += "<td>" + str(col) + "<td />"
		str_out += "</tr>"
#
		count += 1
#
	str_out += "</table>"
#
	return	str_out
# ------------------------------------------------------------------
```

```py:dict_to_pdf.py
# ------------------------------------------------------------------
#
#	dict_to_pdf.py
#
#						Nov/02/2018
# ------------------------------------------------------------------
import os
import sys
#
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfgen import canvas
# ------------------------------------------------------------------
def dict_to_pdf_proc(file_pdf,dict_aa):
	from reportlab.pdfgen import canvas
	fontname_g = "HeiseiKakuGo-W5"
	pdfmetrics.registerFont(UnicodeCIDFont (fontname_g))
	canvas = canvas.Canvas(file_pdf)

	canvas.setFont(fontname_g, 20)

	ypos = 700
	for key in sorted(dict_aa.keys()):
		unit = dict_aa[key]
		canvas.drawString(50,ypos,key)
		canvas.drawString(185,ypos,unit['name'])
		canvas.drawString(285,ypos,str (unit['population']))
		canvas.drawString(400,ypos,unit['date_mod'])
		ypos = ypos - 25
#
	canvas.save()
# ------------------------------------------------------------------
```

```py:dict_to_xlsx.py
# ------------------------------------------------------------------
#
#	dict_to_xlsx.py
#
#					Nov/02/2018
# ------------------------------------------------------------------
import sys
#
from openpyxl import Workbook
# ------------------------------------------------------------------
def dict_to_xlsx_proc (xlsx_file,dict_aa):
	wb = Workbook()
	ws = wb.worksheets[0]
	ws.title = "Cities"
	print ("ws.max_row = %d" % ws.max_row)
#
	keys_list = list(dict_aa.keys())
	print ("len = %d\n" % len(keys_list)) 
#
	for it in range (len(keys_list)):
		jt = it + 1
		key = keys_list[it]
		unit_aa = dict_aa[key]
#
		ws['A%d' % jt] = key
		ws['B%d' % jt] = unit_aa['name']
		ws['C%d' % jt] = unit_aa['population']
		ws['D%d' % jt] = unit_aa['date_mod']
#
	wb.save (filename = xlsx_file)
	wb.close()
#
# ------------------------------------------------------------------
```

```py:zip_gen.py
# -*- coding: utf-8 -*-
#
#	zip_gen.py
#
#					   Nov/02/2018
#
# ------------------------------------------------------------------
import os
import sys
#
import zipfile
#
# ------------------------------------------------------------------
def zip_gen_proc(upload_dir,file_zip,file_json,file_pdf,file_xlsx):
#
	path_zip = os.path.join(upload_dir,file_zip)
	path_json = os.path.join(upload_dir,file_json)
	path_pdf = os.path.join(upload_dir,file_pdf)
	path_xlsx = os.path.join(upload_dir,file_xlsx)
#
	try:
		wzip = zipfile.ZipFile(path_zip,mode='w')
		wzip.write(path_json,file_json)
		wzip.write(path_pdf,file_pdf)
		wzip.write(path_xlsx,file_xlsx)
		wzip.close()
	except Exception as ee:
		sys.stderr.write("*** error *** in zipfile ***\n")
		sys.stderr.write(str(ee) + "\n")
#
# ------------------------------------------------------------------
```

```text:Procfile
web: gunicorn json_upload:app --log-file -
```

```text:requirements.txt
Flask==1.0.2
gunicorn==19.9.0
reportlab==3.5.9
pdfrw==0.4
openpyxl==2.5.9
```

```html:static/upload.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="CONTENT-TYPE" content="text/html; charset=utf-8" />
<title>JSON Upload</title>
</head>
<body>
<h2>JSON 変換システム</h2>
<form action="/upload" method="post" enctype="multipart/form-data"> 
	<input type="file" name="uploadFile_aa"/ multiple="multiple">
	<input type="submit" value="submit"/>
</form>
<p />
<blockquote>
json をアップロードして下さい。<p />
PDF, xlsx, zip が作成されます。<p />
</blockquote>
<p />
Nov/02/2018 PM 16:36<p />
</body>
</html>
```

ビルトインサーバーでのテスト

```bash
python json_upload.py
```

![heroku_nov0201.png](https://qiita-image-store.s3.amazonaws.com/0/179446/2e946c44-b64a-e014-e1f7-707346209148.png)

uWSGI でのテスト

```bash
uwsgi --plugin http,python --http :9090 \
	--wsgi-file json_upload.py --callable app
```

![heroku_nov0202.png](https://qiita-image-store.s3.amazonaws.com/0/179446/c92fffe8-025c-1cfd-c693-7d3063840f2d.png)


 テストに使う JSON

```json:cities.json
{
  "t0921": {
    "name": "宇都宮",
    "population": 41295,
    "date_mod": "2003-8-12"
  },
  "t0922": {
    "name": "小山",
    "population": 38756,
    "date_mod": "2003-5-15"
  },
  "t0923": {
    "name": "佐野",
    "population": 71294,
    "date_mod": "2003-6-8"
  },
  "t0924": {
    "name": "足利",
    "population": 27138,
    "date_mod": "2003-7-21"
  },
  "t0925": {
    "name": "日光",
    "population": 74682,
    "date_mod": "2003-4-19"
  },
  "t0926": {
    "name": "下野",
    "population": 82951,
    "date_mod": "2003-10-14"
  }
}
```

Heroku へのアップロード方法
[Heroku で Flask を使う](https://qiita.com/ekzemplaro/items/b25033bc03ef45938623)