This commit is contained in:
Faris Ansari 2019-08-26 12:58:27 +05:30
parent 0d6f256e03
commit 11d07b57b9
4 changed files with 136 additions and 61 deletions

View file

@ -40,6 +40,7 @@ class Importer:
self.header_row = None
self.data = None
# used to store date formats guessed from data rows per column
self._guessed_date_formats = {}
self.meta = frappe.get_meta(doctype)
self.prepare_content(file_path, content)
@ -191,7 +192,9 @@ class Importer:
for df in meta.fields:
if df.fieldtype not in no_value_fields:
# label as key
label = df.label if self.doctype == doctype else "{0} ({1})".format(df.label, df.parent)
label = (
df.label if self.doctype == doctype else "{0} ({1})".format(df.label, df.parent)
)
out[label] = df
# fieldname as key
if self.doctype == doctype:
@ -294,25 +297,35 @@ class Importer:
if missing_link_values:
return {"missing_link_values": missing_link_values}
if warnings:
return warnings
# parse import data
payloads = self.get_payloads_for_import(fields, data)
for doc in docs:
doc = self.process_doc(doc)
import_log.append({"inserted": True, "name": doc.name})
# collect warnings
warnings = []
for payload in payloads:
warnings += payload.warnings
if warnings:
return {"warnings": warnings}
# start import
print("Importing {0} rows...".format(len(data)))
for payload in payloads:
doc = payload.doc
row_indexes = [row[0] for row in payload.rows]
try:
doc = self.process_doc(doc)
import_log.append({"success": True, "docname": doc.name, "row_indexes": row_indexes})
except Exception as e:
import_log.append({"success": False, "exception": frappe.get_traceback(), "row_indexes": row_indexes})
self.data_import.db_set("import_log", json.dumps(import_log))
def get_docs_for_import(self, fields, data):
docs = []
parse_warnings = []
def get_payloads_for_import(self, fields, data):
payloads = []
while data:
doc, data, warnings = self.parse_next_row_for_import(fields, data)
if not warnings:
docs.append(doc)
else:
parse_warnings += warnings
return docs, parse_warnings
doc, rows, data, warnings = self.parse_next_row_for_import(fields, data)
payloads.append(frappe._dict(doc=doc, rows=rows, warnings=warnings))
return payloads
def parse_next_row_for_import(self, fields, data):
doc = {}
@ -383,7 +396,7 @@ class Importer:
table_field = table_dfs[0]
doc[table_field.fieldname] = docs
return doc, data[len(rows) :], warnings
return doc, rows, data[len(rows) :], warnings
def get_first_parent_column_index(self, fields):
"""
@ -406,7 +419,8 @@ class Importer:
pass
def insert_record(self, doc):
doc.update({"doctype": self.doctype})
# name shouldn't be set when inserting a new record
doc.update({"doctype": self.doctype, "name": None})
new_doc = frappe.get_doc(doc)
return new_doc.insert()
@ -415,6 +429,8 @@ class Importer:
def has_one_mandatory_field(doctype):
meta = frappe.get_meta(doctype)
# get mandatory fields with default not set
# mandatory_fields = [df for df in meta.fields if df.reqd and not df.default]
mandatory_fields = [df for df in meta.fields if df.reqd]
mandatory_fields_count = len(mandatory_fields)
if meta.autoname.lower() == "prompt":

View file

@ -6,7 +6,7 @@ frappe.ui.form.on('Data Import Beta', {
frm.page.hide_icon_group();
frm.trigger('import_file');
frm.trigger('reference_doctype');
frm.trigger('show_import_log');
// frm.trigger('show_import_log');
if (!frm.is_new()) {
frm.page.set_primary_action(__('Start Import'), () =>
frm.events.start_import(frm)
@ -43,34 +43,46 @@ frappe.ui.form.on('Data Import Beta', {
},
import_file(frm) {
if (frm.doc.import_file) {
$('<span class="text-muted">')
.html(__('Loading import file...'))
.appendTo(frm.get_field('import_preview').$wrapper);
frm
.call({
doc: frm.doc,
method: 'get_preview_from_template',
freeze: true,
freeze_message: __('Preparing Preview...')
})
.then(r => {
let preview_data = r.message;
frm.events.show_import_preview(frm, preview_data);
});
} else {
frm.get_field('import_preview').$wrapper.empty();
}
frm.toggle_display('section_import_preview', frm.doc.import_file);
if (!frm.doc.import_file) {
frm.get_field('import_preview').$wrapper.empty();
return;
}
// load import preview
$('<span class="text-muted">')
.html(__('Loading import file...'))
.appendTo(frm.get_field('import_preview').$wrapper);
frm
.call({
doc: frm.doc,
method: 'get_preview_from_template',
freeze: true,
freeze_message: __('Preparing Preview...')
})
.then(r => {
let preview_data = r.message;
frm.events.show_import_preview(frm, preview_data);
});
},
show_import_preview(frm, preview_data) {
let import_log = JSON.parse(frm.doc.import_log || '[]');
if (frm.import_preview) {
frm.import_preview.preview_data = preview_data;
frm.import_preview.import_log = import_log;
frm.import_preview.refresh();
return;
}
frappe.require('/assets/js/data_import_tools.min.js', () => {
frm.import_preview = new frappe.data_import.ImportPreview({
wrapper: frm.get_field('import_preview').$wrapper,
doctype: frm.doc.reference_doctype,
preview_data,
import_log,
events: {
remap_column(header_row_index, fieldname) {
let template_options = JSON.parse(frm.doc.template_options || '{}');
@ -123,9 +135,15 @@ frappe.ui.form.on('Data Import Beta', {
let import_log = JSON.parse(frm.doc.import_log);
let rows = import_log
.map(log => {
if (log.inserted) {
return `<tr>
<td>${log.name}</td>
<td>${log.inserted ? 'Inserted' : ''}</td>
</tr>`;
}
return `<tr>
<td>${log.name}</td>
<td>${log.inserted ? 'Inserted' : ''}</td>
<td>Failed</td>
<td><pre>${log.exception}</pre></td>
</tr>`;
})
.join('');

View file

@ -8,10 +8,14 @@ from frappe.model.document import Document
from frappe.core.doctype.data_import.importer_new import Importer
from frappe.core.doctype.data_import.exporter_new import Exporter
class DataImportBeta(Document):
def validate(self):
if not self.import_file:
self.import_json = ''
doc_before_save = self.get_doc_before_save()
if not self.import_file or (
doc_before_save and doc_before_save.import_file != self.import_file
):
self.template_options = ""
def get_preview_from_template(self):
if not self.import_file:
@ -44,7 +48,7 @@ class DataImportBeta(Document):
return docs
@frappe.whitelist()
def download_template(doctype, export_fields=None, export_records=None, export_filters=None, file_type='CSV'):
def download_template(doctype, export_fields=None, export_records=None, export_filters=None, file_type="CSV"):
"""
Download template from Exporter
:param doctype: Document Type
@ -57,10 +61,11 @@ def download_template(doctype, export_fields=None, export_records=None, export_f
export_fields = frappe.parse_json(export_fields)
export_filters = frappe.parse_json(export_filters)
e = Exporter(doctype,
e = Exporter(
doctype,
export_fields=export_fields,
export_data=True,
export_filters=export_filters,
file_type=file_type
file_type=file_type,
)
e.build_csv_response()

View file

@ -4,26 +4,41 @@ import ColumnPickerFields from './column_picker_fields';
frappe.provide('frappe.data_import');
const SVG_ICONS = {
'checkbox-circle-line': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="import-success">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-.997-4L6.76 11.757l1.414-1.414 2.829 2.829 5.656-5.657 1.415 1.414L11.003 16z"/>
</g>
</svg>`
};
frappe.data_import.ImportPreview = class ImportPreview {
constructor({ wrapper, doctype, preview_data, events = {} }) {
constructor({ wrapper, doctype, preview_data, import_log, events = {} }) {
frappe.import_preview = this;
this.wrapper = wrapper;
this.doctype = doctype;
this.header_row = preview_data.header_row;
this.preview_fields = preview_data.fields;
this.preview_data = preview_data.data;
this.preview_warnings = preview_data.warnings;
this.preview_data = preview_data;
this.events = events;
this.import_log = import_log;
frappe.model.with_doctype(doctype, () => {
this.make_wrapper();
this.prepare_columns();
this.prepare_data();
this.render_warnings(this.preview_warnings);
this.render_datatable();
this.refresh();
});
}
refresh() {
this.header_row = this.preview_data.header_row;
this.fields = this.preview_data.fields;
this.data = this.preview_data.data;
this.warnings = this.preview_data.warnings;
this.prepare_columns();
this.prepare_data();
this.render_warnings(this.warnings);
this.render_datatable();
}
make_wrapper() {
this.wrapper.html(`
<div>
@ -43,7 +58,7 @@ frappe.data_import.ImportPreview = class ImportPreview {
}
prepare_columns() {
this.columns = this.preview_fields.map((df, i) => {
this.columns = this.fields.map((df, i) => {
let header_row_index = i - 1;
if (df.skip_import) {
return {
@ -54,7 +69,13 @@ frappe.data_import.ImportPreview = class ImportPreview {
focusable: false,
header_row_index,
format: (value, row, column, data) => {
return `<div class="text-muted">${value}</div>`;
let html = `<div class="text-muted">${value}</div>`;
if (df.label === 'Sr. No' && this.is_row_imported(row)) {
html = `
<div class="flex justify-between">${SVG_ICONS['checkbox-circle-line'] + html}</div>
`;
}
return html;
}
};
}
@ -79,7 +100,7 @@ frappe.data_import.ImportPreview = class ImportPreview {
}
prepare_data() {
this.preview_data = this.preview_data.map(row => {
this.data = this.data.map(row => {
return row.map(cell => {
if (cell == null) {
return '';
@ -104,7 +125,7 @@ frappe.data_import.ImportPreview = class ImportPreview {
let self = this;
this.datatable = new DataTable(this.$table_preview.get(0), {
data: this.preview_data,
data: this.data,
columns: this.columns,
layout: 'fixed',
cellHeight: 35,
@ -144,7 +165,9 @@ frappe.data_import.ImportPreview = class ImportPreview {
columns.forEach(col => {
if (!col.skip_import && col.df) {
this.datatable.style.setStyle(
`.dt-header .dt-cell--col-${col.colIndex}, .dt-header .dt-cell--col-${col.colIndex} .dt-dropdown__toggle`,
`.dt-header .dt-cell--col-${col.colIndex}, .dt-header .dt-cell--col-${
col.colIndex
} .dt-dropdown__toggle`,
{
backgroundColor: frappe.ui.color.get_color_shade(
'green',
@ -156,7 +179,9 @@ frappe.data_import.ImportPreview = class ImportPreview {
}
if (col.skip_import && col.name !== 'Sr. No') {
this.datatable.style.setStyle(
`.dt-header .dt-cell--col-${col.colIndex}, .dt-header .dt-cell--col-${col.colIndex} .dt-dropdown__toggle`,
`.dt-header .dt-cell--col-${col.colIndex}, .dt-header .dt-cell--col-${
col.colIndex
} .dt-dropdown__toggle`,
{
backgroundColor: frappe.ui.color.get_color_shade(
'orange',
@ -170,11 +195,15 @@ frappe.data_import.ImportPreview = class ImportPreview {
});
}
});
this.datatable.style.setStyle(`svg.import-success`, {
width: '16px',
fill: frappe.ui.color.get_color_shade('green', 'dark')
});
}
add_row() {
this.preview_data.push([]);
this.datatable.refresh(this.preview_data);
this.data.push([]);
this.datatable.refresh(this.data);
}
remap_column(col) {
@ -204,4 +233,11 @@ frappe.data_import.ImportPreview = class ImportPreview {
skip_import(col) {
this.events.skip_import(col.header_row_index);
}
is_row_imported(row) {
let serial_no = row[0].content;
return this.import_log.find(log => {
return log.success && log.row_indexes.includes(serial_no);
});
}
};