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))
Exemple #2
0
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))
Exemple #3
0
    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)
Exemple #5
0
    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)
Exemple #6
0
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
Exemple #7
0
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')