changeset b739c7c6a470 in tryton:default
details: https://hg.tryton.org/tryton?cmd=changeset;node=b739c7c6a470
description:
Improve report and CSV for tree structure
issue10116
review341431002
diffstat:
CHANGELOG | 3 +
tryton/gui/window/form.py | 37 +++++++++++++++++----
tryton/gui/window/view_form/screen/screen.py | 19 +++++++++++
tryton/gui/window/view_form/view/list.py | 44 ++++++++++++++++++++++++++
tryton/gui/window/win_export.py | 47 ++++++++++++++++++++++-----
5 files changed, 134 insertions(+), 16 deletions(-)
diffs (302 lines):
diff -r 09dd58c192e7 -r b739c7c6a470 CHANGELOG
--- a/CHANGELOG Sun Mar 07 15:40:40 2021 +0100
+++ b/CHANGELOG Sun Mar 07 19:07:53 2021 +0100
@@ -1,3 +1,6 @@
+* Add paths and model context to action data
+* Add indentation in CSV export of tree
+* Manage which records to use for actions
* Handle device cookie
* Add breadcrumb as title of window form
* Display revision on dialog
diff -r 09dd58c192e7 -r b739c7c6a470 tryton/gui/window/form.py
--- a/tryton/gui/window/form.py Sun Mar 07 15:40:40 2021 +0100
+++ b/tryton/gui/window/form.py Sun Mar 07 19:07:53 2021 +0100
@@ -7,6 +7,7 @@
import locale
import os
import tempfile
+from itertools import zip_longest
from gi.repository import Gdk, GLib, Gtk
@@ -339,7 +340,14 @@
def do_export(self, widget, export):
if not self.modified_save():
return
- ids = [r.id for r in self.screen.selected_records]
+ if (self.screen.current_view
+ and self.screen.current_view.view_type == 'tree'
+ and self.screen.current_view.children_field):
+ ids = [r.id for r in self.screen.listed_records]
+ paths = self.screen.listed_paths
+ else:
+ ids = [r.id for r in self.screen.selected_records]
+ paths = self.screen.selected_paths
fields = [f['name'] for f in export['export_fields.']]
data = RPCExecute(
'model', self.model, 'export_data', ids, fields,
@@ -352,8 +360,10 @@
with open(fname, 'w') as fp:
writer = csv.writer(fp, delimiter=delimiter)
writer.writerow(fields)
- for row in data:
- writer.writerow(WinExport.format_row(row))
+ for row, path in zip_longest(data, paths or []):
+ indent = len(path) - 1 if path else 0
+ if row:
+ writer.writerow(WinExport.format_row(row, indent=indent))
os.close(fileno)
common.file_open(fname, 'csv')
@@ -575,11 +585,20 @@
action = action.copy()
record_id = (self.screen.current_record.id
if self.screen.current_record else None)
- record_ids = [r.id for r in self.screen.selected_records]
+ if action.get('records') == 'listed':
+ record_ids = [r.id for r in self.screen.listed_records]
+ record_paths = self.screen.listed_paths
+ else:
+ record_ids = [r.id for r in self.screen.selected_records]
+ record_paths = self.screen.selected_paths
data = {
'model': self.screen.model_name,
+ 'model_context': (
+ self.screen.context_screen.model_name
+ if self.screen.context_screen else None),
'id': record_id,
'ids': record_ids,
+ 'paths': record_paths,
}
Action.execute(action, data, context=self.screen.local_context)
@@ -741,11 +760,15 @@
menuitem = Gtk.MenuItem(label=set_underline(name))
menuitem.set_use_underline(True)
menuitem.connect('activate', lambda m, func: func({
- 'model': self.model,
+ 'model': self.screen.model_name,
+ 'model_context': (
+ self.screen.context_screen.model_name
+ if self.screen.context_screen else None),
+ 'id': (self.screen.current_record.id
+ if self.screen.current_record else None),
'ids': [r.id
for r in self.screen.selected_records],
- 'id': (self.screen.current_record.id
- if self.screen.current_record else None),
+ 'paths': self.screen.selected_paths,
}), func)
menuitem._update_action = True
menu.add(menuitem)
diff -r 09dd58c192e7 -r b739c7c6a470
tryton/gui/window/view_form/screen/screen.py
--- a/tryton/gui/window/view_form/screen/screen.py Sun Mar 07 15:40:40
2021 +0100
+++ b/tryton/gui/window/view_form/screen/screen.py Sun Mar 07 19:07:53
2021 +0100
@@ -1124,6 +1124,25 @@
def selected_records(self):
return self.current_view.selected_records if self.current_view else []
+ @property
+ def selected_paths(self):
+ if self.current_view and self.current_view.view_type == 'tree':
+ return self.current_view.get_selected_paths()
+
+ @property
+ def listed_records(self):
+ if self.current_view and self.current_view.view_type == 'tree':
+ return self.current_view.listed_records
+ elif self.current_record:
+ return [self.current_record]
+ else:
+ return []
+
+ @property
+ def listed_paths(self):
+ if self.current_view and self.current_view.view_type == 'tree':
+ return self.current_view.get_listed_paths()
+
def clear(self):
self.current_record = None
self.group.clear()
diff -r 09dd58c192e7 -r b739c7c6a470 tryton/gui/window/view_form/view/list.py
--- a/tryton/gui/window/view_form/view/list.py Sun Mar 07 15:40:40 2021 +0100
+++ b/tryton/gui/window/view_form/view/list.py Sun Mar 07 19:07:53 2021 +0100
@@ -1182,6 +1182,50 @@
id_paths.append(id_path)
return id_paths
+ @property
+ def listed_records(self):
+ model = self.treeview.get_model()
+ if not self.children_field:
+ return list(model.group)
+
+ def get_listed_records(start):
+ records = []
+ if start:
+ iter_ = model.get_iter(Gtk.TreePath(start))
+ else:
+ iter_ = None
+ for idx in range(model.iter_n_children(iter_)):
+ path = start + (idx,)
+ iter_ = model.get_iter(path)
+ record = model.get_value(iter_, 0)
+ records.append(record)
+ if self.treeview.row_expanded(Gtk.TreePath(path)):
+ records += get_listed_records(path)
+ return records
+ return get_listed_records(())
+
+ def get_listed_paths(self):
+ model = self.treeview.get_model()
+ if not self.children_field:
+ return [[r.id] for r in model.group]
+
+ def get_listed_paths(start=None, start_path=None):
+ paths = []
+ if start:
+ iter_ = model.get_iter(Gtk.TreePath(start))
+ else:
+ iter_ = None
+ for idx in range(model.iter_n_children(iter_)):
+ path = start + (idx,)
+ iter_ = model.get_iter(path)
+ record = model.get_value(iter_, 0)
+ id_path = start_path + [record.id]
+ paths.append(id_path)
+ if self.treeview.row_expanded(Gtk.TreePath(path)):
+ paths += get_listed_paths(path, id_path)
+ return paths
+ return get_listed_paths((), [])
+
def select_nodes(self, nodes):
selection = self.treeview.get_selection()
if not nodes:
diff -r 09dd58c192e7 -r b739c7c6a470 tryton/gui/window/win_export.py
--- a/tryton/gui/window/win_export.py Sun Mar 07 15:40:40 2021 +0100
+++ b/tryton/gui/window/win_export.py Sun Mar 07 19:07:53 2021 +0100
@@ -8,6 +8,7 @@
import json
import locale
import urllib.parse
+from itertools import zip_longest
from numbers import Number
from gi.repository import Gdk, GObject, Gtk
@@ -42,6 +43,13 @@
def context(self):
return self.screen.context
+ @property
+ def screen_is_tree(self):
+ return bool(
+ self.screen.current_view
+ and self.screen.current_view.view_type == 'tree'
+ and self.screen.current_view.children_field)
+
def add_buttons(self, box):
button_save_export = Gtk.Button(
label=_('_Save Export'), stock=None, use_underline=True)
@@ -116,7 +124,10 @@
self.selected_records, expand=True, fill=True, padding=3)
self.selected_records.append_text(_("Listed Records"))
self.selected_records.append_text(_("Selected Records"))
- self.selected_records.set_active(1)
+ if not self.screen_is_tree:
+ self.selected_records.set_active(1)
+ else:
+ self.selected_records.set_active(0)
self.ignore_search_limit = Gtk.CheckButton(
label=_("Ignore search limit"))
@@ -125,7 +136,8 @@
self.selected_records.connect(
'changed',
- lambda w: self.ignore_search_limit.set_visible(not w.get_active()))
+ lambda w: self.ignore_search_limit.set_visible(
+ not w.get_active() and not self.screen_is_tree))
def add_csv_header_param(self, box):
self.add_field_names = Gtk.CheckButton(label=_("Add field names"))
@@ -320,6 +332,17 @@
if self.selected_records.get_active():
ids = [r.id for r in self.screen.selected_records]
+ paths = self.screen.selected_paths
+ try:
+ data = RPCExecute(
+ 'model', self.model, 'export_data',
+ ids, fields,
+ context=self.context)
+ except RPCException:
+ data = []
+ elif self.screen_is_tree:
+ ids = [r.id for r in self.screen.listed_records]
+ paths = self.screen.listed_paths
try:
data = RPCExecute(
'model', self.model, 'export_data',
@@ -328,6 +351,7 @@
except RPCException:
data = []
else:
+ paths = None
domain = self.screen.search_domain(
self.screen.screen_container.get_text())
if self.ignore_search_limit.get_active():
@@ -346,16 +370,16 @@
fname = common.file_selection(_('Save As...'),
action=Gtk.FileChooserAction.SAVE)
if fname:
- self.export_csv(fname, fields2, data)
+ self.export_csv(fname, fields2, data, paths)
else:
fileno, fname = tempfile.mkstemp(
'.csv', common.slugify(self.name) + '_')
- self.export_csv(fname, fields2, data, popup=False)
+ self.export_csv(fname, fields2, data, paths, popup=False)
os.close(fileno)
common.file_open(fname, 'csv')
self.destroy()
- def export_csv(self, fname, fields, data, popup=True):
+ def export_csv(self, fname, fields, data, paths, popup=True):
encoding = self.csv_enc.get_active_text() or 'UTF-8'
locale_format = self.csv_locale.get_active()
@@ -366,8 +390,11 @@
delimiter=self.get_delimiter())
if self.add_field_names.get_active():
writer.writerow(fields)
- for row in data:
- writer.writerow(self.format_row(row, locale_format))
+ for row, path in zip_longest(data, paths or []):
+ indent = len(path) - 1 if path else 0
+ if row:
+ writer.writerow(self.format_row(
+ row, indent=indent, locale_format=locale_format))
if popup:
if len(data) == 1:
common.message(_('%d record saved.') % len(data))
@@ -380,9 +407,9 @@
return False
@classmethod
- def format_row(cls, line, locale_format=True):
+ def format_row(cls, line, indent=0, locale_format=True):
row = []
- for val in line:
+ for i, val in enumerate(line):
if locale_format:
if isinstance(val, Number):
val = locale.str(val)
@@ -397,6 +424,8 @@
val = val.total_seconds()
elif isinstance(val, bool):
val = int(val)
+ if i == 0 and indent and isinstance(val, str):
+ val = ' ' * indent + val
row.append(val)
return row