Mercurial > odfcast > odfcast
comparison odfcast/convert.py @ 47:e99cbb47eafb
Improve error handling by returning flask/werkzeug responses directly on errors
author | Björn Ricks <bjoern.ricks@intevation.de> |
---|---|
date | Tue, 28 Oct 2014 10:51:42 +0100 |
parents | 6d511e93a331 |
children | 64f3e3a28bd4 |
comparison
equal
deleted
inserted
replaced
46:f270eaa7de8d | 47:e99cbb47eafb |
---|---|
8 | 8 |
9 from py3o.template import Template | 9 from py3o.template import Template |
10 | 10 |
11 from PyPDF2 import PdfFileMerger | 11 from PyPDF2 import PdfFileMerger |
12 | 12 |
13 from werkzeug.utils import escape | |
14 | |
13 log = logging.getLogger(__name__) | 15 log = logging.getLogger(__name__) |
14 | 16 |
15 ALLOWED_FORMATS = ["pdf", "doc", "docx", "odt"] | 17 ALLOWED_FORMATS = ["pdf", "doc", "docx", "odt"] |
16 | 18 |
17 PDF_MIMETYPE = "application/pdf" | 19 PDF_MIMETYPE = "application/pdf" |
20 JSON_MIMETYPE = "application/json" | |
21 HTML_MIMETYPE = "text/html" | |
18 | 22 |
19 MIMETYPES = { | 23 MIMETYPES = { |
20 "odt": "application/vnd.oasis.opendocument.text", | 24 "odt": "application/vnd.oasis.opendocument.text", |
21 "doc": "application/msword", | 25 "doc": "application/msword", |
22 "docx": "application/vnd.openxmlformats-officedocument" | 26 "docx": "application/vnd.openxmlformats-officedocument" |
25 } | 29 } |
26 | 30 |
27 DEFAULT_MIMETYPE = "application/octet-stream" | 31 DEFAULT_MIMETYPE = "application/octet-stream" |
28 | 32 |
29 | 33 |
30 class ErrorResponse(object): | 34 class ErrorResponse(Response): |
31 | 35 |
32 def __init__(self, title, details="", html_error_code=500): | 36 BAD_REQUEST_ERROR_CODE = 400 |
33 self.title = title | 37 |
34 self.details = details | 38 def __init__(self, title, error_code, details, |
35 self.html_error_code = html_error_code | 39 html_error_code=BAD_REQUEST_ERROR_CODE): |
36 | 40 data, mime_type = self.get_response_data(title, error_code, details) |
37 def __call__(self): | 41 super(ErrorResponse, self).__init__(response=data, mimetype=mime_type, |
42 status=html_error_code) | |
43 | |
44 def json(self, title, error_code, details): | |
45 return json.dumps({ | |
46 "error": title, | |
47 "error_code": error_code, | |
48 "details": details, | |
49 }), JSON_MIMETYPE | |
50 | |
51 def html(self, title, error_code, details): | |
52 data = "" | |
53 return ( | |
54 u'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n' | |
55 u'<title>%(code)s %(name)s</title>\n' | |
56 u'<h1>%(name)s</h1>\n' | |
57 u'%(details)s\n' | |
58 ) % { | |
59 "code": error_code, | |
60 "name": escape(title), | |
61 "details": escape(details), | |
62 } | |
63 return data, HTML_MIMETYPE | |
64 | |
65 def get_response_data(self, title, error_code, details): | |
38 if self.is_wants_json(): | 66 if self.is_wants_json(): |
39 return self.json() | 67 return self.json(title, error_code, details) |
40 else: | 68 return self.html(title, error_code, details) |
41 return self.html() | |
42 | |
43 def json(self): | |
44 return json.dumps({ | |
45 "error": self.title, | |
46 "details": self.details | |
47 }), self.html_error_code | |
48 | |
49 def html(self): | |
50 return self.title, self.html_error_code | |
51 | 69 |
52 def is_wants_json(self): | 70 def is_wants_json(self): |
53 best = request.accept_mimetypes.best_match(['application/json', | 71 best = request.accept_mimetypes.best_match([JSON_MIMETYPE, |
54 'text/html']) | 72 HTML_MIMETYPE]) |
55 return best == 'application/json' and \ | 73 return best == JSON_MIMETYPE and \ |
56 request.accept_mimetypes[best] > \ | 74 request.accept_mimetypes[best] > \ |
57 request.accept_mimetypes['text/html'] | 75 request.accept_mimetypes[HTML_MIMETYPE] |
76 | |
77 | |
78 class TemplateErrorResponse(ErrorResponse): | |
79 | |
80 TEMPLATE_ERROR_CODE = 100 | |
81 | |
82 def __init__(self, details, error_code=TEMPLATE_ERROR_CODE): | |
83 super(TemplateErrorResponse, self).__init__( | |
84 title="TemplateError", error_code=error_code, details=details, | |
85 html_error_code=500) | |
86 | |
87 | |
88 class ConversionErrorResponse(ErrorResponse): | |
89 | |
90 CONVERSION_ERROR_CODE = 200 | |
91 | |
92 def __init__(self, details, error_code=CONVERSION_ERROR_CODE): | |
93 super(TemplateErrorResponse, self).__init__( | |
94 title="ConversionError", error_code=error_code, details=details, | |
95 html_error_code=500) | |
58 | 96 |
59 | 97 |
60 class ConvertView(MethodView): | 98 class ConvertView(MethodView): |
61 | 99 |
62 def __init__(self, pyuno_driver_name="", hostname="localhost", port=2001): | 100 def __init__(self, pyuno_driver_name="", hostname="localhost", port=2001): |
71 return fformat and fformat.lower() in ALLOWED_FORMATS | 109 return fformat and fformat.lower() in ALLOWED_FORMATS |
72 | 110 |
73 def post(self): | 111 def post(self): |
74 ffile = request.files['file'] | 112 ffile = request.files['file'] |
75 if not ffile.filename: | 113 if not ffile.filename: |
76 error = ErrorResponse("Please upload a file for conversion", | 114 return ErrorResponse( |
77 html_error_code=401) | 115 "Upload file missing", error_code=101, |
78 return error() | 116 details="Please upload a file for conversion", |
117 html_error_code=400) | |
79 | 118 |
80 fformat = request.form['format'] | 119 fformat = request.form['format'] |
81 if not self.is_format_supported(fformat): | 120 if not self.is_format_supported(fformat): |
82 error = ErrorResponse("Format %s not allowed" % fformat, | 121 return ErrorResponse( |
83 html_error_code=401) | 122 "Invalid format", error_code=102, |
84 return error() | 123 details="Format %s not allowed" % fformat, |
124 html_error_code=400) | |
85 | 125 |
86 datadict = self.get_datadict() | 126 datadict = self.get_datadict() |
87 | 127 |
88 mimetype = self.get_mimetype_for_format(fformat) | 128 mimetype = self.get_mimetype_for_format(fformat) |
89 | 129 |
96 t.render(datadict) | 136 t.render(datadict) |
97 outfile.close() | 137 outfile.close() |
98 outfile = tfile | 138 outfile = tfile |
99 except Exception, e: | 139 except Exception, e: |
100 log.exception("Template error") | 140 log.exception("Template error") |
101 error = ErrorResponse( | 141 return TemplateErrorResponse(details=str(e)) |
102 "Template error", details=str(e), html_error_code=500) | |
103 return error() | |
104 | 142 |
105 if fformat != "odt": | 143 if fformat != "odt": |
106 try: | 144 try: |
107 outfile = self.convert(outfile, fformat) | 145 outfile = self.convert(outfile, fformat) |
108 except Exception, e: | 146 except Exception, e: |
109 log.exception("Conversion error") | 147 log.exception("Conversion error") |
110 error = ErrorResponse( | 148 return ConversionErrorResponse(details=str(e)) |
111 "Conversion error", details=str(e), html_error_code=500) | |
112 return error() | |
113 | 149 |
114 return Response(outfile, mimetype=mimetype) | 150 return Response(outfile, mimetype=mimetype) |
115 | 151 |
116 def get(self): | 152 def get(self): |
117 return render_template("convert.html") | 153 return render_template("convert.html") |