bjoern@0: # -*- coding: utf-8 -*- bjoern@0: bjoern@29: import logging bjoern@0: import tempfile bjoern@0: bjoern@16: from flask import request, Response, json, render_template bjoern@0: from flask.views import MethodView bjoern@0: bjoern@4: from py3o.template import Template bjoern@4: bjoern@19: from PyPDF2 import PdfFileMerger bjoern@61: from PyPDF2.utils import PyPdfError bjoern@19: bjoern@47: from werkzeug.utils import escape bjoern@47: bjoern@29: log = logging.getLogger(__name__) bjoern@19: bjoern@0: ALLOWED_FORMATS = ["pdf", "doc", "docx", "odt"] bjoern@0: bjoern@19: PDF_MIMETYPE = "application/pdf" bjoern@47: JSON_MIMETYPE = "application/json" bjoern@47: HTML_MIMETYPE = "text/html" bjoern@19: bjoern@0: MIMETYPES = { bjoern@0: "odt": "application/vnd.oasis.opendocument.text", bjoern@0: "doc": "application/msword", bjoern@0: "docx": "application/vnd.openxmlformats-officedocument" bjoern@0: ".wordprocessingml.document", bjoern@19: "pdf": PDF_MIMETYPE, bjoern@0: } bjoern@0: bjoern@0: DEFAULT_MIMETYPE = "application/octet-stream" bjoern@0: bjoern@0: bjoern@47: class ErrorResponse(Response): bjoern@42: bjoern@47: BAD_REQUEST_ERROR_CODE = 400 bjoern@42: bjoern@47: def __init__(self, title, error_code, details, bjoern@47: html_error_code=BAD_REQUEST_ERROR_CODE): bjoern@47: data, mime_type = self.get_response_data(title, error_code, details) bjoern@47: super(ErrorResponse, self).__init__(response=data, mimetype=mime_type, bjoern@47: status=html_error_code) bjoern@47: bjoern@47: def json(self, title, error_code, details): bjoern@47: return json.dumps({ bjoern@47: "error": title, bjoern@47: "error_code": error_code, bjoern@47: "details": details, bjoern@47: }), JSON_MIMETYPE bjoern@47: bjoern@47: def html(self, title, error_code, details): bjoern@59: data = ( bjoern@47: u'\n' bjoern@47: u'%(code)s %(name)s\n' bjoern@47: u'

%(name)s

\n' bjoern@47: u'%(details)s\n' bjoern@47: ) % { bjoern@47: "code": error_code, bjoern@47: "name": escape(title), bjoern@47: "details": escape(details), bjoern@47: } bjoern@47: return data, HTML_MIMETYPE bjoern@47: bjoern@47: def get_response_data(self, title, error_code, details): bjoern@42: if self.is_wants_json(): bjoern@47: return self.json(title, error_code, details) bjoern@47: return self.html(title, error_code, details) bjoern@42: bjoern@42: def is_wants_json(self): bjoern@47: best = request.accept_mimetypes.best_match([JSON_MIMETYPE, bjoern@47: HTML_MIMETYPE]) bjoern@47: return best == JSON_MIMETYPE and \ bjoern@42: request.accept_mimetypes[best] > \ bjoern@47: request.accept_mimetypes[HTML_MIMETYPE] bjoern@47: bjoern@47: bjoern@47: class TemplateErrorResponse(ErrorResponse): bjoern@47: bjoern@47: TEMPLATE_ERROR_CODE = 100 bjoern@47: bjoern@47: def __init__(self, details, error_code=TEMPLATE_ERROR_CODE): bjoern@47: super(TemplateErrorResponse, self).__init__( bjoern@47: title="TemplateError", error_code=error_code, details=details, bjoern@47: html_error_code=500) bjoern@47: bjoern@47: bjoern@47: class ConversionErrorResponse(ErrorResponse): bjoern@47: bjoern@47: CONVERSION_ERROR_CODE = 200 bjoern@47: bjoern@47: def __init__(self, details, error_code=CONVERSION_ERROR_CODE): bjoern@54: super(ConversionErrorResponse, self).__init__( bjoern@47: title="ConversionError", error_code=error_code, details=details, bjoern@47: html_error_code=500) bjoern@42: bjoern@42: bjoern@60: class MergeErrorResponse(ErrorResponse): bjoern@60: bjoern@60: MERGE_ERROR_CODE = 300 bjoern@60: bjoern@60: def __init__(self, details, error_code=MERGE_ERROR_CODE): bjoern@60: super(MergeErrorResponse, self).__init__( bjoern@60: title="MergeError", error_code=error_code, details=details, bjoern@60: html_error_code=500) bjoern@60: bjoern@60: bjoern@0: class ConvertView(MethodView): bjoern@0: bjoern@0: def __init__(self, pyuno_driver_name="", hostname="localhost", port=2001): bjoern@0: driver_module = self._load_driver_module(pyuno_driver_name) bjoern@0: self.convertor = driver_module.Convertor(hostname, port) bjoern@0: bjoern@0: def _load_driver_module(self, pyuno_driver_name): bjoern@0: return __import__(pyuno_driver_name, globals(), locals(), bjoern@0: ["Convertor"]) bjoern@0: bjoern@3: def is_format_supported(self, fformat): bjoern@3: return fformat and fformat.lower() in ALLOWED_FORMATS bjoern@0: bjoern@3: def post(self): bjoern@3: ffile = request.files['file'] bjoern@29: if not ffile.filename: bjoern@47: return ErrorResponse( bjoern@47: "Upload file missing", error_code=101, bjoern@47: details="Please upload a file for conversion", bjoern@47: html_error_code=400) bjoern@29: bjoern@3: fformat = request.form['format'] bjoern@3: if not self.is_format_supported(fformat): bjoern@47: return ErrorResponse( bjoern@47: "Invalid format", error_code=102, bjoern@47: details="Format %s not allowed" % fformat, bjoern@47: html_error_code=400) bjoern@3: bjoern@30: datadict = self.get_datadict() bjoern@30: bjoern@29: mimetype = self.get_mimetype_for_format(fformat) bjoern@29: bjoern@30: outfile = self.save_form_file(ffile) bjoern@30: bjoern@30: if datadict: bjoern@31: try: bjoern@31: tfile = tempfile.NamedTemporaryFile() bjoern@42: t = Template(outfile, tfile, ignore_undefined_variables=True) bjoern@31: t.render(datadict) bjoern@31: outfile.close() bjoern@31: outfile = tfile bjoern@42: except Exception, e: bjoern@41: log.exception("Template error") bjoern@47: return TemplateErrorResponse(details=str(e)) bjoern@30: bjoern@32: if fformat != "odt": bjoern@30: try: bjoern@30: outfile = self.convert(outfile, fformat) bjoern@42: except Exception, e: bjoern@30: log.exception("Conversion error") bjoern@47: return ConversionErrorResponse(details=str(e)) bjoern@29: bjoern@0: return Response(outfile, mimetype=mimetype) bjoern@0: bjoern@16: def get(self): bjoern@16: return render_template("convert.html") bjoern@16: bjoern@3: def save_form_file(self, infile): bjoern@33: outfile = tempfile.NamedTemporaryFile() bjoern@30: infile.save(outfile) bjoern@30: infile.close() bjoern@33: outfile.seek(0) bjoern@3: return outfile bjoern@3: bjoern@3: def convert(self, infile, fformat): bjoern@0: outfile = tempfile.NamedTemporaryFile() bjoern@0: bjoern@3: self.convertor.convert(infile.name, outfile.name, fformat) bjoern@0: bjoern@0: infile.close() bjoern@0: return outfile bjoern@0: bjoern@3: def get_mimetype_for_format(self, fformat): bjoern@3: return MIMETYPES.get(fformat, DEFAULT_MIMETYPE) bjoern@4: bjoern@4: def get_datadict(self): bjoern@34: vars = request.form.get('datadict') bjoern@30: if not vars: bjoern@30: return None bjoern@4: return json.loads(vars) bjoern@18: bjoern@19: bjoern@19: class MergeView(MethodView): bjoern@19: bjoern@19: def get(self): bjoern@19: return render_template("merge.html") bjoern@19: bjoern@19: def post(self): bjoern@57: log.debug("Merging PDF documents") bjoern@57: bjoern@19: merger = PdfFileMerger() bjoern@67: bjoern@67: ffiles = [] bjoern@67: bjoern@67: # allow files to have arbitray form names bjoern@67: # order files by their form names bjoern@67: for key, value in sorted(request.files.iterlists(), bjoern@67: key=lambda x: x[0].lower()): bjoern@67: ffiles.extend(value) bjoern@19: bjoern@62: for ffile in ffiles: bjoern@62: try: bjoern@61: merger.append(ffile) bjoern@62: except Exception, e: bjoern@62: log.exception("Error merging file %s" % ffile) bjoern@62: if self.is_ignore_file_errors(): bjoern@62: continue bjoern@62: else: bjoern@62: return MergeErrorResponse(details=str(e)) bjoern@19: bjoern@62: outfile = tempfile.NamedTemporaryFile() bjoern@19: bjoern@62: try: bjoern@61: merger.write(outfile) bjoern@61: merger.close() bjoern@61: outfile.seek(0) bjoern@61: except PyPdfError, e: bjoern@61: log.exception("Merge error") bjoern@61: return MergeErrorResponse(details=str(e)) bjoern@19: bjoern@57: log.debug("PDF documents merged") bjoern@19: return Response(outfile, mimetype=PDF_MIMETYPE) bjoern@22: bjoern@62: def is_ignore_file_errors(self): bjoern@62: return request.args.get("ignore_file_errors", False) or \ bjoern@62: request.form.get("ignore_file_errors", False) bjoern@62: bernhard@73: class CheckView(MethodView): bernhard@73: bernhard@73: def get(self): bernhard@73: return render_template("check.html") bernhard@73: bernhard@73: def post(self): bernhard@73: log.debug("Checking a PDF document's readiness for merging") bernhard@73: bernhard@73: merger = PdfFileMerger() bernhard@73: bernhard@73: ffile = request.files['file'] bernhard@73: if not ffile.filename: bernhard@73: return ErrorResponse( bernhard@73: "Upload file missing", error_code=101, bernhard@73: details="Please upload a file for conversion", bernhard@73: html_error_code=400) bernhard@73: bernhard@73: try: bernhard@73: merger.append(ffile) bernhard@73: except Exception, e: bernhard@73: log.exception("Error merging file %s" % ffile) bernhard@73: return MergeErrorResponse(details=str(e)) bernhard@73: bernhard@73: merger.close() bernhard@73: bernhard@73: log.debug("PDF document %s checked." % ffile) bernhard@73: return Response("Okay.") bernhard@73: bjoern@22: bjoern@22: class TemplateView(MethodView): bjoern@22: bjoern@22: template_name = "" bjoern@22: bjoern@22: def __init__(self, template_name=None): bjoern@22: if template_name: bjoern@22: self.template_name = template_name bjoern@22: bjoern@22: def get_template_name(self): bjoern@22: return self.template_name bjoern@22: bjoern@22: def get(self): bjoern@22: return render_template(self.get_template_name())