@@ -61,6 +61,7 @@ def get_script(report_name): | |||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def run(report_name, filters=None, user=None): | def run(report_name, filters=None, user=None): | ||||
report = get_report_doc(report_name) | report = get_report_doc(report_name) | ||||
if not user: | if not user: | ||||
user = frappe.session.user | user = frappe.session.user | ||||
@@ -111,6 +112,40 @@ def run(report_name, filters=None, user=None): | |||||
"chart": chart | "chart": chart | ||||
} | } | ||||
@frappe.whitelist() | |||||
def export_query(): | |||||
"""export from query reports""" | |||||
data = frappe._dict(frappe.local.form_dict) | |||||
del data["cmd"] | |||||
if isinstance(data.get("filters"), basestring): | |||||
filters = json.loads(data["filters"]) | |||||
if isinstance(data.get("report_name"), basestring): | |||||
report_name = data["report_name"] | |||||
if isinstance(data.get("file_format_type"), basestring): | |||||
file_format_type = data["file_format_type"] | |||||
if file_format_type == "Excel": | |||||
data = run(report_name, filters) | |||||
data = frappe._dict(data) | |||||
columns = get_columns_dict(data.columns) | |||||
content = [] | |||||
for col in columns.values(): | |||||
content.append(col["label"]) | |||||
from frappe.utils.xlsxutils import make_xlsx | |||||
xlsx_file = make_xlsx([content] + data.result, "Query Report") | |||||
frappe.response['filename'] = report_name + '.xlsx' | |||||
frappe.response['filecontent'] = xlsx_file.getvalue() | |||||
frappe.response['type'] = 'binary' | |||||
def get_report_module_dotted_path(module, report_name): | def get_report_module_dotted_path(module, report_name): | ||||
return frappe.local.module_app[scrub(module)] + "." + scrub(module) \ | return frappe.local.module_app[scrub(module)] + "." + scrub(module) \ | ||||
+ ".report." + scrub(report_name) + "." + scrub(report_name) | + ".report." + scrub(report_name) + "." + scrub(report_name) | ||||
@@ -166,6 +201,7 @@ def add_total_row(result, columns, meta = None): | |||||
result.append(total_row) | result.append(total_row) | ||||
return result | return result | ||||
def get_filtered_data(ref_doctype, columns, data, user): | def get_filtered_data(ref_doctype, columns, data, user): | ||||
result = [] | result = [] | ||||
linked_doctypes = get_linked_doctypes(columns, data) | linked_doctypes = get_linked_doctypes(columns, data) | ||||
@@ -189,6 +225,7 @@ def get_filtered_data(ref_doctype, columns, data, user): | |||||
return result | return result | ||||
def has_match(row, linked_doctypes, doctype_match_filters, ref_doctype, if_owner, columns_dict, user): | def has_match(row, linked_doctypes, doctype_match_filters, ref_doctype, if_owner, columns_dict, user): | ||||
"""Returns True if after evaluating permissions for each linked doctype | """Returns True if after evaluating permissions for each linked doctype | ||||
- There is an owner match for the ref_doctype | - There is an owner match for the ref_doctype | ||||
@@ -297,6 +334,7 @@ def get_columns_dict(columns): | |||||
else: | else: | ||||
col_dict["fieldtype"] = col[1] | col_dict["fieldtype"] = col[1] | ||||
col_dict["label"] = col[0] | |||||
col_dict["fieldname"] = frappe.scrub(col[0]) | col_dict["fieldname"] = frappe.scrub(col[0]) | ||||
# dict | # dict | ||||
@@ -91,8 +91,10 @@ def export_query(): | |||||
form_params["as_list"] = True | form_params["as_list"] = True | ||||
doctype = form_params.doctype | doctype = form_params.doctype | ||||
add_totals_row = None | add_totals_row = None | ||||
file_format_type = form_params["file_format_type"] | |||||
del form_params["doctype"] | del form_params["doctype"] | ||||
del form_params["file_format_type"] | |||||
if 'add_totals_row' in form_params and form_params['add_totals_row']=='1': | if 'add_totals_row' in form_params and form_params['add_totals_row']=='1': | ||||
add_totals_row = 1 | add_totals_row = 1 | ||||
@@ -110,20 +112,32 @@ def export_query(): | |||||
for i, row in enumerate(ret): | for i, row in enumerate(ret): | ||||
data.append([i+1] + list(row)) | data.append([i+1] + list(row)) | ||||
# convert to csv | |||||
from cStringIO import StringIO | |||||
import csv | |||||
if file_format_type == "CSV": | |||||
f = StringIO() | |||||
writer = csv.writer(f) | |||||
for r in data: | |||||
# encode only unicode type strings and not int, floats etc. | |||||
writer.writerow(map(lambda v: isinstance(v, unicode) and v.encode('utf-8') or v, r)) | |||||
# convert to csv | |||||
import csv | |||||
from cStringIO import StringIO | |||||
f = StringIO() | |||||
writer = csv.writer(f) | |||||
for r in data: | |||||
# encode only unicode type strings and not int, floats etc. | |||||
writer.writerow(map(lambda v: isinstance(v, unicode) and v.encode('utf-8') or v, r)) | |||||
f.seek(0) | |||||
frappe.response['result'] = unicode(f.read(), 'utf-8') | |||||
frappe.response['type'] = 'csv' | |||||
frappe.response['doctype'] = doctype | |||||
elif file_format_type == "Excel": | |||||
from frappe.utils.xlsxutils import make_xlsx | |||||
xlsx_file = make_xlsx(data, doctype) | |||||
frappe.response['filename'] = doctype + '.xlsx' | |||||
frappe.response['filecontent'] = xlsx_file.getvalue() | |||||
frappe.response['type'] = 'binary' | |||||
f.seek(0) | |||||
frappe.response['result'] = unicode(f.read(), 'utf-8') | |||||
frappe.response['type'] = 'csv' | |||||
frappe.response['doctype'] = doctype | |||||
def append_totals_row(data): | def append_totals_row(data): | ||||
if not data: | if not data: | ||||
@@ -88,7 +88,7 @@ frappe.views.QueryReport = Class.extend({ | |||||
}, me.report_doc.letter_head); | }, me.report_doc.letter_head); | ||||
}, true); | }, true); | ||||
this.page.add_menu_item(__('Export'), function() { me.export_report(); }, | |||||
this.page.add_menu_item(__('Export'), function() { me.make_export(); }, | |||||
true); | true); | ||||
this.page.add_menu_item(__("Setup Auto Email"), function() { | this.page.add_menu_item(__("Setup Auto Email"), function() { | ||||
@@ -784,18 +784,49 @@ frappe.views.QueryReport = Class.extend({ | |||||
} | } | ||||
}); | }); | ||||
}, | }, | ||||
export_report: function() { | |||||
make_export: function() { | |||||
var me = this; | |||||
this.title = this.report_name; | |||||
if(!frappe.model.can_export(this.report_doc.ref_doctype)) { | if(!frappe.model.can_export(this.report_doc.ref_doctype)) { | ||||
msgprint(__("You are not allowed to export this report")); | msgprint(__("You are not allowed to export this report")); | ||||
return false; | return false; | ||||
} | } | ||||
var result = $.map(frappe.slickgrid_tools.get_view_data(this.columns, this.dataView), | |||||
function(row) { | |||||
return [row.splice(1)]; | |||||
}); | |||||
this.title = this.report_name; | |||||
frappe.tools.downloadify(result, null, this.title); | |||||
frappe.prompt({fieldtype:"Select", label: __("Select File Type"), fieldname:"file_format_type", | |||||
options:"Excel\nCSV", default:"Excel", reqd: 1}, | |||||
function(data) { | |||||
if (data.file_format_type == "CSV") { | |||||
var result = $.map(frappe.slickgrid_tools.get_view_data(me.columns, me.dataView), | |||||
function(row) { | |||||
return [row.splice(1)]; | |||||
}); | |||||
frappe.tools.downloadify(result, null, me.title); | |||||
} | |||||
else if (data.file_format_type == "Excel") { | |||||
me.wrapper.find(".results").toggle(false); | |||||
try { | |||||
var filters = me.get_values(true); | |||||
} catch(e) { | |||||
return; | |||||
} | |||||
var args = { | |||||
cmd: 'frappe.desk.query_report.export_query', | |||||
report_name: me.report_name, | |||||
file_format_type: data.file_format_type, | |||||
filters: filters | |||||
}; | |||||
open_url_post(frappe.request.url, args); | |||||
} | |||||
}, __("Export Report: "+ me.title), __("Download")); | |||||
return false; | return false; | ||||
}, | }, | ||||
@@ -265,6 +265,7 @@ frappe.views.ReportView = frappe.ui.Listing.extend({ | |||||
filters: this.filter_list.get_filters(), | filters: this.filter_list.get_filters(), | ||||
save_list_settings_fields: 1, | save_list_settings_fields: 1, | ||||
with_childnames: 1, | with_childnames: 1, | ||||
file_format_type: this.file_format_type | |||||
} | } | ||||
}, | }, | ||||
@@ -676,11 +677,22 @@ frappe.views.ReportView = frappe.ui.Listing.extend({ | |||||
} | } | ||||
var export_btn = this.page.add_menu_item(__('Export'), function() { | var export_btn = this.page.add_menu_item(__('Export'), function() { | ||||
var args = me.get_args(); | var args = me.get_args(); | ||||
args.cmd = 'frappe.desk.reportview.export_query' | |||||
if(me.add_totals_row) { | |||||
args.add_totals_row = 1; | |||||
} | |||||
open_url_post(frappe.request.url, args); | |||||
frappe.prompt({fieldtype:"Select", label: __("Select File Type"), fieldname:"file_format_type", | |||||
options:"Excel\nCSV", default:"Excel", reqd: 1}, | |||||
function(data) { | |||||
args.cmd = 'frappe.desk.reportview.export_query'; | |||||
args.file_format_type = data.file_format_type; | |||||
if(me.add_totals_row) { | |||||
args.add_totals_row = 1; | |||||
} | |||||
open_url_post(frappe.request.url, args); | |||||
}, __("Export Report: " + me.doctype), __("Download")); | |||||
}, true); | }, true); | ||||
}, | }, | ||||
@@ -36,7 +36,8 @@ def build_response(response_type=None): | |||||
'download': as_raw, | 'download': as_raw, | ||||
'json': as_json, | 'json': as_json, | ||||
'page': as_page, | 'page': as_page, | ||||
'redirect': redirect | |||||
'redirect': redirect, | |||||
'binary': as_binary | |||||
} | } | ||||
return response_type_map[frappe.response.get('type') or response_type]() | return response_type_map[frappe.response.get('type') or response_type]() | ||||
@@ -68,6 +69,13 @@ def as_json(): | |||||
response.data = json.dumps(frappe.local.response, default=json_handler, separators=(',',':')) | response.data = json.dumps(frappe.local.response, default=json_handler, separators=(',',':')) | ||||
return response | return response | ||||
def as_binary(): | |||||
response = Response() | |||||
response.mimetype = 'application/octet-stream' | |||||
response.headers[b"Content-Disposition"] = ("filename=\"%s\"" % frappe.response['filename'].replace(' ', '_')).encode("utf-8") | |||||
response.data = frappe.response['filecontent'] | |||||
return response | |||||
def make_logs(response = None): | def make_logs(response = None): | ||||
"""make strings for msgprint and errprint""" | """make strings for msgprint and errprint""" | ||||
if not response: | if not response: | ||||
@@ -0,0 +1,26 @@ | |||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors | |||||
# MIT License. See license.txt | |||||
from __future__ import unicode_literals | |||||
import frappe | |||||
from frappe.utils import encode, cstr, cint, flt, comma_or | |||||
import openpyxl | |||||
from cStringIO import StringIO | |||||
from openpyxl.styles import Font | |||||
# return xlsx file object | |||||
def make_xlsx(data, sheet_name): | |||||
wb = openpyxl.Workbook(write_only=True) | |||||
ws = wb.create_sheet(sheet_name, 0) | |||||
row1 = ws.row_dimensions[1] | |||||
row1.font = Font(name='Calibri',bold=True) | |||||
for row in data: | |||||
ws.append(row) | |||||
xlsx_file = StringIO() | |||||
wb.save(xlsx_file) | |||||
return xlsx_file |
@@ -41,3 +41,4 @@ xlwt | |||||
oauthlib | oauthlib | ||||
PyJWT | PyJWT | ||||
pypdf | pypdf | ||||
openpyxl |