ソースを参照

[fix] formatting and modified filters for auto email report, fixes frappe/erpnext#7081 and frappe/erpnext#7147

version-14
Rushabh Mehta 8年前
コミット
eabceaec56
14個のファイルの変更179行の追加58行の削除
  1. +1
    -1
      frappe/__init__.py
  2. +33
    -10
      frappe/core/doctype/report/report.py
  3. +2
    -2
      frappe/core/doctype/report/test_report.py
  4. +3
    -0
      frappe/email/doctype/auto_email_report/auto_email_report.js
  5. +31
    -1
      frappe/email/doctype/auto_email_report/auto_email_report.json
  6. +25
    -8
      frappe/email/doctype/auto_email_report/auto_email_report.py
  7. +28
    -1
      frappe/email/doctype/auto_email_report/test_auto_email_report.py
  8. +5
    -1
      frappe/model/db_query.py
  9. +9
    -4
      frappe/public/css/website.css
  10. +10
    -5
      frappe/public/less/website.less
  11. +10
    -11
      frappe/templates/includes/login/login.css
  12. +10
    -6
      frappe/templates/includes/print_table.html
  13. +10
    -6
      frappe/utils/xlsutils.py
  14. +2
    -2
      frappe/www/login.html

+ 1
- 1
frappe/__init__.py ファイルの表示

@@ -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)


+ 33
- 10
frappe/core/doctype/report/report.py ファイルの表示

@@ -59,27 +59,38 @@ 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:
print 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 +101,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, debug=True)

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


+ 2
- 2
frappe/core/doctype/report/test_report.py ファイルの表示

@@ -16,14 +16,14 @@ class TestReport(unittest.TestCase):
frappe.get_doc(json.loads(f.read())).insert()

report = frappe.get_doc('Report', 'User Activity Report')
data = report.get_data()
columns, data = report.get_data()
self.assertEquals(data[0][0], 'ID')
self.assertEquals(data[0][1], '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'})
columns, data = report.get_data(filters={'user': 'Administrator', 'doctype': 'DocType'})
self.assertEquals(data[0][0], 'Name')
self.assertEquals(data[0][1], 'Module')
self.assertTrue('User' in [d[0] for d in data])


+ 3
- 0
frappe/email/doctype/auto_email_report/auto_email_report.js ファイルの表示

@@ -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();


+ 31
- 1
frappe/email/doctype/auto_email_report/auto_email_report.json ファイルの表示

@@ -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",


+ 25
- 8
frappe/email/doctype/auto_email_report/auto_email_report.py ファイルの表示

@@ -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())



+ 28
- 1
frappe/email/doctype/auto_email_report/test_auto_email_report.py ファイルの表示

@@ -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()


+ 5
- 1
frappe/model/db_query.py ファイルの表示

@@ -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):
@@ -263,6 +264,8 @@ class DatabaseQuery(object):

f = get_filter(self.doctype, f)

print f

tname = ('`tab' + f.doctype + '`')
if not tname in self.tables:
self.append_table(tname)
@@ -297,11 +300,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'"



+ 9
- 4
frappe/public/css/website.css ファイルの表示

@@ -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;
}

+ 10
- 5
frappe/public/less/website.less ファイルの表示

@@ -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;
}

+ 10
- 11
frappe/templates/includes/login/login.css ファイルの表示

@@ -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{


+ 10
- 6
frappe/templates/includes/print_table.html ファイルの表示

@@ -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>


+ 10
- 6
frappe/utils/xlsutils.py ファイルの表示

@@ -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)


+ 2
- 2
frappe/www/login.html ファイルの表示

@@ -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>


読み込み中…
キャンセル
保存