ソースを参照

Data Import Tool in excel file format (#3231)

* Data Import in excel file format

* Include test case and minor fixes

* typos
version-14
Manas Solanki 8年前
committed by Rushabh Mehta
コミット
5315bea77f
7個のファイルの変更98行の追加17行の削除
  1. +1
    -1
      frappe/core/page/data_import_tool/README.md
  2. +11
    -1
      frappe/core/page/data_import_tool/data_import_main.html
  3. +5
    -2
      frappe/core/page/data_import_tool/data_import_tool.js
  4. +26
    -6
      frappe/core/page/data_import_tool/exporter.py
  5. +21
    -5
      frappe/core/page/data_import_tool/importer.py
  6. +11
    -0
      frappe/tests/test_data_import.py
  7. +23
    -2
      frappe/utils/xlsxutils.py

+ 1
- 1
frappe/core/page/data_import_tool/README.md ファイルの表示

@@ -1 +1 @@
Bulk import / update of data via file upload in CSV.
Bulk import / update of data via file upload in Excel or CSV.

+ 11
- 1
frappe/core/page/data_import_tool/data_import_main.html ファイルの表示

@@ -46,11 +46,21 @@
<h6 class="text-muted">{%= __("Recommended bulk editing records via import, or understanding the import format.") %}</h6> <h6 class="text-muted">{%= __("Recommended bulk editing records via import, or understanding the import format.") %}</h6>
</div> </div>
</div> </div>
<div class="row">
<div class="col-sm-4">
<div class="checkbox" style="margin: 5px 0px;">
<label>
<input type="checkbox" class="excel-check" data-fieldname="excel_check" checked>
<small>{%= __("Download in Excel File Format") %}</small>
</label>
</div>
</div>
</div>
</div> </div>
<div> <div>
<hr style="margin-top: 50px;"> <hr style="margin-top: 50px;">
<h3>{%= __("Import") %}</h3> <h3>{%= __("Import") %}</h3>
<p class="text-muted">{%= __("Update the template and save in CSV (Comma Separate Values) format before attaching.") %}</p>
<p class="text-muted">{%= __("Update the template and save in downloaded format before attaching.") %}</p>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<br> <br>


+ 5
- 2
frappe/core/page/data_import_tool/data_import_tool.js ファイルの表示

@@ -99,7 +99,9 @@ frappe.DataImportTool = Class.extend({
parent_doctype: doctype, parent_doctype: doctype,
select_columns: JSON.stringify(columns), select_columns: JSON.stringify(columns),
with_data: with_data ? 'Yes' : 'No', with_data: with_data ? 'Yes' : 'No',
all_doctypes: 'Yes'
all_doctypes: 'Yes',
from_data_import: 'Yes',
excel_format: this.page.main.find(".excel-check").is(":checked") ? 'Yes' : 'No'
} }
}, },
make_upload: function() { make_upload: function() {
@@ -113,7 +115,8 @@ frappe.DataImportTool = Class.extend({
ignore_encoding_errors: me.page.main.find('[name="ignore_encoding_errors"]').prop("checked"), ignore_encoding_errors: me.page.main.find('[name="ignore_encoding_errors"]').prop("checked"),
overwrite: !me.page.main.find('[name="always_insert"]').prop("checked"), overwrite: !me.page.main.find('[name="always_insert"]').prop("checked"),
update_only: me.page.main.find('[name="update_only"]').prop("checked"), update_only: me.page.main.find('[name="update_only"]').prop("checked"),
no_email: me.page.main.find('[name="no_email"]').prop("checked")
no_email: me.page.main.find('[name="no_email"]').prop("checked"),
from_data_import: 'Yes'
} }
}, },
args: { args: {


+ 26
- 6
frappe/core/page/data_import_tool/exporter.py ファイルの表示

@@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe, json import frappe, json
from frappe import _ from frappe import _
import frappe.permissions import frappe.permissions
import re
import re, csv, os
from frappe.utils.csvutils import UnicodeWriter from frappe.utils.csvutils import UnicodeWriter
from frappe.utils import cstr, formatdate, format_datetime from frappe.utils import cstr, formatdate, format_datetime
from frappe.core.page.data_import_tool.data_import_tool import get_data_keys from frappe.core.page.data_import_tool.data_import_tool import get_data_keys
@@ -22,7 +22,8 @@ reflags = {
} }


@frappe.whitelist() @frappe.whitelist()
def get_template(doctype=None, parent_doctype=None, all_doctypes="No", with_data="No", select_columns=None):
def get_template(doctype=None, parent_doctype=None, all_doctypes="No", with_data="No", select_columns=None,
from_data_import="No", excel_format="No"):
all_doctypes = all_doctypes=="Yes" all_doctypes = all_doctypes=="Yes"
if select_columns: if select_columns:
select_columns = json.loads(select_columns); select_columns = json.loads(select_columns);
@@ -280,7 +281,26 @@ def get_template(doctype=None, parent_doctype=None, all_doctypes="No", with_data
add_field_headings() add_field_headings()
add_data() add_data()


# write out response as a type csv
frappe.response['result'] = cstr(w.getvalue())
frappe.response['type'] = 'csv'
frappe.response['doctype'] = doctype
if from_data_import == "Yes" and excel_format == "Yes":
filename = frappe.generate_hash("", 10)
with open(filename, 'wb') as f:
f.write(cstr(w.getvalue()).encode("utf-8"))
f = open(filename)
reader = csv.reader(f)

from frappe.utils.xlsxutils import make_xlsx
xlsx_file = make_xlsx(reader, "Data Import Template")

f.close()
os.remove(filename)

# write out response as a xlsx type
frappe.response['filename'] = doctype + '.xlsx'
frappe.response['filecontent'] = xlsx_file.getvalue()
frappe.response['type'] = 'binary'

else:
# write out response as a type csv
frappe.response['result'] = cstr(w.getvalue())
frappe.response['type'] = 'csv'
frappe.response['doctype'] = doctype

+ 21
- 5
frappe/core/page/data_import_tool/importer.py ファイルの表示

@@ -5,7 +5,7 @@ from __future__ import unicode_literals, print_function


from six.moves import range from six.moves import range
import requests import requests
import frappe, json
import frappe, json, os
import frappe.permissions import frappe.permissions
import frappe.async import frappe.async


@@ -20,7 +20,7 @@ from frappe.core.page.data_import_tool.data_import_tool import get_data_keys


@frappe.whitelist() @frappe.whitelist()
def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, no_email=True, overwrite=None, def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, no_email=True, overwrite=None,
update_only = None, ignore_links=False, pre_process=None, via_console=False):
update_only = None, ignore_links=False, pre_process=None, via_console=False, from_data_import="No"):
"""upload data""" """upload data"""


frappe.flags.in_import = True frappe.flags.in_import = True
@@ -37,11 +37,11 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False,
no_email = False no_email = False
if params.get('update_only'): if params.get('update_only'):
update_only = True update_only = True
if params.get('from_data_import'):
from_data_import = params.get('from_data_import')


frappe.flags.mute_emails = no_email frappe.flags.mute_emails = no_email


from frappe.utils.csvutils import read_csv_content_from_uploaded_file

def get_data_keys_definition(): def get_data_keys_definition():
return get_data_keys() return get_data_keys()


@@ -207,7 +207,23 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False,


# header # header
if not rows: if not rows:
rows = read_csv_content_from_uploaded_file(ignore_encoding_errors)
from frappe.utils.file_manager import save_uploaded
file_doc = save_uploaded(dt=None, dn="Data Import", folder='Home', is_private=1)
filename, file_extension = os.path.splitext(file_doc.file_name)

if file_extension == '.xlsx' and from_data_import == 'Yes':
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
rows = read_xlsx_file_from_attached_file(file_id=file_doc.name)

elif file_extension == '.csv':
from frappe.utils.file_manager import get_file
from frappe.utils.csvutils import read_csv_content
fname, fcontent = get_file(file_doc.names)
rows = read_csv_content(fcontent, ignore_encoding_errors)

else:
frappe.throw(_("Unsupported File Format"))

start_row = get_start_row() start_row = get_start_row()
header = rows[:start_row] header = rows[:start_row]
data = rows[start_row:] data = rows[start_row:]


+ 11
- 0
frappe/tests/test_data_import.py ファイルの表示

@@ -85,3 +85,14 @@ class TestDataImport(unittest.TestCase):
importer.upload(content) importer.upload(content)


ev = frappe.get_doc("Event", {"subject":"__Test Event with children"}) ev = frappe.get_doc("Event", {"subject":"__Test Event with children"})

def test_excel_import(self):
if frappe.db.exists("Event", "EV00001"):
frappe.delete_doc("Event", "EV00001")

exporter.get_template("Event", all_doctypes="No", with_data="No", from_data_import="Yes", excel_format="Yes")
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
content = read_xlsx_file_from_attached_file(fcontent=frappe.response.filecontent)
content.append(["", "EV00001", "_test", "Private", "05-11-2017 13:51:48", "0", "0", "", "1", "blue"])
importer.upload(content)
self.assertTrue(frappe.db.get_value("Event", "EV00001", "subject"), "_test")

+ 23
- 2
frappe/utils/xlsxutils.py ファイルの表示

@@ -8,6 +8,7 @@ from frappe.utils import encode, cstr, cint, flt, comma_or
import openpyxl import openpyxl
from cStringIO import StringIO from cStringIO import StringIO
from openpyxl.styles import Font from openpyxl.styles import Font
from openpyxl import load_workbook




# return xlsx file object # return xlsx file object
@@ -22,7 +23,7 @@ def make_xlsx(data, sheet_name):
for row in data: for row in data:
clean_row = [] clean_row = []
for item in row: for item in row:
if isinstance(item, basestring):
if isinstance(item, basestring) and sheet_name != "Data Import Template":
value = handle_html(item) value = handle_html(item)
else: else:
value = item value = item
@@ -36,7 +37,6 @@ def make_xlsx(data, sheet_name):




def handle_html(data): def handle_html(data):
# import html2text
from html2text import unescape, HTML2Text from html2text import unescape, HTML2Text


h = HTML2Text() h = HTML2Text()
@@ -53,3 +53,24 @@ def handle_html(data):
return value[0] return value[0]
else: else:
return value[1] return value[1]


def read_xlsx_file_from_attached_file(file_id=None, fcontent=None):
if file_id:
from frappe.utils.file_manager import get_file_path
filename = get_file_path(file_id)
elif fcontent:
from io import BytesIO
filename = BytesIO(fcontent)
else:
return

rows = []
wb1 = load_workbook(filename=filename, read_only=True)
ws1 = wb1.active
for row in ws1.iter_rows():
tmp_list = []
for cell in row:
tmp_list.append(cell.value)
rows.append(tmp_list)
return rows

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