diff --git a/CHANGELOG b/CHANGELOG index f566bfd378..363ce4f9e2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Use lazy load in Import/Export windows * Validate record before switching view * Refactoring: * Better naming in model diff --git a/tryton/gui/window/form.py b/tryton/gui/window/form.py index a59f54ec40..8cdedf3889 100644 --- a/tryton/gui/window/form.py +++ b/tryton/gui/window/form.py @@ -300,7 +300,7 @@ class Form(SignalEvent): fields = {} for name, field in self.screen.group.fields.iteritems(): fields[name] = field.attrs - win = WinImport(self.model, fields, parent=self.window) + win = WinImport(self.model, self.window) win.run() def sig_save_as(self, widget=None): @@ -309,7 +309,7 @@ class Form(SignalEvent): fields = {} for name, field in self.screen.group.fields.iteritems(): fields[name] = field.attrs - win = WinExport(self.model, self.ids_get(), fields, parent=self.window, + win = WinExport(self.model, self.ids_get(), self.window, context=self.context) win.run() diff --git a/tryton/gui/window/tree.py b/tryton/gui/window/tree.py index af33b2f6be..4a5c60f010 100644 --- a/tryton/gui/window/tree.py +++ b/tryton/gui/window/tree.py @@ -402,8 +402,8 @@ class Tree(SignalEvent): return res def sig_save_as(self, widget=None): - win = WinExport(self.model, self.ids_get(), self.tree_res.fields, - parent=self.window, context=self.context) + win = WinExport(self.model, self.ids_get(), self.window, + context=self.context) win.run() def sig_close(self): diff --git a/tryton/gui/window/win_export.py b/tryton/gui/window/win_export.py index d7a38ed46b..fee597f4e3 100644 --- a/tryton/gui/window/win_export.py +++ b/tryton/gui/window/win_export.py @@ -17,7 +17,7 @@ _ = gettext.gettext class WinExport(object): "Window export" - def __init__(self, model, ids, fields, parent=None, context=None): + def __init__(self, model, ids, parent, context=None): self.dialog = gtk.Dialog( title= _("Export to CSV"), parent=parent, @@ -165,6 +165,7 @@ class WinExport(object): self.view1 = gtk.TreeView() self.view1.get_selection().set_mode(gtk.SELECTION_MULTIPLE) + self.view1.connect('row-expanded', self.on_row_expanded) scrolledwindow_all_fields.add(self.view1) self.view2 = gtk.TreeView() self.view2.get_selection().set_mode(gtk.SELECTION_MULTIPLE) @@ -187,36 +188,7 @@ class WinExport(object): self.fields = {} - def model_populate(fields, prefix_node='', prefix=None, - prefix_value='', level=2): - fields_order = fields.keys() - fields_order.sort(lambda x, y: -cmp(fields[x].get('string', ''), - fields[y].get('string', ''))) - for field in fields_order: - self.fields_data[prefix_node+field] = fields[field] - if prefix_node: - self.fields_data[prefix_node + field]['string'] = \ - '%s%s' % (prefix_value, - self.fields_data[prefix_node + \ - field]['string']) - st_name = fields[field]['string'] or field - node = self.model1.insert(prefix, 0, - [st_name, prefix_node+field, - (fields[field].get('required', False) and \ - '#ddddff') or 'white']) - self.fields[prefix_node+field] = (st_name, - fields[field].get('relation', False)) - if fields[field].get('relation', False) and level>0: - try: - fields2 = rpc.execute('model', - fields[field]['relation'], 'fields_get', None, - rpc.CONTEXT) - except Exception, exception: - common.process_exception(exception, self.parent) - continue - model_populate(fields2, prefix_node+field+'/', node, - st_name+'/', level-1) - model_populate(fields) + self.model_populate(self._get_fields(model)) self.view1.set_model(self.model1) self.view2.set_model(self.model2) @@ -229,10 +201,8 @@ class WinExport(object): # Creating the predefined export view self.pref_export = gtk.TreeView() - self.pref_export.append_column(gtk.TreeViewColumn(_('Export name'), + self.pref_export.append_column(gtk.TreeViewColumn(_('Name'), gtk.CellRendererText(), text=2)) - self.pref_export.append_column(gtk.TreeViewColumn(_('Exported fields'), - gtk.CellRendererText(), text=3)) scrolledwindow_exports.add(self.pref_export) self.pref_export.connect("row-activated", self.sel_predef) @@ -241,17 +211,45 @@ class WinExport(object): self.predef_model = gtk.ListStore( gobject.TYPE_INT, gobject.TYPE_PYOBJECT, - gobject.TYPE_STRING, gobject.TYPE_STRING) self.fill_predefwin() self.pref_export.show_all() - def sig_sel_all(self, widget=None): - self.model2.clear() - for field, relation in self.fields.keys(): - if not relation: - self.model2.set(self.model2.append(), 0, self.fields[field], - 1, field) + def model_populate(self, fields, parent_node=None, prefix_field='', + prefix_name=''): + fields_order = fields.keys() + fields_order.sort(lambda x, y: -cmp(fields[x].get('string', ''), + fields[y].get('string', ''))) + for field in fields_order: + self.fields_data[prefix_field + field] = fields[field] + name = fields[field]['string'] or field + if prefix_field: + self.fields_data[prefix_field + field]['string'] = '%s%s' % \ + (prefix_name, self.fields_data[prefix_field + + field]['string']) + node = self.model1.insert(parent_node, 0, [name, prefix_field + + field, (fields[field].get('required', False) and + common.COLORS['required']) or 'white']) + self.fields[prefix_field + field] = (name, + fields[field].get('relation')) + if fields[field].get('relation'): + self.model1.insert(node, 0, [None, '', 'white']) + + def _get_fields(self, model): + args = ('model', model, 'fields_get', None, rpc.CONTEXT) + try: + return rpc.execute(*args) + except Exception, exception: + return common.process_exception(exception, self.parent, *args) + + def on_row_expanded(self, treeview, iter, path): + child = self.model1.iter_children(iter) + if self.model1.get_value(child, 0) is None: + prefix_field = self.model1.get_value(iter, 1) + name, model = self.fields[prefix_field] + self.model_populate(self._get_fields(model), iter, prefix_field + + '/', name + '/') + self.model1.remove(child) def sig_sel(self, widget=None): sel = self.view1.get_selection() @@ -262,8 +260,8 @@ class WinExport(object): if relation: return num = self.model2.append() - self.model2.set(num, 0, store.get_value(iter, 0), 1, - store.get_value(iter, 1)) + name = self.fields_data[store.get_value(iter, 1)]['string'] + self.model2.set(num, 0, name, 1, store.get_value(iter, 1)) def sig_unsel(self, widget=None): store, paths = self.view2.get_selection().get_selected_rows() @@ -292,7 +290,8 @@ class WinExport(object): if not exports: return args = ('model', 'ir.export.line', 'read', - [x['export_fields'] for x in exports], None, rpc.CONTEXT) + sum((x['export_fields'] for x in exports), []), None, + rpc.CONTEXT) try: lines = rpc.execute(*args) except Exception, exception: @@ -303,11 +302,10 @@ class WinExport(object): for line in lines: id2lines.setdefault(line['export'], []).append(line) for export in exports: - self.predef_model.append((export['id'], + self.predef_model.append(( + export['id'], [x['name'] for x in id2lines[export['id']]], - export['name'], - ','.join(self.fields_data[x['name']]['string'] - for x in id2lines[export['id']]))) + export['name'])) self.pref_export.set_model(self.predef_model) def add_predef(self, widget): @@ -336,8 +334,7 @@ class WinExport(object): self.predef_model.append(( new_id, fields, - name, - ','.join(self.fields_data[f]['string'] for f in fields))) + name)) self.pref_export.set_model(self.predef_model) def remove_predef(self, widget): @@ -363,6 +360,20 @@ class WinExport(object): def sel_predef(self, widget, path, column): self.model2.clear() for field in self.predef_model[path[0]][1]: + if field not in self.fields_data: + iter = self.model1.get_iter_first() + prefix = '' + for parent in field.split('/')[:-1]: + while iter: + if self.model1.get_value(iter, 1) == \ + (prefix + parent): + self.on_row_expanded(self.view1, iter, + self.model1.get_path(iter)) + break + iter = self.model1.iter_next(iter) + prefix = parent + '/' + if field not in self.fields_data: + continue self.model2.append((self.fields_data[field]['string'], field)) def run(self): diff --git a/tryton/gui/window/win_import.py b/tryton/gui/window/win_import.py index 705b9a2f0c..74c59b3882 100644 --- a/tryton/gui/window/win_import.py +++ b/tryton/gui/window/win_import.py @@ -14,7 +14,7 @@ _ = gettext.gettext class WinImport(object): "Window import" - def __init__(self, model, fields, parent=None): + def __init__(self, model, parent): self.dialog = gtk.Dialog( title=_("Import from CSV"), parent=parent, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT @@ -175,6 +175,7 @@ class WinImport(object): self.view1 = gtk.TreeView() self.view1.get_selection().set_mode(gtk.SELECTION_MULTIPLE) + self.view1.connect('row-expanded', self.on_row_expanded) scrolledwindow_fields.add(self.view1) self.view2 = gtk.TreeView() self.view2.get_selection().set_mode(gtk.SELECTION_MULTIPLE) @@ -198,40 +199,48 @@ class WinImport(object): self.fields = {} self.fields_invert = {} - def model_populate(fields, prefix_node='', prefix=None, - prefix_value='', level=2): - fields_order = fields.keys() - fields_order.sort(lambda x, y: -cmp(fields[x].get('string', ''), - fields[y].get('string', ''))) - for field in fields_order: - if not fields[field].get('readonly', False): - self.fields_data[prefix_node + field] = fields[field] - st_name = prefix_value + fields[field]['string'] or field - node = self.model1.insert(prefix, 0, [st_name, - prefix_node+field, - (fields[field].get('required', False) and \ - '#ddddff') or 'white']) - self.fields[prefix_node+field] = st_name - self.fields_invert[st_name] = prefix_node + field - if fields[field]['type'] == 'one2many' and level > 0: - args = ('model', fields[field]['relation'], - 'fields_get', None, rpc.CONTEXT) - try: - fields2 = rpc.execute(*args) - except Exception, exception: - fields2 = common.process_exception(exception, - self.dialog, *args) - if not fields2: - continue - model_populate(fields2, prefix_node + field + '/', node, - st_name+'/', level-1) - model_populate(fields) + self.model_populate(self._get_fields(model)) self.view1.set_model(self.model1) self.view2.set_model(self.model2) self.view1.show_all() self.view2.show_all() + def model_populate(self, fields, parent_node=None, prefix_field='', + prefix_name=''): + fields_order = fields.keys() + fields_order.sort(lambda x, y: -cmp(fields[x].get('string', ''), + fields[y].get('string', ''))) + for field in fields_order: + if not fields[field].get('readonly', False): + self.fields_data[prefix_field + field] = fields[field] + name = fields[field]['string'] or field + node = self.model1.insert(parent_node, 0, [name, prefix_field + + field, (fields[field].get('required', False) and + common.COLORS['required']) or 'white']) + name = prefix_name + name + self.fields[prefix_field + field] = (name, + fields[field].get('relation')) + self.fields_invert[name] = prefix_field + field + if fields[field].get('relation'): + self.model1.insert(node, 0, [None, '', 'white']) + + def _get_fields(self, model): + args = ('model', model, 'fields_get', None, rpc.CONTEXT) + try: + return rpc.execute(*args) + except Exception, exception: + return common.process_exception(exception, self.dialog, *args) + + def on_row_expanded(self, treeview, iter, path): + child = self.model1.iter_children(iter) + if self.model1.get_value(child, 0) is None: + prefix_field = self.model1.get_value(iter, 1) + name, model = self.fields[prefix_field] + self.model_populate(self._get_fields(model), iter, prefix_field + + '/', name + '/') + self.model1.remove(child) + def sig_autodetect(self, widget=None): fname = self.import_csv_file.get_filename() if not fname: @@ -253,37 +262,47 @@ class WinImport(object): return True self.sig_unsel_all() word = '' - try: - for line in data: - for word in line: - word = word.decode(csvcode) - num = self.model2.append() - try: - self.model2.set(num, 0, word, 1, self.fields_invert[word]) - except: - self.model2.remove(num) - raise - break - except Exception, exception: - common.warning(_('Error processing the file at field %s.\n' \ - 'Error message:\n%s') % (word, repr(exception)), - self.dialog, _('Error')) + for line in data: + for word in line: + word = word.decode(csvcode) + if word not in self.fields_invert and word not in self.fields: + iter = self.model1.get_iter_first() + prefix = '' + prefix_name = '' + for parent in word.split('/')[:-1]: + while iter: + if self.model1.get_value(iter, 0) == parent or \ + self.model1.get_value(iter, 1) == \ + (prefix + parent): + self.on_row_expanded(self.view1, iter, + self.model1.get_path(iter)) + break + iter = self.model1.iter_next(iter) + prefix = parent + '/' + if word in self.fields_invert: + name = word + field = self.fields_invert[word] + elif word in self.fields: + name = self.fields[word][0] + field = word + else: + common.warning( + _('Error processing the file at field%s.') % + word, self.dialog, _('Error')) + return True + num = self.model2.append() + self.model2.set(num, 0, name, 1, field) + break return True - def sig_sel_all(self, widget=None): - self.model2.clear() - for field in self.fields.keys(): - self.model2.set(self.model2.append(), 0, self.fields[field], - 1, field) - def sig_sel(self, widget=None): sel = self.view1.get_selection() sel.selected_foreach(self._sig_sel_add) def _sig_sel_add(self, store, path, iter): num = self.model2.append() - self.model2.set(num, 0, store.get_value(iter, 0), 1, - store.get_value(iter,1)) + name = self.fields[store.get_value(iter, 1)][0] + self.model2.set(num, 0, name, 1, store.get_value(iter,1)) def sig_unsel(self, widget=None): store, paths = self.view2.get_selection().get_selected_rows()