wip
This commit is contained in:
parent
0d6f256e03
commit
11d07b57b9
4 changed files with 136 additions and 61 deletions
|
|
@ -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":
|
||||
|
|
|
|||
|
|
@ -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('');
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue