Merge pull request #2511 from rmehta/auto-email-fixes
[fix] formatting and modified filters for auto email report, fixes frappe/erpnext#7081 and frappe/erpnext#7147
This commit is contained in:
commit
3bb9af2801
14 changed files with 180 additions and 62 deletions
|
|
@ -236,7 +236,7 @@ def errprint(msg):
|
|||
|
||||
:param msg: Message."""
|
||||
msg = as_unicode(msg)
|
||||
if not request or (not "cmd" in local.form_dict):
|
||||
if not request or (not "cmd" in local.form_dict) or conf.developer_mode:
|
||||
print msg.encode('utf-8')
|
||||
|
||||
error_log.append(msg)
|
||||
|
|
|
|||
|
|
@ -59,27 +59,37 @@ class Report(Document):
|
|||
make_boilerplate("controller.py", self, {"name": self.name})
|
||||
make_boilerplate("controller.js", self, {"name": self.name})
|
||||
|
||||
def get_data(self, filters=None, limit=None, user=None):
|
||||
|
||||
def get_data(self, filters=None, limit=None, user=None, as_dict=False):
|
||||
columns = []
|
||||
out = []
|
||||
|
||||
if self.report_type in ('Query Report', 'Script Report'):
|
||||
# query and script reports
|
||||
data = frappe.desk.query_report.run(self.name, filters=filters, user=user)
|
||||
columns_list = []
|
||||
for d in data.get('columns'):
|
||||
if isinstance(d, dict):
|
||||
columns_list.append(d.get('label'))
|
||||
columns.append(frappe._dict(d))
|
||||
else:
|
||||
columns_list.append(d.split(':')[0])
|
||||
parts = d.split(':')
|
||||
fieldtype, options = parts[1], None
|
||||
if fieldtype and '/' in fieldtype:
|
||||
fieldtype, options = fieldtype.split('/')
|
||||
|
||||
columns.append(frappe._dict(label=parts[0], fieldtype=fieldtype, fieldname=parts[0]))
|
||||
|
||||
out.append(columns_list)
|
||||
out += data.get('result')
|
||||
else:
|
||||
# standard report
|
||||
params = json.loads(self.json)
|
||||
columns = params.get('columns')
|
||||
filters = params.get('filters')
|
||||
_filters = params.get('filters') or []
|
||||
|
||||
if filters:
|
||||
for key, value in filters.iteritems():
|
||||
condition, _value = '=', value
|
||||
if isinstance(value, (list, tuple)):
|
||||
condition, _value = value
|
||||
_filters.append([key, condition, _value])
|
||||
|
||||
def _format(parts):
|
||||
# sort by is saved as DocType.fieldname, covert it to sql
|
||||
|
|
@ -90,14 +100,26 @@ class Report(Document):
|
|||
order_by += ', ' + _format(params.get('sort_by_next').split('.')) + ' ' + params.get('sort_order_next')
|
||||
|
||||
result = frappe.get_list(self.ref_doctype, fields = [_format([c[1], c[0]]) for c in columns],
|
||||
filters=filters, order_by = order_by, as_list=True, limit=limit, user=user)
|
||||
filters=_filters, order_by = order_by, as_list=True, limit=limit, user=user)
|
||||
|
||||
meta = frappe.get_meta(self.ref_doctype)
|
||||
|
||||
out.append([meta.get_label(c[0]) for c in columns])
|
||||
columns = [meta.get_field(c[0]) or frappe._dict(label=meta.get_label(c[0]), fieldname=c[0])
|
||||
for c in columns]
|
||||
|
||||
out = out + [list(d) for d in result]
|
||||
|
||||
return out
|
||||
if as_dict:
|
||||
data = []
|
||||
for row in out:
|
||||
_row = frappe._dict()
|
||||
data.append(_row)
|
||||
for i, val in enumerate(row):
|
||||
_row[columns[i].get('fieldname')] = val
|
||||
else:
|
||||
data = out
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
@Document.whitelist
|
||||
|
|
|
|||
|
|
@ -16,15 +16,15 @@ class TestReport(unittest.TestCase):
|
|||
frappe.get_doc(json.loads(f.read())).insert()
|
||||
|
||||
report = frappe.get_doc('Report', 'User Activity Report')
|
||||
data = report.get_data()
|
||||
self.assertEquals(data[0][0], 'ID')
|
||||
self.assertEquals(data[0][1], 'User Type')
|
||||
columns, data = report.get_data()
|
||||
self.assertEquals(columns[0].get('label'), 'ID')
|
||||
self.assertEquals(columns[1].get('label'), 'User Type')
|
||||
self.assertTrue('Administrator' in [d[0] for d in data])
|
||||
|
||||
def test_query_report(self):
|
||||
report = frappe.get_doc('Report', 'Permitted Documents For User')
|
||||
data = report.get_data(filters={'user': 'Administrator', 'doctype': 'DocType'})
|
||||
self.assertEquals(data[0][0], 'Name')
|
||||
self.assertEquals(data[0][1], 'Module')
|
||||
columns, data = report.get_data(filters={'user': 'Administrator', 'doctype': 'DocType'})
|
||||
self.assertEquals(columns[0].get('label'), 'Name')
|
||||
self.assertEquals(columns[1].get('label'), 'Module')
|
||||
self.assertTrue('User' in [d[0] for d in data])
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@ frappe.ui.form.on('Auto Email Report', {
|
|||
}
|
||||
}
|
||||
},
|
||||
report: function(frm) {
|
||||
frm.set_value('filters', '');
|
||||
},
|
||||
show_filters: function(frm) {
|
||||
var wrapper = $(frm.get_field('filters_display').wrapper);
|
||||
wrapper.empty();
|
||||
|
|
|
|||
|
|
@ -185,6 +185,36 @@
|
|||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "24",
|
||||
"depends_on": "eval:doc.report_type=='Report Builder'",
|
||||
"fieldname": "data_modified_till",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Send Records Updated in Last X Hours",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
|
|
@ -539,7 +569,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2016-11-07 05:50:31.903959",
|
||||
"modified": "2016-12-26 12:46:41.193379",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Email",
|
||||
"name": "Auto Email Report",
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@
|
|||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import frappe, json
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from datetime import timedelta
|
||||
import frappe.utils
|
||||
from frappe.utils.xlsutils import get_xls
|
||||
from frappe.utils.csvutils import to_csv
|
||||
|
|
@ -42,29 +43,45 @@ class AutoEmailReport(Document):
|
|||
def get_report_content(self):
|
||||
'''Returns file in for the report in given format'''
|
||||
report = frappe.get_doc('Report', self.report)
|
||||
raw = report.get_data(limit=self.no_of_rows or 100, user = self.user, filters = self.filters)
|
||||
|
||||
if len(raw)==1 and self.send_if_data:
|
||||
if self.report_type=='Report Builder' and self.data_modified_till:
|
||||
self.filters = json.loads(self.filters) if self.filters else {}
|
||||
self.filters['modified'] = ('>', frappe.utils.now_datetime() - timedelta(hours=self.data_modified_till))
|
||||
|
||||
columns, data = report.get_data(limit=self.no_of_rows or 100, user = self.user,
|
||||
filters = self.filters, as_dict=True)
|
||||
|
||||
if len(data)==1 and self.send_if_data:
|
||||
return None
|
||||
|
||||
if self.format == 'HTML':
|
||||
return self.get_html_table(raw)
|
||||
return self.get_html_table(columns, data)
|
||||
|
||||
elif self.format == 'XLS':
|
||||
return get_xls(raw)
|
||||
return get_xls(columns, data)
|
||||
|
||||
elif self.format == 'CSV':
|
||||
return to_csv(raw)
|
||||
return self.get_csv(columns, data)
|
||||
|
||||
else:
|
||||
frappe.throw(_('Invalid Output Format'))
|
||||
|
||||
def get_html_table(self, data):
|
||||
def get_html_table(self, columns, data):
|
||||
return frappe.render_template('frappe/templates/includes/print_table.html', {
|
||||
'headings': data[0],
|
||||
'columns': columns,
|
||||
'data': data[1:]
|
||||
})
|
||||
|
||||
def get_csv(self, columns, data):
|
||||
out = [[df.label for df in columns], ]
|
||||
for row in data:
|
||||
new_row = []
|
||||
out.append(new_row)
|
||||
for df in columns:
|
||||
new_row.append(frappe.format(row[df.fieldname], df, row))
|
||||
|
||||
return to_csv(out)
|
||||
|
||||
def get_file_name(self):
|
||||
return "{0}.{1}".format(self.report.replace(" ", "-").replace("/", "-"), self.format.lower())
|
||||
|
||||
|
|
|
|||
|
|
@ -9,4 +9,31 @@ import unittest, json
|
|||
# test_records = frappe.get_test_records('Auto Email Report')
|
||||
|
||||
class TestAutoEmailReport(unittest.TestCase):
|
||||
pass
|
||||
def test_auto_email(self):
|
||||
frappe.delete_doc('Auto Email Report', 'Permitted Documents For User')
|
||||
|
||||
auto_email_report = frappe.get_doc(dict(
|
||||
doctype='Auto Email Report',
|
||||
report='Permitted Documents For User',
|
||||
report_type='Script Report',
|
||||
user='Administrator',
|
||||
enabled=1,
|
||||
email_to='test@example.com',
|
||||
format='HTML',
|
||||
frequency='Daily',
|
||||
filters=json.dumps(dict(user='Administrator', doctype='DocType'))
|
||||
)).insert()
|
||||
|
||||
data = auto_email_report.get_report_content()
|
||||
self.assertTrue('<td>DocShare</td>' in data)
|
||||
self.assertTrue('<td>Core</td>' in data)
|
||||
|
||||
auto_email_report.format = 'CSV'
|
||||
|
||||
data = auto_email_report.get_report_content()
|
||||
self.assertTrue('"Language","Core"' in data)
|
||||
|
||||
auto_email_report.format = 'XLS'
|
||||
|
||||
data = auto_email_report.get_report_content()
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from frappe.utils import flt, cint, getdate, get_datetime, get_time, make_filter
|
|||
from frappe import _
|
||||
from frappe.model import optional_fields
|
||||
from frappe.model.utils.list_settings import get_list_settings, update_list_settings
|
||||
from datetime import datetime
|
||||
|
||||
class DatabaseQuery(object):
|
||||
def __init__(self, doctype):
|
||||
|
|
@ -297,11 +298,12 @@ class DatabaseQuery(object):
|
|||
get_datetime(f.value[0]).strftime("%Y-%m-%d %H:%M:%S.%f"),
|
||||
add_to_date(get_datetime(f.value[1]),days=1).strftime("%Y-%m-%d %H:%M:%S.%f"))
|
||||
fallback = "'0000-00-00 00:00:00'"
|
||||
|
||||
elif df and df.fieldtype=="Date":
|
||||
value = getdate(f.value).strftime("%Y-%m-%d")
|
||||
fallback = "'0000-00-00'"
|
||||
|
||||
elif df and df.fieldtype=="Datetime":
|
||||
elif (df and df.fieldtype=="Datetime") or isinstance(f.value, datetime):
|
||||
value = get_datetime(f.value).strftime("%Y-%m-%d %H:%M:%S.%f")
|
||||
fallback = "'0000-00-00 00:00:00'"
|
||||
|
||||
|
|
|
|||
|
|
@ -849,10 +849,6 @@ li .footer-child-item {
|
|||
.blog-text p {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.blogger-name {
|
||||
margin-bottom: 0px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
.comment-view {
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
|
@ -921,3 +917,12 @@ li .footer-child-item {
|
|||
margin-top: -10px;
|
||||
margin-right: -8px;
|
||||
}
|
||||
.page-card {
|
||||
max-width: 360px;
|
||||
padding: 30px;
|
||||
margin: auto;
|
||||
border: 1px solid #d1d8dd;
|
||||
border-radius: 4px;
|
||||
margin: 0 auto;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -584,11 +584,6 @@ li .footer-child-item {
|
|||
}
|
||||
}
|
||||
|
||||
.blogger-name {
|
||||
margin-bottom:0px;
|
||||
margin-top:0px;
|
||||
}
|
||||
|
||||
.comment-view {
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
|
@ -661,3 +656,13 @@ li .footer-child-item {
|
|||
margin-top: -10px;
|
||||
margin-right: -8px;
|
||||
}
|
||||
|
||||
.page-card {
|
||||
max-width: 360px;
|
||||
padding: 30px;
|
||||
margin: auto;
|
||||
border: 1px solid @border-color;
|
||||
border-radius: 4px;
|
||||
margin: 0 auto;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
/* login-css */
|
||||
|
||||
body {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.page-sidebar, #wrap-footer, .page-header {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -36,13 +44,6 @@
|
|||
}
|
||||
|
||||
.form-signin {
|
||||
max-width: 360px;
|
||||
padding-right: 30px;
|
||||
padding-left: 30px;
|
||||
padding-top: 50px;
|
||||
margin: 0 auto;
|
||||
border-radius: 5px;
|
||||
background-color: #fff;
|
||||
}
|
||||
.form-signin .form-signin-heading,
|
||||
.form-signin .checkbox {
|
||||
|
|
@ -96,13 +97,11 @@ h5:before {
|
|||
left: 0;
|
||||
}
|
||||
.login_header{
|
||||
font-size: 36px;
|
||||
font-size: 30px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
margin-bottom:20px;
|
||||
text-transform: uppercase;
|
||||
margin: 10px 0px 30px 0px;
|
||||
letter-spacing: 0.5px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
p{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
{% macro get_align(col) %}
|
||||
{%- if col.fieldtype in ('Int', 'Float', 'Currency', 'Check') %} style='text-align: right'{% endif -%}
|
||||
{% endmacro %}
|
||||
|
||||
<table cellpadding=2px cellspacing=0 border=1px style='width:100%; border-collapse:collapse;'>
|
||||
<thead>
|
||||
<tr>
|
||||
{% for col in headings %}
|
||||
<th>
|
||||
{{ col }}
|
||||
{% for col in columns %}
|
||||
<th {{- get_align(col) }}>
|
||||
{{- col.label -}}
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
|
|
@ -11,9 +15,9 @@
|
|||
<tbody>
|
||||
{% for row in data %}
|
||||
<tr>
|
||||
{% for val in row %}
|
||||
<td>
|
||||
{{ frappe.format(val) }}
|
||||
{% for col in columns %}
|
||||
<td {{- get_align(col) }}>
|
||||
{{- frappe.format(row[col.fieldname], col, row) -}}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
|||
import frappe, xlwt, StringIO, datetime
|
||||
from frappe import _
|
||||
|
||||
def get_xls(data):
|
||||
def get_xls(columns, data):
|
||||
'''Convert data to xls'''
|
||||
stream = StringIO.StringIO()
|
||||
workbook = xlwt.Workbook()
|
||||
|
|
@ -14,18 +14,22 @@ def get_xls(data):
|
|||
(frappe.defaults.get_global_default("date_format") or "yyyy-mm-dd"))
|
||||
bold = xlwt.easyxf('font: bold 1')
|
||||
|
||||
# header
|
||||
for i, col in enumerate(columns):
|
||||
sheet.write(0, i, col.label, bold)
|
||||
|
||||
for i, row in enumerate(data):
|
||||
for j, val in enumerate(row):
|
||||
for j, df in enumerate(columns):
|
||||
f = None
|
||||
|
||||
val = row[columns[j].fieldname]
|
||||
if isinstance(val, (datetime.datetime, datetime.date)):
|
||||
f = dateformat
|
||||
if i==0:
|
||||
f = bold
|
||||
|
||||
if f:
|
||||
sheet.write(i, j, val, f)
|
||||
sheet.write(i+1, j, val, f)
|
||||
else:
|
||||
sheet.write(i, j, val)
|
||||
sheet.write(i+1, j, frappe.format(val, df, row))
|
||||
|
||||
workbook.save(stream)
|
||||
stream.seek(0)
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@
|
|||
{% block page_content %}
|
||||
<!-- {{ for_test }} -->
|
||||
|
||||
<div class="login-content">
|
||||
<div class="login-content page-card">
|
||||
<form class="form-signin form-login" role="form">
|
||||
|
||||
<div class="login_header">Login</div>
|
||||
<div class="login_header">Sign In</div>
|
||||
|
||||
<input type="text" id="login_email"
|
||||
class="form-control" placeholder="{{ _('Email address') }}" required autofocus>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue