def report(request): """Prints a pdf file with all available albums. The albums can be optionally filtered by year. reportbro-lib is used to generate the pdf file. The data itself is retrieved from the database (*get_albums*). The report_definition is also stored in the database and is created on-the-fly if not present (to make this Demo App easier to use). """ year = request.GET.get('year') if year: try: year = int(year) except (ValueError, TypeError): return HttpResponseBadRequest('invalid year parameter') else: year = None # NOTE: these params must match exactly with the parameters defined in the # report definition in ReportBro Designer, check the name and type (Number, Date, List, ...) # of those parameters in the Designer. params = dict(year=year, albums=list(get_albums(year)), current_date=datetime.datetime.now()) if ReportDefinition.objects.filter( report_type='albums_report').count() == 0: create_album_report_template() report_definition = ReportDefinition.objects.get( report_type='albums_report') if not report_definition: return HttpResponseServerError('no report_definition available') try: report_inst = Report(json.loads(report_definition.report_definition), params) if report_inst.errors: # report definition should never contain any errors, # unless you saved an invalid report and didn't test in ReportBro Designer raise ReportBroError(report_inst.errors[0]) pdf_report = report_inst.generate_pdf() response = HttpResponse(pdf_report, content_type='application/pdf') response[ 'Content-Disposition'] = 'inline; filename="{filename}"'.format( filename='albums.pdf') return response except ReportBroError as ex: return HttpResponseServerError('report error: ' + str(ex.error)) except Exception as ex: return HttpResponseServerError('report exception: ' + str(ex))
def report(): """Prints a pdf file with all available albums. The albums can be optionally filtered by year. reportbro-lib is used to generate the pdf file. The data itself is retrieved from the database (*get_albums*). The report_definition is also stored in the database and is created on-the-fly if not present (to make this Demo App easier to use). """ from reportbro import Report, ReportBroError year = None if request.vars.year: try: year = int(request.vars.year) except (ValueError, TypeError): raise HTTP(400, 'invalid year parameter') # NOTE: these params must match exactly with the parameters defined in the # report definition in ReportBro Designer, check the name and type (Number, Date, List, ...) # of those parameters in the Designer. params = dict(year=year, albums=get_albums(year), current_date=request.now) if db(db.report_definition.report_type == 'albums_report').count() == 0: create_album_report_template() report_definition = db( db.report_definition.report_type == 'albums_report').select( db.report_definition.id, db.report_definition.report_definition).first() if not report_definition: raise HTTP(500, 'no report_definition available') try: report = Report(report_definition.report_definition, params) if report.errors: # report definition should never contain any errors, # unless you saved an invalid report and didn't test in ReportBro Designer raise ReportBroError(report.errors[0]) pdf_report = report.generate_pdf() response.headers['Content-Type'] = 'application/pdf' response.headers[ 'Content-Disposition'] = 'inline; filename="albums.pdf"' return pdf_report except ReportBroError as ex: raise HTTP(500, 'report error: ' + str(ex.error)) except Exception as ex: raise HTTP(500, 'report exception: ' + str(ex))
def json2Pdf(cls, jsonTemplate, data, output): if os.path.exists(jsonTemplate): with open(jsonTemplate, "rb") as f: report_definition = json.loads(f.read()) else: report_definition = json.loads(jsonTemplate) additional_fonts = [dict(value='firefly', filename='fireflysung.ttf')] is_test_data = {} report = Report(report_definition, data, is_test_data, additional_fonts=additional_fonts) report_file = report.generate_pdf(filename=output, add_watermark=False) logger.info("%sjson to pdf successed!" % jsonTemplate) return output
def process(self, report, data, default=None): try: r = ReportDefinition.objects.get(name=report).definition except ObjectDoesNotExist: if default: r = default else: r = default_report data = { "report_name": report, "data_dump": json.dumps(data, cls=DjangoJSONEncoder) } r = Report(json.loads(r), data) if r.errors: raise ReportBroError(r.errors[0]) pdf_report = r.generate_pdf() return FileResponse(io.BytesIO(pdf_report), as_attachment=False)
def tpce(self, template, data, output): if os.path.exists(template): with open(template, "rb") as f: report_definition = json.loads(f.read()) else: report_definition = json.loads(template) if os.path.exists(data): with open(data, "rb") as f: data = json.loads(f.read()) else: data = json.loads(data) additional_fonts = [dict(value='firefly', filename='fireflysung.ttf')] is_test_data = {} report = Report(report_definition, data, is_test_data, additional_fonts=additional_fonts) report_file = report.generate_pdf(filename=output, add_watermark=False)
def run(): """Generates a report for preview. This method is called by ReportBro Designer when the Preview button is clicked, the url is defined when initializing the Designer, see *reportServerUrl* in templates/report/edit.html """ response = Response() response.headers['Access-Control-Allow-Origin'] = '*' response.headers['Access-Control-Allow-Methods'] = 'GET, PUT, OPTIONS' response.headers['Access-Control-Allow-Headers'] =\ 'Origin, X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept, Z-Key' if request.method == 'OPTIONS': # options request is usually sent by browser for a cross-site request, we only need to set the # Access-Control-Allow headers in the response so the browser sends the following get/put request return response additional_fonts = [] # add additional fonts here if additional fonts are used in ReportBro Designer db_engine = db.get_db() if request.method == 'PUT': # all data needed for report preview is sent in the initial PUT request, it contains # the format (pdf or xlsx), the report itself (report_definition), the data (test data # defined within parameters in the Designer) and is_test_data flag (always True # when request is sent from Designer) json_data = request.json output_format = json_data.get('outputFormat') if output_format not in ('pdf', 'xlsx'): abort(400, 'outputFormat parameter missing or invalid') report_definition = json_data.get('report') data = json_data.get('data') is_test_data = bool(json_data.get('isTestData')) report = None try: report = Report(report_definition, data, is_test_data, additional_fonts=additional_fonts) except Exception as e: abort(400, 'failed to initialize report: ' + str(e)) if report.errors: # return list of errors in case report contains errors, e.g. duplicate parameters. # with this information ReportBro Designer can select object containing errors, # highlight erroneous fields and display error messages response.set_data( json.dumps(dict(errors=report.errors), default=jsonconverter)) return response try: now = datetime.datetime.now() # delete old reports (older than 3 minutes) to avoid table getting too big db_engine.execute(db.report_request.delete().where( db.report_request.c.created_on < ( now - datetime.timedelta(minutes=3)))) total_size = db_engine.execute( select([func.sum(db.report_request.c.pdf_file_size) ])).scalar() if total_size and total_size > MAX_CACHE_SIZE: # delete all reports older than 10 seconds to reduce db size for cached pdf files db_engine.execute(db.report_request.delete().where( db.report_request.c.created_on < ( now - datetime.timedelta(seconds=10)))) start = timer() report_file = report.generate_pdf(add_watermark=True) end = timer() print('pdf generated in %.3f seconds' % (end - start)) key = str(uuid.uuid4()) # add report request into sqlite db, this enables downloading the report by url # (the report is identified by the key) without any post parameters. # This is needed for pdf and xlsx preview. db_engine.execute(db.report_request.insert(), key=key, report_definition=json.dumps(report_definition), data=json.dumps(data, default=jsonconverter), is_test_data=is_test_data, pdf_file=report_file, pdf_file_size=len(report_file), created_on=now) response.set_data('key:' + key) return response except ReportBroError as err: # in case an error occurs during report generation a ReportBroError exception is thrown # to stop processing. We return this error within a list so the error can be # processed by ReportBro Designer. response.set_data( json.dumps(dict(errors=[err.error]), default=jsonconverter)) return response elif request.method == 'GET': output_format = request.args.get('outputFormat') assert output_format in ('pdf', 'xlsx') key = request.args.get('key') report = None report_file = None if key and len(key) == 36: # the report is identified by a key which was saved # in an sqlite table during report preview with a PUT request row = db_engine.execute( select([db.report_request ]).where(db.report_request.c.key == key)).fetchone() if not row: abort( 400, 'report not found (preview probably too old), update report preview and try again' ) if output_format == 'pdf' and row['pdf_file']: report_file = row['pdf_file'] else: report_definition = json.loads(row['report_definition']) data = json.loads(row['data']) is_test_data = row['is_test_data'] report = Report(report_definition, data, is_test_data, additional_fonts=additional_fonts) if report.errors: abort(400, 'error generating report') else: # in case there is a GET request without a key we expect all report data to be available. # this is NOT used by ReportBro Designer and only added for the sake of completeness. json_data = request.json report_definition = json_data.get('report') data = json_data.get('data') is_test_data = bool(json_data.get('isTestData')) if not isinstance(report_definition, dict) or not isinstance( data, dict): abort(400, 'report_definition or data missing') report = Report(report_definition, data, is_test_data, additional_fonts=additional_fonts) if report.errors: abort(400, 'error generating report') try: # once we have the reportbro.Report instance we can generate # the report (pdf or xlsx) and return it now = datetime.datetime.now() if output_format == 'pdf': if report_file is None: # as it is currently implemented the pdf file is always stored in the # report_request table along the other report data. Therefor report_file # will always be set. The generate_pdf call here is only needed in case # the code is changed to clear report_request.pdf_file column when the # data in this table gets too big (currently whole table rows are deleted) report_file = report.generate_pdf(add_watermark=True) response.headers['Content-Type'] = 'application/pdf' response.headers[ 'Content-Disposition'] = 'inline; filename="{filename}"'.format( filename='report-' + str(now) + '.pdf') else: report_file = report.generate_xlsx() response.headers[ 'Content-Type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' response.headers[ 'Content-Disposition'] = 'inline; filename="{filename}"'.format( filename='report-' + str(now) + '.xlsx') response.set_data(report_file) return response except ReportBroError: abort(400, 'error generating report') return None
def plantilla_vista_previa(request): """ Vista previa del reporte :param request: :return: """ max_cache_size = 10 * 1024 * 1024 # keep max. 10 MB of generated pdf files in db now = datetime.datetime.now() response = HttpResponse('') response['Access-Control-Allow-Origin'] = '*' response['Access-Control-Allow-Methods'] = 'GET, PUT, OPTIONS' response[ 'Access-Control-Allow-Headers'] = \ 'Origin, X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept, Z-Key' if request.method == 'OPTIONS': # options request is usually sent by browser for a cross-site request, we only need to set the # Access-Control-Allow headers in the response so the browser sends the following get/put request return response additional_fonts = [] # add additional fonts here if additional fonts are used in ReportBro Designer if request.method == 'PUT': json_data = json.loads(request.body.decode('utf-8')) if not isinstance(json_data, dict) or not isinstance(json_data.get('report'), dict) or \ not isinstance(json_data.get('data'), dict) or not isinstance(json_data.get('isTestData'), bool): return HttpResponseBadRequest('invalid report values') output_format = json_data.get('outputFormat') if output_format not in ('pdf', 'xlsx'): return HttpResponseBadRequest( 'outputFormat parameter missing or invalid') report_definition = json_data.get('report') data = json_data.get('data') is_test_data = json_data.get('isTestData') try: report = Report(report_definition, data, is_test_data, additional_fonts=additional_fonts) except Exception as e: return HttpResponseBadRequest('failed to initialize report: ' + str(e)) if report.errors: return HttpResponse(json.dumps(dict(errors=report.errors))) try: # delete old reports (older than 3 minutes) to avoid table getting too big PlantillaModelo.objects.filter( created_on__lt=(now - datetime.timedelta(minutes=3))).delete() total_size = PlantillaModelo.objects.aggregate( Sum('pdf_file_size')) if total_size['pdf_file_size__sum'] and total_size[ 'pdf_file_size__sum'] > max_cache_size: # delete all reports older than 10 seconds to reduce db size for cached pdf files PlantillaModelo.objects.filter( created_on__lt=(now - datetime.timedelta(seconds=10))).delete() start = timer() report_file = report.generate_pdf() end = timer() print('pdf generated in %.3f seconds' % (end - start)) key = str(uuid.uuid4()) # add report request into sqlite db, this enables downloading the report by url (the report is identified # by the key) without any post parameters. This is needed for pdf and xlsx preview. PlantillaModelo.objects.create( key=key, definicion=json.dumps(report_definition, default=json_default), data=json.dumps(data, default=json_default), is_test_data=is_test_data, pdf_file=report_file, pdf_file_size=len(report_file), created_on=now) return HttpResponse('key:' + key) except ReportBroError as err: return HttpResponse(json.dumps(dict(errors=[err.error]))) except Exception as e: print(e) elif request.method == 'GET': output_format = request.GET.get('outputFormat') if output_format not in ('pdf', 'xlsx'): return HttpResponseBadRequest( 'outputFormat parameter missing or invalid') key = request.GET.get('key') report = None report_file = None if key and len(key) == 36: # the report is identified by a key which was saved # in an table during report preview with a PUT request try: plantilla_modelo = PlantillaModelo.objects.get(key=key) except PlantillaModelo.DoesNotExist: return HttpResponseBadRequest( 'report not found (preview probably too old), update report preview and try again' ) if output_format == 'pdf' and plantilla_modelo.pdf_file: report_file = plantilla_modelo.pdf_file else: report_definition = json.loads(plantilla_modelo.definicion) data = json.loads(plantilla_modelo.data) is_test_data = plantilla_modelo.is_test_data report = Report(report_definition, data, is_test_data, additional_fonts=additional_fonts) if report.errors: return HttpResponseBadRequest( reason='error generating report') else: # generate and download report with a GET request json_data = json.loads(request.body.decode('utf-8')) if not isinstance(json_data, dict) or not isinstance(json_data.get('report'), dict) or \ not isinstance(json_data.get('data'), dict) or not isinstance(json_data.get('isTestData'), bool): return HttpResponseBadRequest('invalid report values') report_definition = json_data.get('report') data = json_data.get('data') is_test_data = json_data.get('isTestData') if not isinstance(report_definition, dict) or not isinstance( data, dict): return HttpResponseBadRequest( 'report_definition or data missing') report = Report(report_definition, data, is_test_data, additional_fonts=additional_fonts) if report.errors: return HttpResponseBadRequest(reason='error generating report') try: if output_format == 'pdf': if report_file is None: report_file = report.generate_pdf() response = HttpResponse(report_file, content_type='application/pdf') response[ 'Content-Disposition'] = 'inline; filename="{filename}"'.format( filename='report-' + str(now) + '.pdf') else: report_file = report.generate_xlsx() response = HttpResponse( report_file, content_type= 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ) response[ 'Content-Disposition'] = 'inline; filename="{filename}"'.format( filename='report-' + str(now) + '.xlsx') return response except ReportBroError: return HttpResponseBadRequest('error generating report') except Exception as ex: print(ex) return None
def put(self): # all data needed for report preview is sent in the initial PUT request, it contains # the format (pdf or xlsx), the report itself (report_definition), the data (test data # defined within parameters in the Designer) and is_test_data flag (always True # when request is sent from Designer) self.set_access_headers() json_data = json.loads(self.request.body.decode('utf-8')) report_definition = json_data.get('report') output_format = json_data.get('outputFormat') if output_format not in ('pdf', 'xlsx'): raise HTTPError(400, reason='outputFormat parameter missing or invalid') data = json_data.get('data') is_test_data = bool(json_data.get('isTestData')) try: report = Report(report_definition, data, is_test_data, additional_fonts=self.additional_fonts) except Exception as e: raise HTTPError(400, reason='failed to initialize report: ' + str(e)) if report.errors: # return list of errors in case report contains errors, e.g. duplicate parameters. # with this information ReportBro Designer can select object containing errors, # highlight erroneous fields and display error messages self.write(json.dumps(dict(errors=report.errors))) return try: now = datetime.datetime.now() # delete old reports (older than 3 minutes) to avoid table getting too big self.db_connection.execute(report_request.delete().where( report_request.c.created_on < (now - datetime.timedelta(minutes=3)))) total_size = self.db_connection.execute( select([func.sum(report_request.c.pdf_file_size)])).scalar() if total_size and total_size > MAX_CACHE_SIZE: # delete all reports older than 10 seconds to reduce db size for cached pdf files self.db_connection.execute(report_request.delete().where( report_request.c.created_on < ( now - datetime.timedelta(seconds=10)))) report_file = report.generate_pdf() key = str(uuid.uuid4()) # add report request into sqlite db, this enables downloading the report by url # (the report is identified by the key) without any post parameters. # This is needed for pdf and xlsx preview. self.db_connection.execute( report_request.insert(), key=key, report_definition=json.dumps(report_definition), data=json.dumps(data, default=jsonconverter), is_test_data=is_test_data, pdf_file=report_file, pdf_file_size=len(report_file), created_on=now) self.write('key:' + key) except ReportBroError as err: # in case an error occurs during report generation a ReportBroError exception is thrown # to stop processing. We return this error within a list so the error can be # processed by ReportBro Designer. self.write(json.dumps(dict(errors=[err.error]))) return
def get(self): self.set_access_headers() output_format = self.get_query_argument('outputFormat') assert output_format in ('pdf', 'xlsx') key = self.get_query_argument('key', '') report = None report_file = None if key and len(key) == 36: # the report is identified by a key which was saved # in an sqlite table during report preview with a PUT request row = self.db_connection.execute( select([report_request ]).where(report_request.c.key == key)).fetchone() if not row: raise HTTPError( 400, reason= 'report not found (preview probably too old), update report preview and try again' ) if output_format == 'pdf' and row['pdf_file']: report_file = row['pdf_file'] else: report_definition = json.loads(row['report_definition']) data = json.loads(row['data']) is_test_data = row['is_test_data'] report = Report(report_definition, data, is_test_data, additional_fonts=self.additional_fonts) if report.errors: raise HTTPError(400, reason='error generating report') else: # in case there is a GET request without a key we expect all report data to be available. # this is NOT used by ReportBro Designer and only added for the sake of completeness. json_data = json.loads(self.request.body.decode('utf-8')) report_definition = json_data.get('report') data = json_data.get('data') is_test_data = bool(json_data.get('isTestData')) if not isinstance(report_definition, dict) or not isinstance( data, dict): raise HTTPError(400, reason='report_definition or data missing') report = Report(report_definition, data, is_test_data, additional_fonts=self.additional_fonts) if report.errors: raise HTTPError(400, reason='error generating report') try: # once we have the reportbro.Report instance we can generate # the report (pdf or xlsx) and return it now = datetime.datetime.now() if output_format == 'pdf': if report_file is None: # as it is currently implemented the pdf file is always stored in the # report_request table along the other report data. Therefor report_file # will always be set. The generate_pdf call here is only needed in case # the code is changed to clear report_request.pdf_file column when the # data in this table gets too big (currently whole table rows are deleted) report_file = report.generate_pdf() self.set_header('Content-Type', 'application/pdf') self.set_header( 'Content-Disposition', 'inline; filename="{filename}"'.format(filename='report-' + str(now) + '.pdf')) else: report_file = report.generate_xlsx() self.set_header( 'Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ) self.set_header( 'Content-Disposition', 'inline; filename="{filename}"'.format(filename='report-' + str(now) + '.xlsx')) self.write(report_file) except ReportBroError: raise HTTPError(400, reason='error generating report')