Improve report and CSV for tree structure

issue10116
review341431002
This commit is contained in:
Cédric Krier committed 2021-03-07 19:07:53 +01:00
1 parent 3e41aed055
commit 8b00fc1544
5 files changed
+134 -16

No files matched your search

+3
View File
@@ -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
+30 -7
View File
@@ -7,6 +7,7 @@ import gettext
import locale
import os
import tempfile
from itertools import zip_longest
from gi.repository import Gdk, GLib, Gtk
@@ -339,7 +340,14 @@ class Form(SignalEvent, TabContent):
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 @@ class Form(SignalEvent, TabContent):
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 @@ class Form(SignalEvent, TabContent):
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 @@ class Form(SignalEvent, TabContent):
menuitem = Gtk.MenuItem(label=set_underline(name))
menuitem.set_use_underline(True)
menuitem.connect('activate', lambda m, func: func({
'model': self.model,
'ids': [r.id
for r in self.screen.selected_records],
'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],
'paths': self.screen.selected_paths,
}), func)
menuitem._update_action = True
menu.add(menuitem)
@@ -1124,6 +1124,25 @@ class Screen(SignalEvent):
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()
+44
View File
@@ -1182,6 +1182,50 @@ class ViewTree(View):
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:
+38 -9
View File
@@ -8,6 +8,7 @@ import gettext
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 @@ class WinExport(WinCSV):
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 @@ class WinExport(WinCSV):
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 @@ class WinExport(WinCSV):
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 @@ class WinExport(WinCSV):
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 @@ class WinExport(WinCSV):
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 @@ class WinExport(WinCSV):
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 @@ class WinExport(WinCSV):
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 @@ class WinExport(WinCSV):
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 @@ class WinExport(WinCSV):
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