Subversion Repositories basico

Rev

Rev 355 | Rev 358 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
353 t00mlabs 1
#!/usr/bin/python
2
# -*- coding: utf-8 -*-
3
"""
356 t00mlabs 4
# File: wdg_visor_attachments.py
353 t00mlabs 5
# Author: Tomás Vírseda
6
# License: GPL v3
7
# Description: Attachments Visor
8
"""
9
 
10
import os
11
from os.path import sep as SEP
12
from html import escape
13
import glob
14
import json
15
import html
16
import gi
17
gi.require_version('Gdk', '3.0')
18
gi.require_version('Gtk', '3.0')
19
from gi.repository import Gdk
20
from gi.repository import Gio
21
from gi.repository import Gtk
22
from gi.repository.GdkPixbuf import Pixbuf
23
from gi.repository import Pango
24
 
25
from basico.core.mod_env import LPATH, ATYPES
26
from basico.core.mod_wdg import BasicoWidget
27
from basico.widgets.wdg_cols import CollectionsMgtView
28
from basico.widgets.wdg_import import ImportWidget
29
 
30
 
31
# ~ total, used, free = shutil.disk_usage('/home/t00m/.basico/opt/webdrivers/geckodriver')
32
# ~ ft = float(ceil(total/(1024*1024*1024)))
33
# ~ ff = float(ceil(free/(1024*1024*1024)))
34
# ~ pf = (ft-ff)*100/ft
35
 
36
# ~ ft = int(ceil(total/(1024*1024*1024)))
37
# ~ ff = int(ceil(free/(1024*1024*1024)))
38
# ~ pf = (ft-ff)*100/ft
39
 
40
class AttachmentsVisor(BasicoWidget, Gtk.VBox):
41
    def __init__(self, app):
42
        super().__init__(app, __class__.__name__)
43
        Gtk.HBox.__init__(self, app)
44
        self.set_homogeneous(False)
45
        self.bag = []
46
        self.get_services()
47
        self.setup_visor()
48
        self.icons = {}
49
        self.icons['type'] = {}
50
        for atype in ATYPES:
51
            self.icons['type'][atype.lower()] = self.srvicm.get_pixbuf_icon('basico-attachment-type-%s' % atype.lower())
52
        self.log.debug("Attachments Visor initialized")
356 t00mlabs 53
        self.populate_attachments()
353 t00mlabs 54
 
55
 
56
    def get_services(self):
57
        self.srvgui = self.get_service("GUI")
58
        self.srvclb = self.get_service('Callbacks')
59
        self.srvsap = self.get_service('SAP')
60
        self.srvicm = self.get_service('IM')
61
        self.srvstg = self.get_service('Settings')
62
        self.srvdtb = self.get_service('DB')
63
        self.srvuif = self.get_service("UIF")
64
        self.srvutl = self.get_service("Utils")
355 t00mlabs 65
        self.srvatc = self.get_service('Attachment')
356 t00mlabs 66
 
353 t00mlabs 67
 
68
    def get_treeview(self):
69
        return self.treeview
70
 
71
 
72
    def sort_by_timestamp(self):
356 t00mlabs 73
        self.sorted_model.set_sort_column_id(2, Gtk.SortType.DESCENDING)
353 t00mlabs 74
 
75
 
76
    def setup_visor(self):
77
        scr = Gtk.ScrolledWindow()
78
        scr.set_hexpand(True)
79
        scr.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
80
        scr.set_shadow_type(Gtk.ShadowType.NONE)
81
        self.treeview = Gtk.TreeView()
82
        scr.add(self.treeview)
83
        scr.set_hexpand(True)
84
        self.pack_start(scr, True, True, 0)
85
 
86
        hbox = Gtk.HBox()
87
        pgb = self.srvgui.add_widget('gtk_progressbar_diskusage', Gtk.ProgressBar())
88
        fraction = self.srvutl.get_disk_usage_fraction(LPATH['DB'])
89
        text = self.srvutl.get_disk_usage_human(LPATH['DB'])
90
        pgb.set_fraction(fraction)
91
        pgb.set_text(text)
92
        pgb.set_show_text(True)
93
        hbox.pack_start(pgb, True, True, 0)
94
        self.pack_start(hbox, False, False, 0)
95
 
96
        # Setup model
97
        self.model = Gtk.TreeStore(
356 t00mlabs 98
            str,        # TID (Attachment ID)
353 t00mlabs 99
            Pixbuf,     # Icon
356 t00mlabs 100
            str,        # SAP Note
101
            str,        # Title
102
            str,        # Description
103
            str,        # Added
104
            str,        # Added (timestamp)
105
            str,        # Size
106
            int,        # Size (int)
107
            str,        # Mimetype
108
            str,        # Document Type
353 t00mlabs 109
        )
110
 
111
        # Setup columns
112
        def get_column_header_widget(title, icon_name=None, width=24, height=24):
113
            hbox = Gtk.HBox()
114
            icon = self.srvicm.get_new_image_icon(icon_name, width, height)
115
            label = Gtk.Label()
116
            label.set_markup("<b>%s</b>" % title)
117
            # ~ label.modify_font(Pango.FontDescription('Monospace 10'))
118
            hbox.pack_start(icon, False, False, 3)
119
            hbox.pack_start(label, True, True, 3)
120
            hbox.show_all()
121
            return hbox
122
 
356 t00mlabs 123
        # TID (Attachment ID)
353 t00mlabs 124
        self.renderer_key = Gtk.CellRendererText()
356 t00mlabs 125
        self.column_key = Gtk.TreeViewColumn('TID', self.renderer_key, text=0)
353 t00mlabs 126
        self.column_key.set_visible(False)
127
        self.column_key.set_expand(False)
356 t00mlabs 128
        # ~ self.column_key.set_clickable(False)
129
        # ~ self.column_key.set_sort_indicator(False)
353 t00mlabs 130
        self.treeview.append_column(self.column_key)
131
 
132
        # Icon
133
        self.renderer_icon = Gtk.CellRendererPixbuf()
134
        self.renderer_icon.set_alignment(0.0, 0.5)
135
        self.column_icon = Gtk.TreeViewColumn('', self.renderer_icon, pixbuf=1)
356 t00mlabs 136
        # ~ widget = get_column_header_widget('', 'basico-attachment')
137
        # ~ self.column_icon.set_widget(widget)
353 t00mlabs 138
        self.column_icon.set_visible(True)
139
        self.column_icon.set_expand(False)
356 t00mlabs 140
        # ~ self.column_icon.set_clickable(False)
141
        # ~ self.column_icon.set_sort_indicator(True)
353 t00mlabs 142
        self.treeview.append_column(self.column_icon)
143
 
144
        # SAP Note Id
145
        self.renderer_sid = Gtk.CellRendererText()
146
        self.renderer_sid.set_property('xalign', 1.0)
147
        self.renderer_sid.set_property('height', 36)
148
        self.renderer_sid.set_property('background', '#F0E3E3')
356 t00mlabs 149
        self.column_sid = Gtk.TreeViewColumn('SAP Note', self.renderer_sid, markup=2)
150
        # ~ widget = get_column_header_widget('SAP Note', 'basico-sid')
151
        # ~ self.column_sid.set_widget(widget)
353 t00mlabs 152
        self.column_sid.set_visible(True)
153
        self.column_sid.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
154
        self.column_sid.set_expand(False)
155
        self.column_sid.set_clickable(True)
156
        self.column_sid.set_sort_indicator(True)
356 t00mlabs 157
        self.column_sid.set_sort_column_id(2)
353 t00mlabs 158
        self.treeview.append_column(self.column_sid)
159
 
356 t00mlabs 160
        # Attachment Title
353 t00mlabs 161
        self.renderer_title = Gtk.CellRendererText()
162
        self.renderer_title.set_property('background', '#FFFEEA')
163
        self.renderer_title.set_property('ellipsize', Pango.EllipsizeMode.MIDDLE)
356 t00mlabs 164
        self.column_title = Gtk.TreeViewColumn('Title', self.renderer_title, markup=3)
165
        # ~ widget = get_column_header_widget('Title', 'basico-tag')
166
        # ~ self.column_title.set_widget(widget)
353 t00mlabs 167
        self.column_title.set_visible(True)
168
        self.column_title.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
169
        self.column_title.set_expand(True)
170
        self.column_title.set_clickable(True)
171
        self.column_title.set_sort_indicator(True)
356 t00mlabs 172
        self.column_title.set_sort_column_id(3)
353 t00mlabs 173
        self.treeview.append_column(self.column_title)
174
 
356 t00mlabs 175
        # Description
353 t00mlabs 176
        self.renderer_component = Gtk.CellRendererText()
177
        self.renderer_component.set_property('background', '#E3E3F0')
356 t00mlabs 178
        self.column_component = Gtk.TreeViewColumn('Description', self.renderer_component, markup=4)
179
        # ~ widget = get_column_header_widget('Description', 'basico-component')
180
        # ~ self.column_component.set_widget(widget)
353 t00mlabs 181
        self.column_component.set_visible(True)
182
        self.column_component.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
183
        self.column_component.set_expand(False)
184
        self.column_component.set_clickable(True)
185
        self.column_component.set_sort_indicator(True)
356 t00mlabs 186
        self.column_component.set_sort_column_id(4)
353 t00mlabs 187
        self.treeview.append_column(self.column_component)
188
 
356 t00mlabs 189
        # Added
353 t00mlabs 190
        self.renderer_category = Gtk.CellRendererText()
191
        self.renderer_category.set_property('background', '#E3F1E3')
356 t00mlabs 192
        self.column_category = Gtk.TreeViewColumn('Added', self.renderer_category, markup=5)
193
        widget = get_column_header_widget('Added', 'basico-category')
353 t00mlabs 194
        self.column_category.set_widget(widget)
356 t00mlabs 195
        self.column_category.set_visible(True)
353 t00mlabs 196
        self.column_category.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
197
        self.column_category.set_expand(False)
198
        self.column_category.set_clickable(True)
199
        self.column_category.set_sort_indicator(True)
200
        self.column_category.set_sort_column_id(6)
201
        self.treeview.append_column(self.column_category)
202
 
356 t00mlabs 203
        # Added (timestamp)
353 t00mlabs 204
        self.renderer_type = Gtk.CellRendererText()
356 t00mlabs 205
        self.column_type = Gtk.TreeViewColumn('Added', self.renderer_type, text=6)
206
        self.column_type.set_visible(False)
353 t00mlabs 207
        self.treeview.append_column(self.column_type)
208
 
356 t00mlabs 209
        # Size
353 t00mlabs 210
        self.renderer_priority = Gtk.CellRendererText()
211
        self.renderer_priority.set_property('background', '#e4f1f1')
356 t00mlabs 212
        self.column_priority = Gtk.TreeViewColumn('Size', self.renderer_priority, markup=7)
353 t00mlabs 213
        self.column_priority.set_visible(True)
214
        self.column_priority.set_expand(False)
215
        self.column_priority.set_clickable(True)
216
        self.column_priority.set_sort_indicator(True)
217
        self.column_priority.set_sort_column_id(8)
218
        self.treeview.append_column(self.column_priority)
219
 
356 t00mlabs 220
        # Size (int)
221
        self.renderer_sizeint = Gtk.CellRendererText()
222
        self.renderer_sizeint.set_property('background', '#FFE6D1')
223
        self.column_sizeint = Gtk.TreeViewColumn('SizeInt', self.renderer_sizeint, text=8)
224
        self.column_sizeint.set_visible(False)
225
        self.column_sizeint.set_clickable(True)
226
        self.column_sizeint.set_sort_indicator(True)
227
        self.column_sizeint.set_sort_column_id(8)
228
        self.treeview.append_column(self.column_sizeint)
353 t00mlabs 229
 
356 t00mlabs 230
        # Mimetype
353 t00mlabs 231
        self.renderer_annotation = Gtk.CellRendererText()
356 t00mlabs 232
        self.column_annotation = Gtk.TreeViewColumn('Mimetype', self.renderer_annotation, markup=9)
353 t00mlabs 233
        self.column_annotation.set_visible(False)
234
        self.treeview.append_column(self.column_annotation)
235
 
356 t00mlabs 236
        # Document Type
353 t00mlabs 237
        self.renderer_timestamp = Gtk.CellRendererText()
356 t00mlabs 238
        self.column_timestamp = Gtk.TreeViewColumn('Document type', self.renderer_timestamp, text=10)
353 t00mlabs 239
        self.column_timestamp.set_visible(False)
240
        self.treeview.append_column(self.column_timestamp)
241
 
242
        # Treeview properties
243
        self.treeview.set_can_focus(False)
244
        self.treeview.set_enable_tree_lines(True)
245
        self.treeview.set_headers_visible(True)
246
        self.treeview.set_enable_search(True)
247
        self.treeview.set_hover_selection(False)
248
        self.treeview.set_grid_lines(Gtk.TreeViewGridLines.NONE)
249
        self.treeview.set_enable_tree_lines(True)
250
        self.treeview.set_level_indentation(10)
251
        self.treeview.connect('button_press_event', self.right_click)
252
 
253
        # DOC: In order to have a Gtk.Widged with sorting and filtering
254
        # capabilities, you have to filter the model first, and use this
255
        # new model to create the sorted model. Then, attach the sorted
256
        # model to the treeview...
257
 
258
        # Treeview filtering:
259
        self.visible_filter = self.model.filter_new()
260
        self.visible_filter.set_visible_func(self.visible_function)
261
 
262
        # TreeView sorting
263
        self.sorted_model = Gtk.TreeModelSort(model=self.visible_filter)
264
        self.sorted_model.set_sort_func(0, self.sort_function, None)
265
 
266
        # Selection
267
        self.selection = self.treeview.get_selection()
268
        self.selection.set_mode(Gtk.SelectionMode.SINGLE)
356 t00mlabs 269
        # ~ self.selection.connect('changed', self.row_changed)
353 t00mlabs 270
 
271
        # Set model (filtered and sorted)
272
        self.sorted_model.set_sort_column_id(9, Gtk.SortType.ASCENDING)
273
        self.treeview.set_model(self.sorted_model)
274
 
275
        self.show_all()
276
 
277
 
278
    def sort_function(self, model, row1, row2, user_data):
279
        sort_column = 0
280
 
281
        value1 = model.get_value(row1, sort_column)
282
        value2 = model.get_value(row2, sort_column)
283
 
284
        if value1 < value2:
285
            return -1
286
        elif value1 == value2:
287
            return 0
288
        else:
289
            return 1
290
 
291
 
292
    def always_visible(self, model, itr, data):
293
        return False
294
 
295
 
296
    def visible_function(self, model, itr, data):
297
        entry = self.srvgui.get_widget('gtk_entry_filter_visor')
298
        text = self.srvutl.clean_html(entry.get_text())
299
        title = model.get(itr, 4)[0]
300
        match = text.upper() in title.upper()
301
 
302
        return match
303
 
304
 
356 t00mlabs 305
    def update_total_attachments_count(self):
353 t00mlabs 306
        statusbar = self.srvgui.get_widget('widget_statusbar')
307
        lblnotescount = self.srvgui.get_widget('gtk_label_total_notes')
356 t00mlabs 308
        total = self.srvatc.get_total()
353 t00mlabs 309
        count = len(self.visible_filter)
356 t00mlabs 310
        lblnotescount.set_markup("<b>%d/<big>%d attachments</big></b>" % (count, total))
311
        msg = 'View populated with %d attachments' % count
353 t00mlabs 312
        self.srvuif.statusbar_msg(msg)
313
 
314
 
315
    def get_visible_filter(self):
316
        return self.visible_filter
317
 
318
 
356 t00mlabs 319
    # ~ def row_changed(self, selection):
320
        # ~ try:
321
            # ~ model, treeiter = selection.get_selected()
322
            # ~ if treeiter is not None:
323
                # ~ component = model[treeiter][5]
324
                # ~ if component == 'Annotation':
325
                    # ~ aid = model[treeiter][10]
326
                    # ~ is_valid = self.srvant.is_valid(aid)
327
                    # ~ if is_valid:
328
                        # ~ self.srvclb.action_annotation_edit(aid)
329
                # ~ else:
330
                    # ~ aid = None
331
                    # ~ self.srvuif.set_widget_visibility('gtk_vbox_container_annotations', False)
332
        # ~ except Exception as error:
333
            # ~ head = "Error reading annotation's contents"
334
            # ~ body = "<i>%s</i>\n\n" % error
335
            # ~ body += "As a workaround, a new file will be created"
336
            # ~ dialog = self.srvuif.message_dialog_error(head, body)
337
            # ~ self.log.debug(error)
338
            # ~ self.log.debug(self.get_traceback())
339
            # ~ dialog.run()
340
            # ~ dialog.destroy()
341
            # ~ #FIXME: create an empty file for contents
353 t00mlabs 342
 
343
 
344
    def toggle_checkbox(self, cell, path):
345
        path = self.sorted_model.convert_path_to_child_path(Gtk.TreePath(path))
346
        self.model[path][2] = not self.model[path][2]
347
 
348
 
356 t00mlabs 349
    def get_node(self, key, icon, sid, title, description, added, timestamp, size, sizeint, mimetype, doctype):
353 t00mlabs 350
        # Add completion entries
356 t00mlabs 351
        # ~ completion = self.srvgui.get_widget('gtk_entrycompletion_visor')
352
        # ~ completion_model = completion.get_model()
353
        # ~ title = self.srvutl.clean_html(title)
354
        # ~ completion_model.append([title])
353 t00mlabs 355
 
356
        node = []
357
        node.append(key)
358
        node.append(icon)
359
        node.append(sid)
360
        node.append(title)
356 t00mlabs 361
        node.append(description)
362
        node.append(added)
363
        node.append(timestamp)
364
        node.append(size)
365
        node.append(sizeint)
366
        node.append(mimetype)
367
        node.append(doctype)
353 t00mlabs 368
        return node
369
 
370
 
356 t00mlabs 371
    def set_bag(self, attachments):
372
        self.bag = attachments
353 t00mlabs 373
 
374
 
375
    def get_bag(self):
376
        return self.bag
377
 
378
 
379
    def reload(self):
380
        bag = self.get_bag()
356 t00mlabs 381
        self.populate_attachments(bag)
353 t00mlabs 382
 
383
 
356 t00mlabs 384
    def populate_attachments(self, attachments=None):
385
        # ~ completion = self.srvgui.get_widget('gtk_entrycompletion_visor')
386
        # ~ completion_model = completion.get_model()
387
        # ~ completion_model.clear()
353 t00mlabs 388
 
389
        self.treeview.set_model(None)
390
        self.model.clear()
391
 
356 t00mlabs 392
        if attachments is None:
393
            attachments = self.srvatc.get_all()
353 t00mlabs 394
        else:
356 t00mlabs 395
            self.set_bag(attachments)
353 t00mlabs 396
 
397
        dcats = {}
398
        snpids = {}
399
        nodes = []
356 t00mlabs 400
        for fname in attachments:
353 t00mlabs 401
            try:
402
                with open(fname, 'r') as fa:
403
                    try:
356 t00mlabs 404
                        metadata = json.load(fa)
405
                        sid = self.srvatc.get_sid(metadata['TID'])
353 t00mlabs 406
                        try:
356 t00mlabs 407
                            sid = str(int(sid))
408
                        except:
409
                            sid = ''
410
                        self.log.debug(metadata['TID'])
411
                        if sid == '0000000000':
412
                            sid = ''        
413
                        node = self.get_node(   metadata['TID'],
414
                                                None,
415
                                                sid,
416
                                                metadata['Title'],
417
                                                metadata['Description'],
418
                                                self.srvutl.fuzzy_date_from_timestamp(metadata['Created']),
419
                                                metadata['Created'],
420
                                                str(self.srvutl.get_human_sizes(int(metadata['Size']))),
421
                                                int(metadata['Size']),
422
                                                metadata['Mimetype'],
423
                                                metadata['Doctype'],
424
                                            )
425
                        nodes.append(node)
353 t00mlabs 426
                    except Exception as error:
427
                        self.log.error(error)
428
                        self.log.error(self.get_traceback())
429
            except Exception as error:
430
                # Whenever an annotation is deleted, after reloading
431
                # the view, it fails. Then, skip it
356 t00mlabs 432
                self.log.error("Attachment: %s", fname)
353 t00mlabs 433
                self.log.error(error)
434
 
435
        for node in nodes:
356 t00mlabs 436
            self.model.append(None, node)
353 t00mlabs 437
 
438
        self.treeview.set_model(self.sorted_model)
439
        self.sort_by_timestamp()
356 t00mlabs 440
        self.update_total_attachments_count()
353 t00mlabs 441
 
442
 
443
    def show_widgets(self):
444
        self.srvuif.set_widget_visibility('gtk_label_total_notes', True)
445
 
446
 
447
    def right_click(self, treeview, event, data=None):
448
        if event.button == 3:
449
            rect = Gdk.Rectangle()
450
            rect.x = x = int(event.x)
451
            rect.y = y = int(event.y)
452
            pthinfo = self.treeview.get_path_at_pos(x,y)
453
            if pthinfo is not None:
454
                path,col,cellx,celly = pthinfo
455
                model = treeview.get_model()
456
                treeiter = model.get_iter(path)
457
                component = model[treeiter][5]
458
                aid = model[treeiter][10]
459
                toolbar = self.srvgui.get_widget('visortoolbar')
460
                popover = self.srvgui.add_widget('gtk_popover_visor_row', Gtk.Popover.new(treeview))
461
                popover.set_position(Gtk.PositionType.TOP)
462
                popover.set_pointing_to(rect)
463
                box = self.build_popover(aid, popover, component)
464
                if box is not None:
465
                    popover.add(box)
466
                    self.srvclb.gui_show_popover(None, popover)
467
 
468
 
469
    def build_popover(self, aid, popover, component):
356 t00mlabs 470
        sid = self.srvatc.get_sid(aid)
353 t00mlabs 471
 
472
        def get_popover_button(text, icon_name):
473
            button = Gtk.Button()
474
            button.set_relief(Gtk.ReliefStyle.NONE)
475
            hbox = Gtk.HBox()
476
            icon = self.srvicm.get_new_image_icon(icon_name, 24, 24)
477
            lbltext = Gtk.Label()
478
            lbltext.set_xalign(0.0)
479
            lbltext.set_markup('%s' % text)
480
            hbox.pack_start(icon, False, False, 3)
481
            hbox.pack_start(lbltext, True, True, 3)
482
            button.add(hbox)
483
            return button
484
 
485
        if component == 'Annotation':
486
            box = Gtk.Box(spacing = 3, orientation="vertical")
487
 
488
            # Popover button "Delete annotation"
489
            button = get_popover_button("<b>Delete</b> annotation", 'basico-delete')
490
            button.show_all()
491
            button.connect('clicked', self.srvclb.action_annotation_delete)
492
            box.pack_start(button, False, False, 0)
493
 
494
            # Popover button "Duplicate annotation"
495
            if sid != '0000000000':
496
                # Jump to SAP Note
497
                button = get_popover_button("Jump to SAP Note %d" % int(sid), 'basico-jump-sapnote')
498
                button.show_all()
499
                button.connect('clicked', self.srvclb.gui_jump_to_sapnote, sid)
500
                box.pack_start(button, False, False, 0)
501
            else:
502
                pass
503
            return box
504
 
505
 
506
    def connect_menuview_signals(self):
507
        # Categories
508
        button = self.srvgui.get_widget('gtk_togglebutton_categories')
509
        button.connect('toggled', self.set_visible_categories)
510
 
511
        for name in ['inbox', 'drafts', 'archived']:
512
            button = self.srvgui.get_widget('gtk_button_category_%s' % name)
513
            button.connect('toggled', self.set_visible_category, name)
514
 
515
        # Types
516
        button = self.srvgui.get_widget('gtk_togglebutton_types')
517
        button.connect('toggled', self.set_visible_types)
518
 
519
        for name in ATYPES:
520
            button = self.srvgui.get_widget('gtk_button_type_%s' % name.lower())
521
            button.connect('toggled', self.set_visible_annotation_type, name)
522
 
523
 
524
    def set_active_categories(self):
525
        category = self.srvgui.get_widget('gtk_togglebutton_inbox')
526
        category.set_active(True)
527