Removing geraldo reports/reportlabs in favor of weasyprint

This commit is contained in:
Adolfo Gómez García 2018-02-07 10:25:22 +01:00
parent c9bea9cf78
commit 766786715d
9 changed files with 176 additions and 85 deletions

View File

@ -109,13 +109,16 @@ class Reports(model.BaseModelHandler):
logger.debug('Report: {}'.format(report)) logger.debug('Report: {}'.format(report))
result = report.generateEncoded() result = report.generateEncoded()
return { data = {
'mime_type': report.mime_type, 'mime_type': report.mime_type,
'encoded': report.encoded, 'encoded': report.encoded,
'filename': report.filename, 'filename': report.filename,
'data': result 'data': result
} }
logger.debug('Data: {}'.format(data))
return data
except Exception as e: except Exception as e:
logger.exception('Generating report') logger.exception('Generating report')
return self.invalidRequestException(six.text_type(e)) return self.invalidRequestException(six.text_type(e))

View File

@ -33,17 +33,20 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils.translation import ugettext, ugettext_noop as _ from django.utils.translation import ugettext, ugettext_noop as _
from django.template import loader
from uds.core.ui.UserInterface import UserInterface from uds.core.ui.UserInterface import UserInterface
from uds.core.util import encoders from uds.core.util import encoders
from . import stock
import datetime
import six import six
from weasyprint import HTML, CSS
from datetime import datetime
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
__updated__ = '2017-11-15' __updated__ = '2018-02-07'
class Report(UserInterface): class Report(UserInterface):
@ -82,6 +85,46 @@ class Report(UserInterface):
raise Exception('Class does not includes an uuid!!!: {}'.format(cls)) raise Exception('Class does not includes an uuid!!!: {}'.format(cls))
return cls.uuid return cls.uuid
@staticmethod
def asPDF(html, header=None, water=None):
"""
Renders an html as PDF.
Uses the "report.css" as stylesheet
"""
with open(stock.getStockCssPath('report.css'), 'r') as f:
css = f.read()
css = (
css.replace("{header}", _('Report') if header is None else header)
.replace('{page}', _('Page'))
.replace('{of}', _('of'))
.replace('{water}', 'UDS Enterprise' if water is None else water)
.replace('{printed}', _('Printed in {now:%Y, %b %d} at {now:%H:%M}').format(now=datetime.now()))
)
h = HTML(string=html)
c = CSS(string=css)
pdf = h.write_pdf(stylesheets=[c])
with open('/home/dkmaster/kk/kk.pdf', 'wb') as f:
f.write(pdf)
return pdf
@staticmethod
def templateAsPDF(templateName, dct, header=None, water=None):
"""
Renders a template as PDF
"""
t = loader.get_template(templateName)
renderedHtml = t.render(dct)
logger.debug('HTML: {}'.format(renderedHtml))
return Report.asPDF(renderedHtml, header, water)
def __init__(self, values=None): def __init__(self, values=None):
""" """
Do not forget to invoke this in your derived class using Do not forget to invoke this in your derived class using
@ -127,10 +170,14 @@ class Report(UserInterface):
Basically calls generate and encodes resuslt as base64 Basically calls generate and encodes resuslt as base64
""" """
data = self.generate() data = self.generate()
with open('/home/dkmaster/kk/kk2.pdf', 'wb') as f:
f.write(data)
if self.encoded: if self.encoded:
return encoders.encode(data, 'base64', asText=True).replace('\n', '') data = encoders.encode(data, 'base64', asText=True).replace('\n', '')
else: with open('/home/dkmaster/kk/kk2.base64', 'w') as f:
return data f.write(data)
return data
def __str__(self): def __str__(self):
return 'Report {} with uuid {}'.format(self.name, self.uuid) return 'Report {} with uuid {}'.format(self.name, self.uuid)

View File

@ -0,0 +1,32 @@
@page {
margin: 3cm 2cm; padding-left: 1.5cm;
@top-center {
content: "{header}";
vertical-align: bottom;
border-bottom: 0.5pt solid
}
@top-right {
content: "{page} " counter(page)
" {of} " counter(pages)
}
@bottom-left {
content: "Generated by UDS"
}
@bottom-right {
content: '{printed}'
}
@left-top {
content: "{water}"; font: .5cm/1.5 Fontin Sans;
background: #005a9c; color: #fff; text-align: right;
padding-right: 2em; height: 1.5em; width: 10cm;
transform-origin: 100% 0;
transform: rotate(-90deg);
}
}
body { text-align: justify }
h1 { bookmark-level: none }
tbody tr:nth-child(odd) {
background-color: #4C8BF5;
color: #fff;
}

View File

@ -39,10 +39,19 @@ import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
__updated__ = '2015-04-29' __updated__ = '2018-02-07'
LOGO = 'logo-512.png' LOGO = 'logo-512.png'
CSS = 'report.css'
def getStockImagePath(stockImg): def getStockImagePath(stockImg=None):
if stockImg is None:
stockImg = LOGO
return tools.packageRelativeFile(__name__, 'stock_images/' + stockImg) return tools.packageRelativeFile(__name__, 'stock_images/' + stockImg)
def getStockCssPath(css=None):
if css is None:
css = CSS
return tools.packageRelativeFile(__name__, 'css/' + css)

View File

@ -46,8 +46,7 @@ import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
__updated__ = '2017-05-10' __updated__ = '2018-02-07'
availableReports = [] availableReports = []
@ -78,10 +77,10 @@ def __init__():
# Dinamycally import children of this package. The __init__.py files must import classes # Dinamycally import children of this package. The __init__.py files must import classes
pkgpath = os.path.dirname(sys.modules[__name__].__file__) pkgpath = os.path.dirname(sys.modules[__name__].__file__)
# TODO: Make this work with python3 also!!! (look for alternative, we have time...) # TODO: Make this work with python3 also!!! (look for alternative, we have time...)
if six.PY2: for _, name, _ in pkgutil.iter_modules([pkgpath]):
for _, name, _ in pkgutil.iter_modules([pkgpath]): __import__(name, globals(), locals(), [], 1)
__import__(name, globals(), locals(), [], 1)
recursiveAdd(reports.Report) recursiveAdd(reports.Report)
__init__() __init__()

View File

@ -34,7 +34,6 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext, ugettext_lazy as _ from django.utils.translation import ugettext, ugettext_lazy as _
from uds.core.ui.UserInterface import gui from uds.core.ui.UserInterface import gui
from uds.core.reports import stock
from uds.models import Authenticator from uds.models import Authenticator
import six import six
@ -42,62 +41,11 @@ import csv
from .base import ListReport from .base import ListReport
from geraldo.generators.pdf import PDFGenerator
from geraldo import Report, landscape, ReportBand, ObjectValue, SystemField, BAND_WIDTH, Label, Image
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm
from reportlab.lib.enums import TA_RIGHT, TA_CENTER
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
__updated__ = '2015-06-21' __updated__ = '2018-02-07'
class UsersReport(Report):
title = ''
author = 'UDS'
print_if_empty = True
page_size = A4
margin_left = 2 * cm
margin_top = 0.5 * cm
margin_right = 0.5 * cm
margin_bottom = 0.5 * cm
class band_detail(ReportBand):
height = 0.5 * cm
elements = (
ObjectValue(attribute_name='name', left=0.5 * cm, style={'fontName': 'Helvetica', 'fontSize': 8}),
ObjectValue(attribute_name='real_name', left=6 * cm, style={'fontName': 'Helvetica', 'fontSize': 8}),
ObjectValue(attribute_name='last_access', left=15 * cm, style={'fontName': 'Helvetica', 'fontSize': 8}),
)
class band_page_header(ReportBand):
height = 2.0 * cm
elements = [
SystemField(expression='%(report_title)s', top=0.5 * cm, left=0, width=BAND_WIDTH,
style={'fontName': 'Helvetica-Bold', 'fontSize': 14, 'alignment': TA_CENTER}),
Label(text=_('User ID'), top=1.5 * cm, left=0.5 * cm),
Label(text=_('Real Name'), top=1.5 * cm, left=6 * cm),
Label(text=_('Last access'), top=1.5 * cm, left=15 * cm),
SystemField(expression=_('Page %(page_number)d of %(page_count)d'), top=0.1 * cm,
width=BAND_WIDTH, style={'alignment': TA_RIGHT}),
Image(filename=stock.getStockImagePath(stock.LOGO), left=0.1 * cm, top=0.0 * cm, width=2 * cm, height=2 * cm),
]
borders = {'bottom': True}
class band_page_footer(ReportBand):
height = 0.5 * cm
elements = [
Label(text='Generated by UDS', top=0.1 * cm),
SystemField(expression=_('Printed in %(now:%Y, %b %d)s at %(now:%H:%M)s'), top=0.1 * cm,
width=BAND_WIDTH, style={'alignment': TA_RIGHT}),
]
borders = {'top': True}
class ListReportUsers(ListReport): class ListReportUsers(ListReport):
@ -131,12 +79,12 @@ class ListReportUsers(ListReport):
auth = Authenticator.objects.get(uuid=self.authenticator.value) auth = Authenticator.objects.get(uuid=self.authenticator.value)
users = auth.users.order_by('name') users = auth.users.order_by('name')
output = six.StringIO() return self.templateAsPDF(
'uds/reports/lists/users.html',
report = UsersReport(queryset=users) dct={'users': users},
report.title = _('Users List for {}').format(auth.name) header=ugettext('Users List for {}').format(auth.name),
report.generate_by(PDFGenerator, filename=output) water=auth.name
return output.getvalue() )
class ListReportsUsersCSV(ListReportUsers): class ListReportsUsersCSV(ListReportUsers):
@ -154,7 +102,7 @@ class ListReportsUsersCSV(ListReportUsers):
self.filename = auth.name + '.csv' self.filename = auth.name + '.csv'
def generate(self): def generate(self):
output = StringIO.StringIO() output = six.StringIO()
writer = csv.writer(output) writer = csv.writer(output)
auth = Authenticator.objects.get(uuid=self.authenticator.value) auth = Authenticator.objects.get(uuid=self.authenticator.value)
users = auth.users.order_by('name') users = auth.users.order_by('name')

View File

@ -31,10 +31,10 @@
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
""" """
from .usage import StatsReportUsage # from .usage import StatsReportUsage
from .login import StatsReportLogin, StatsReportLoginCSV # from .login import StatsReportLogin, StatsReportLoginCSV
from .pool_performance import PoolPerformanceReport # from .pool_performance import PoolPerformanceReport
from .usage_by_pool import UsageByPool # from .usage_by_pool import UsageByPool
from .pools_usage import CountersPoolAssigned # from .pools_usage import CountersPoolAssigned
from .usage_summary_pool import UsageSummaryByPool # from .usage_summary_pool import UsageSummaryByPool

View File

@ -1,4 +1,4 @@
# jshint strict: true # jshint strict: true
gui.reports = new GuiElement(api.reports, "reports") gui.reports = new GuiElement(api.reports, "reports")
gui.reports.link = (event) -> gui.reports.link = (event) ->
"use strict" "use strict"
@ -9,7 +9,7 @@ gui.reports.link = (event) ->
reports: "reports-placeholder" reports: "reports-placeholder"
) )
gui.setLinksEvents() gui.setLinksEvents()
tableId = gui.reports.table( tableId = gui.reports.table(
icon: 'reports' icon: 'reports'
container: "reports-placeholder" container: "reports-placeholder"
@ -43,14 +43,18 @@ gui.reports.link = (event) ->
closeFnc() closeFnc()
gui.doLog data gui.doLog data
if data.encoded if data.encoded
gui.doLog('Data is encoded')
content = base64.decode(data.data) content = base64.decode(data.data)
gui.doLog('Length: ' + content.length)
else else
content = data.data content = data.data
setTimeout( (()-> setTimeout( (()->
byteContent = new Uint8Array(content.length)
byteContent[i] = content.charCodeAt(i) for i in [0..content.length-1]
blob = new Blob([byteContent], type: data.content_type)
saveAs( saveAs(
new Blob([content], blob,
type: data.content_type
),
data.filename data.filename
) )
), 100) ), 100)
@ -83,4 +87,4 @@ gui.reports.link = (event) ->
) )
return return
false false

View File

@ -0,0 +1,49 @@
<html lang="en">
<head>
<title>Report test</title>
</head>
<body>
<div class="content">
<table style="width: 100%">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Parent</th>
</tr>
</thead>
<tbody>
<tr>
<td>name of person</td>
<td>123</td>
<td>his</td>
</tr>
<tr>
<td>name of person 2</td>
<td>123 2</td>
<td>his 2</td>
</tr>
<tr>
<td>name of person 3</td>
<td>123 3</td>
<td>his </td>
</tr>
<tr>
<td>name of person 4</td>
<td>123 4</td>
<td>his 4</td>
</tr>
<tr>
<td>name of person 5</td>
<td>123 5</td>
<td>his 5</td>
</tr>
</tbody>
</table>
<p style="page-break-before: always">
This is a new page
</p>
</div>
</body>
</html>