diff --git a/frappe/__init__.py b/frappe/__init__.py index 513c444ab9..ac1472de75 100644 --- a/frappe/__init__.py +++ b/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) diff --git a/frappe/core/doctype/report/report.py b/frappe/core/doctype/report/report.py index 3ed44a24a0..4aeae4503d 100644 --- a/frappe/core/doctype/report/report.py +++ b/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 diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py index 6168672733..68d48e5bf6 100644 --- a/frappe/core/doctype/report/test_report.py +++ b/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]) diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.js b/frappe/email/doctype/auto_email_report/auto_email_report.js index 019cdc6a52..c565490193 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.js +++ b/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(); diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.json b/frappe/email/doctype/auto_email_report/auto_email_report.json index 03a28175bd..113ade1c3a 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.json +++ b/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", diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.py b/frappe/email/doctype/auto_email_report/auto_email_report.py index 710d6ddc3f..f4a9413c2e 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.py +++ b/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()) diff --git a/frappe/email/doctype/auto_email_report/test_auto_email_report.py b/frappe/email/doctype/auto_email_report/test_auto_email_report.py index 72da43098b..1436626430 100644 --- a/frappe/email/doctype/auto_email_report/test_auto_email_report.py +++ b/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 \ No newline at end of file + 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('DocShare' in data) + self.assertTrue('Core' 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() + diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index d64eef4976..cbfebf0b80 100644 --- a/frappe/model/db_query.py +++ b/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'" diff --git a/frappe/public/css/website.css b/frappe/public/css/website.css index 9972ea7887..388242e8d1 100644 --- a/frappe/public/css/website.css +++ b/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; +} diff --git a/frappe/public/less/website.less b/frappe/public/less/website.less index 677ccc7624..b63d94700d 100644 --- a/frappe/public/less/website.less +++ b/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; +} diff --git a/frappe/templates/includes/login/login.css b/frappe/templates/includes/login/login.css index 3cf72a1753..ce9b84c288 100644 --- a/frappe/templates/includes/login/login.css +++ b/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{ diff --git a/frappe/templates/includes/print_table.html b/frappe/templates/includes/print_table.html index 4a542ceb02..d2a2f55543 100644 --- a/frappe/templates/includes/print_table.html +++ b/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 %} + - {% for col in headings %} - {% endfor %} @@ -11,9 +15,9 @@ {% for row in data %} - {% for val in row %} - {% endfor %} diff --git a/frappe/utils/xlsutils.py b/frappe/utils/xlsutils.py index 643aadef3a..bff7807308 100644 --- a/frappe/utils/xlsutils.py +++ b/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) diff --git a/frappe/www/login.html b/frappe/www/login.html index 69da9034bf..ab0ba62ad8 100644 --- a/frappe/www/login.html +++ b/frappe/www/login.html @@ -9,10 +9,10 @@ {% block page_content %} -
+
- +
- {{ col }} + {% for col in columns %} + + {{- col.label -}}
- {{ frappe.format(val) }} + {% for col in columns %} + + {{- frappe.format(row[col.fieldname], col, row) -}}