Subversion Repositories basico

Compare Revisions

Ignore whitespace Rev 232 → Rev 233

/branches/BR-0.3/basico/settings.py
File deleted
/branches/BR-0.3/basico/gui.py
File deleted
Property changes:
Deleted: svn:executable
## -1 +0,0 ##
-*
\ No newline at end of property
Index: branches/BR-0.3/basico/sapnoteviewvisor.py
===================================================================
--- branches/BR-0.3/basico/sapnoteviewvisor.py (revision 232)
+++ branches/BR-0.3/basico/sapnoteviewvisor.py (nonexistent)
@@ -1,838 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: sapnoteviewvisor.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: SAPNoteViewVisor widget
-
-import os
-from os.path import sep as SEP
-from cgi import escape
-import glob
-import json
-
-import gi
-gi.require_version('Gdk', '3.0')
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gdk
-from gi.repository import Gio
-from gi.repository import Gtk
-from gi.repository.GdkPixbuf import Pixbuf
-from gi.repository import Pango
-
-from .env import LPATH
-from .service import Service
-from .widgets.collections import CollectionsMgtView
-from .widgets.annotation import AnnotationWidget
-from .widgets.importwdg import ImportWidget
-
-class SAPNoteViewVisorToolbar(Gtk.HBox, Service):
- def __init__(self, app):
- Gtk.Box.__init__(self, app)
- self.app = app
- self.get_services()
- self.set_homogeneous(False)
- self.tool_bar = Gtk.Toolbar()
- self.pack_start(self.tool_bar, False, True, 0)
- self.tool_bar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
- self.tool_bar.set_property('margin-bottom', 0)
-
- # Toggle Views button
- tool = self.gui.add_widget('gtk_toogletoolbutton_menu_view', Gtk.ToggleToolButton.new())
- tool.set_icon_name('gtk-select-color')
- tool.connect('toggled', self.cb.gui_toggle_menu_view)
- tool.set_active(False)
- tool.set_hexpand(False)
- self.tool_bar.insert(tool, -1)
-
- # View combobox button/popover
- tool = Gtk.ToolItem.new()
- lhbox = Gtk.HBox()
- menuviews = self.gui.add_widget('gtk_button_menu_views', Gtk.Button())
- hbox = Gtk.HBox()
- label = self.gui.add_widget('gtk_label_current_view', Gtk.Label())
- label.set_xalign(0.0)
- image = self.gui.add_widget('gtk_image_current_view', Gtk.Image())
- hbox.pack_start(image, False, False, 3)
- hbox.pack_start(label, True, True, 3)
- menuviews.add(hbox)
- lhbox.pack_start(menuviews, True, True, 3)
- lhbox.show_all()
- tool.add(lhbox)
- tool.set_expand(False)
- self.tool_bar.insert(tool, -1)
-
- ### Popover menuviews
- popover = self.gui.add_widget('gtk_popover_button_menu_views', Gtk.Popover.new(menuviews))
- menuviews.connect('clicked', self.cb.gui_show_popover, popover)
- box = Gtk.Box(spacing = 0, orientation="vertical")
- popover.add(box)
-
-
- box.pack_start(self.uif.create_menuview_button('annotation'), False, False, 0)
- separator = Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)
- box.pack_start(separator, False, False, 0)
- box.pack_start(self.uif.create_menuview_button('collection'), False, False, 0)
- box.pack_start(self.uif.create_menuview_button('component'), False, False, 0)
- box.pack_start(self.uif.create_menuview_button('description'), False, False, 0)
- box.pack_start(self.uif.create_menuview_button('bookmarks'), False, False, 0)
- box.pack_start(self.uif.create_menuview_button('category'), False, False, 0)
- box.pack_start(self.uif.create_menuview_button('chronologic'), False, False, 0)
- box.pack_start(self.uif.create_menuview_button('priority'), False, False, 0)
- box.pack_start(self.uif.create_menuview_button('type'), False, False, 0)
-
- # Import button
- tool = Gtk.ToolButton()
- tool.set_icon_name('basico-add')
- popover = self.gui.add_widget('gtk_popover_toolbutton_import', Gtk.Popover.new(tool))
- tool.connect('clicked', self.cb.gui_show_popover, popover)
- self.tool_bar.insert(tool, -1)
-
- ## Popover body
- box = Gtk.VBox(spacing = 0, orientation="vertical")
- box.set_property('margin', 3)
- widget_import = self.gui.add_widget('widget_import', ImportWidget(self.app))
- box.pack_start(widget_import, True, True, 6)
- popover.add(box)
-
- # Annotation button
- tool = Gtk.ToolButton()
- tool.set_icon_name('basico-annotation')
- popover = self.gui.add_widget('gtk_popover_annotation', Gtk.Popover.new(tool))
- # ~ tool.connect('clicked', self.cb.gui_show_popover, popover)
- tool.connect('clicked', self.cb.gui_annotation_widget_show)
- self.tool_bar.insert(tool, -1)
-
- ## Popover body
- box = Gtk.VBox(spacing = 0, orientation="vertical")
- box.set_property('margin', 3)
- widget_import = self.gui.add_widget('widget_annotation', AnnotationWidget(self.app))
- box.pack_start(widget_import, True, True, 6)
- popover.add(box)
-
-
- # Filter entry
- tool = Gtk.ToolItem.new()
-
- hbox = Gtk.HBox()
- entry = Gtk.Entry()
- entry.connect('activate', self.cb.gui_filter_visor)
- self.gui.add_widget('gtk_entry_filter_visor', entry)
-
- icon = self.im.get_pixbuf_icon('basico-find')
- entry.set_icon_from_pixbuf(Gtk.EntryIconPosition.PRIMARY, icon)
- entry.set_icon_sensitive(Gtk.EntryIconPosition.PRIMARY, True)
- entry.set_icon_tooltip_markup (Gtk.EntryIconPosition.PRIMARY, "Search in the whole database")
-
- icon = self.im.get_pixbuf_icon('basico-filter')
- entry.set_icon_from_pixbuf(Gtk.EntryIconPosition.SECONDARY, icon)
- entry.set_icon_sensitive(Gtk.EntryIconPosition.SECONDARY, True)
- entry.set_icon_tooltip_markup (Gtk.EntryIconPosition.SECONDARY, "Click here to filter results")
- entry.set_placeholder_text("Filter results...")
-
- def on_icon_pressed(entry, icon_pos, event):
- if icon_pos == Gtk.EntryIconPosition.PRIMARY:
- self.cb.action_search(entry)
- elif icon_pos == Gtk.EntryIconPosition.SECONDARY:
- self.cb.gui_filter_visor(entry)
-
- entry.connect('changed', self.cb.gui_filter_visor)
- entry.connect("icon-press", on_icon_pressed)
- hbox.pack_start(entry, True, True, 0)
- tool.add(hbox)
- tool.set_expand(True)
- self.tool_bar.insert(tool, -1)
-
- # ~ Separator
- tool = Gtk.SeparatorToolItem.new()
- tool.set_draw(False)
- tool.set_expand(True)
- self.tool_bar.insert(tool, -1)
-
- # Button Total SAP Notes
- tool = Gtk.ToolItem()
- tool.set_expand(False)
- button = self.gui.add_widget('gtk_button_total_notes', Gtk.Button())
- button.set_relief(Gtk.ReliefStyle.NONE)
-
- popover = self.gui.add_widget('gtk_popover_button_total_notes', Gtk.Popover.new(button))
- label = self.gui.add_widget('gtk_label_total_notes', Gtk.Label())
- hbox = Gtk.HBox()
- hbox.pack_start(label, False, False, 0)
- button.add(hbox)
- button.connect('clicked', self.cb.gui_show_popover, popover)
- tool.add(button)
- self.tool_bar.insert(tool, -1)
-
- ## Popover body
- box = self.build_popover(popover)
- popover.add(box)
-
- # Fullscreen toggle button
- tool = Gtk.ToolItem()
- tool.set_expand(False)
- icon = self.im.get_new_image_icon('basico-fullscreen', 24, 24)
- box = self.gui.add_widget('gtk_box_container_icon_fullscreen', Gtk.Box())
- box.pack_start(icon, False, False, 0)
- button = Gtk.ToggleButton()
- button.set_relief(Gtk.ReliefStyle.NONE)
- button.connect('toggled', self.cb.gui_toggle_fullscreen)
- button.add(box)
- tool.add(button)
- self.tool_bar.insert(tool, -1)
-
- # Toolbar initial settings
- self.set_visible(True)
- self.set_no_show_all(False)
- self.tool_bar.set_hexpand(True)
-
-
- def get_services(self):
- self.gui = self.app.get_service("GUI")
- self.cb = self.app.get_service('Callbacks')
- self.sap = self.app.get_service('SAP')
- self.im = self.app.get_service('IM')
- self.settings = self.app.get_service('Settings')
- self.db = self.app.get_service('DB')
- self.uif = self.app.get_service("UIF")
-
-
- def build_popover(self, popover):
- box = Gtk.Box(spacing = 3, orientation="vertical")
- sid = '0000000000'
-
- def get_popover_button(text, icon_name):
- button = Gtk.Button()
- button.set_relief(Gtk.ReliefStyle.NONE)
- hbox = Gtk.HBox()
- icon = self.im.get_new_image_icon(icon_name, 24, 24)
- lbltext = Gtk.Label()
- lbltext.set_xalign(0.0)
- lbltext.set_markup('%s' % text)
- hbox.pack_start(icon, False, False, 3)
- hbox.pack_start(lbltext, True, True, 3)
- button.add(hbox)
- return button
-
- # Popover button "Bookmark"
- button = get_popover_button("(Un)bookmark SAP Notes", 'basico-bookmarks')
- button.connect('clicked', self.cb.switch_bookmark_current_set, popover)
- box.pack_start(button, False, False, 0)
-
- # Separator
- separator = Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)
- box.pack_start(separator, True, True, 0)
-
- # Popover button Collection Management
- button = get_popover_button("Manage collections", 'basico-collection')
- box.pack_start(button, False, False, 0)
- self.popcollections = self.gui.add_widget('gtk_popover_button_manage_collections_selected_notes', Gtk.Popover.new(button))
- self.popcollections.set_position(Gtk.PositionType.RIGHT)
- button.connect('clicked', self.cb.gui_show_popover, self.popcollections)
- self.popcollections.add(CollectionsMgtView(self.app, sid, self.popcollections))
-
- # Separator
- separator = Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)
- box.pack_start(separator, True, True, 0)
-
-
- # Popover button "Delete SAP Notes"
- button = get_popover_button("Delete SAP Notes", 'basico-delete')
- button.connect('clicked', self.cb.sapnote_delete, sid)
- box.pack_start(button, False, False, 0)
-
- return box
-
-class SAPNoteViewVisor(Gtk.Box, Service):
- def __init__(self, app):
- Gtk.Box.__init__(self, app)
- self.app = app
- self.bag = []
- self.get_services()
- self.setup()
-
-
- def get_services(self):
- self.gui = self.app.get_service("GUI")
- self.cb = self.app.get_service('Callbacks')
- self.sap = self.app.get_service('SAP')
- self.im = self.app.get_service('IM')
- self.settings = self.app.get_service('Settings')
- self.db = self.app.get_service('DB')
- self.uif = self.app.get_service("UIF")
- self.utils = self.app.get_service("Utils")
- self.annot = self.app.get_service('Annotation')
-
- def get_treeview(self):
- return self.treeview
-
-
- def setup(self):
- scr = Gtk.ScrolledWindow()
- scr.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
- scr.set_shadow_type(Gtk.ShadowType.IN)
- viewport = Gtk.Viewport()
- self.treeview = Gtk.TreeView()
- viewport.add(self.treeview)
- scr.add(viewport)
- self.pack_start(scr, True, True, 6)
-
- # Setup model
- self.model = Gtk.TreeStore(
- int, # key
- Pixbuf, # Icon
- int, # checkbox
- str, # sid
- str, # title
- str, # component
- str, # category
- str, # type
- str, # priority
- str, # last update
- str, # Annotation Id (extra key)
- str, # Timestamp
- )
-
- # Setup columns
- def get_column_header_widget(title, icon_name=None, width=24, height=24):
- hbox = Gtk.HBox()
- icon = self.im.get_new_image_icon(icon_name, width, height)
- label = Gtk.Label()
- label.set_markup("<b>%s</b>" % title)
- label.modify_font(Pango.FontDescription('Monospace 10'))
- hbox.pack_start(icon, False, False, 3)
- hbox.pack_start(label, True, True, 3)
- hbox.show_all()
- return hbox
-
- # SAP Note key
- self.renderer_key = Gtk.CellRendererText()
- self.renderer_key.set_property('height', 32)
- self.column_key = Gtk.TreeViewColumn('Key', self.renderer_key, text=0)
- self.column_key.set_visible(False)
- self.column_key.set_expand(False)
- self.column_key.set_clickable(False)
- self.column_key.set_sort_indicator(False)
- self.treeview.append_column(self.column_key)
-
- # Icon
- self.renderer_icon = Gtk.CellRendererPixbuf()
- self.renderer_icon.set_alignment(0.0, 0.5)
- self.column_icon = Gtk.TreeViewColumn('Bookmark', self.renderer_icon, pixbuf=1)
- widget = get_column_header_widget('', 'basico-bookmarks')
- self.column_icon.set_widget(widget)
- self.column_icon.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- self.column_icon.set_visible(True)
- self.column_icon.set_expand(False)
- self.column_icon.set_clickable(False)
- self.column_icon.set_sort_indicator(False)
- self.treeview.append_column(self.column_icon)
-
- # SAP Note Checkbox
- self.renderer_checkbox = Gtk.CellRendererToggle()
- self.renderer_checkbox.connect("toggled", self.toggle_checkbox)
- self.column_checkbox = Gtk.TreeViewColumn('', self.renderer_checkbox, active=2)
- self.column_checkbox.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- self.column_checkbox.set_visible(False)
- self.column_checkbox.set_expand(False)
- self.column_checkbox.set_clickable(False)
- self.column_checkbox.set_sort_indicator(False)
- self.column_checkbox.set_property('spacing', 50)
- self.treeview.append_column(self.column_checkbox)
-
- # SAP Note Id
- self.renderer_sid = Gtk.CellRendererText()
- self.renderer_sid.set_property('xalign', 1.0)
- self.renderer_sid.set_property('height', 36)
- self.renderer_sid.set_property('background', '#F0E3E3')
- self.column_sid = Gtk.TreeViewColumn('SAP Note Id', self.renderer_sid, markup=3)
- widget = get_column_header_widget('SAP Note Id', 'basico-sid')
- self.column_sid.set_widget(widget)
- self.column_sid.set_visible(True)
- self.column_sid.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- self.column_sid.set_expand(False)
- self.column_sid.set_clickable(True)
- self.column_sid.set_sort_indicator(True)
- self.column_sid.set_sort_column_id(0)
- self.column_sid.set_sort_order(Gtk.SortType.ASCENDING)
- self.model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
- self.treeview.append_column(self.column_sid)
-
- # SAP Note title
- self.renderer_title = Gtk.CellRendererText()
- self.renderer_title.set_property('background', '#FFFEEA')
- self.renderer_title.set_property('ellipsize', Pango.EllipsizeMode.MIDDLE)
- self.column_title = Gtk.TreeViewColumn('Title', self.renderer_title, markup=4)
- widget = get_column_header_widget('Title', 'basico-tag')
- self.column_title.set_widget(widget)
- self.column_title.set_visible(True)
- self.column_title.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- self.column_title.set_expand(True)
- self.column_title.set_clickable(True)
- self.column_title.set_sort_indicator(True)
- self.column_title.set_sort_column_id(4)
- self.treeview.append_column(self.column_title)
-
- # SAP Note Component
- self.renderer_component = Gtk.CellRendererText()
- self.renderer_component.set_property('background', '#E3E3F0')
- self.column_component = Gtk.TreeViewColumn('Component', self.renderer_component, markup=5)
- widget = get_column_header_widget('Component', 'basico-component')
- self.column_component.set_widget(widget)
- self.column_component.set_visible(True)
- self.column_component.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- self.column_component.set_expand(False)
- self.column_component.set_clickable(True)
- self.column_component.set_sort_indicator(True)
- self.column_component.set_sort_column_id(5)
- self.treeview.append_column(self.column_component)
-
- # SAP Note Category
- self.renderer_category = Gtk.CellRendererText()
- self.renderer_category.set_property('background', '#E3F1E3')
- self.column_category = Gtk.TreeViewColumn('Category', self.renderer_category, markup=6)
- widget = get_column_header_widget('Category', 'basico-category')
- self.column_category.set_widget(widget)
- self.column_category.set_visible(False)
- self.column_category.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- self.column_category.set_expand(False)
- self.column_category.set_clickable(True)
- self.column_category.set_sort_indicator(True)
- self.column_category.set_sort_column_id(6)
- self.treeview.append_column(self.column_category)
-
- # SAP Note Type
- self.renderer_type = Gtk.CellRendererText()
- self.renderer_type.set_property('background', '#e4f1f1')
- self.column_type = Gtk.TreeViewColumn('Type', self.renderer_type, markup=7)
- self.column_type.set_visible(True)
- self.column_type.set_expand(False)
- self.column_type.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- self.column_type.set_clickable(True)
- self.column_type.set_sort_indicator(True)
- self.column_type.set_sort_column_id(7)
- self.treeview.append_column(self.column_type)
-
- # SAP Note Priority
- self.renderer_priority = Gtk.CellRendererText()
- self.column_priority = Gtk.TreeViewColumn('Priority', self.renderer_priority, markup=8)
- self.column_priority.set_visible(False)
- self.column_priority.set_expand(True)
- self.column_priority.set_clickable(True)
- self.column_priority.set_sort_indicator(True)
- self.column_priority.set_sort_column_id(8)
- self.treeview.append_column(self.column_priority)
-
- # SAP Note UpdatedOn
- self.renderer_updated = Gtk.CellRendererText()
- self.renderer_updated.set_property('background', '#FFE6D1')
- self.column_updated = Gtk.TreeViewColumn('Updated On', self.renderer_updated, markup=9)
- self.column_updated.set_visible(True)
- self.column_updated.set_expand(False)
- self.column_updated.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- self.column_updated.set_clickable(True)
- self.column_updated.set_sort_indicator(True)
- self.column_updated.set_sort_column_id(11)
- self.treeview.append_column(self.column_updated)
-
- # Annotation Id
- self.renderer_annotation = Gtk.CellRendererText()
- self.column_annotation = Gtk.TreeViewColumn('Annotation Id', self.renderer_annotation, markup=10)
- self.column_annotation.set_visible(False)
- self.column_annotation.set_expand(False)
- self.column_annotation.set_clickable(False)
- self.column_annotation.set_sort_indicator(False)
- self.treeview.append_column(self.column_annotation)
-
- # Timestamp
- self.renderer_timestamp = Gtk.CellRendererText()
- self.column_timestamp = Gtk.TreeViewColumn('Annotation Id', self.renderer_timestamp, text=11)
- self.column_timestamp.set_visible(False)
- self.column_timestamp.set_expand(False)
- self.column_timestamp.set_clickable(False)
- self.column_timestamp.set_sort_indicator(False)
- self.treeview.append_column(self.column_timestamp)
-
- # Treeview properties
- self.treeview.set_can_focus(False)
- self.treeview.set_enable_tree_lines(True)
- self.treeview.set_headers_visible(True)
- self.treeview.set_enable_search(True)
- self.treeview.set_hover_selection(False)
- self.treeview.set_grid_lines(Gtk.TreeViewGridLines.NONE)
- self.treeview.set_enable_tree_lines(True)
- self.treeview.set_level_indentation(10)
- self.treeview.modify_font(Pango.FontDescription('Monospace 10'))
- self.treeview.connect('button_press_event', self.right_click)
-
- # DOC: In order to have a Gtk.Widged with sorting and filtering
- # capabilities, you have to filter the model first, and use this
- # new model to create the sorted model. Then, attach the sorted
- # model to the treeview...
-
- # Treeview filtering:
- self.visible_filter = self.model.filter_new()
- self.visible_filter.set_visible_func(self.visible_function)
- # ~ treeview.set_search_equal_func(self.search_function)
- # https://stackoverflow.com/questions/23355866/user-search-collapsed-rows-in-a-gtk-treeview
-
- # TreeView sorting
- self.sorted_model = Gtk.TreeModelSort(model=self.visible_filter)
- self.sorted_model.set_sort_func(0, self.sort_function, None)
-
- # Selection
- self.selection = self.treeview.get_selection()
- self.selection.set_mode(Gtk.SelectionMode.SINGLE)
- self.selection.connect('changed', self.row_changed)
-
- # Set model (filtered and sorted)
- self.treeview.set_model(self.sorted_model)
-
- self.show_all()
-
-
- def sort_function(self, model, row1, row2, user_data):
- sort_column = 0
-
- value1 = model.get_value(row1, sort_column)
- value2 = model.get_value(row2, sort_column)
-
- if value1 < value2:
- return -1
- elif value1 == value2:
- return 0
- else:
- return 1
-
-
- def visible_function(self, model, itr, data):
- entry = self.gui.get_widget('gtk_entry_filter_visor')
- text = entry.get_text()
- sid = str(model.get(itr, 3)[0])
- if sid.startswith('<'):
- sid = sid[3:-4]
- title = model.get(itr, 4)[0]
- component = str(model.get(itr, 5)[0])
- category = model.get(itr, 6)[0]
- rtype = model.get(itr, 7)[0]
- string = ' '.join(['SAP Note %s' % sid, title, component, category, rtype])
- match = text.upper() in string.upper()
- # ~ self.debug("%s in %s? %s" % (text, string, match))
-
- return match
-
-
- def update_total_sapnotes_count(self, count=0):
- statusbar = self.gui.get_widget('widget_statusbar')
- lblnotescount = self.gui.get_widget('gtk_label_total_notes')
- total = self.db.get_total()
-
- lblnotescount.set_markup("<b>%d/<big>%d</big></b>" % (count, total))
- statusbar.message("<b>View populated with %d SAP Notes</b>" % count)
-
-
- def get_visible_filter(self):
- return self.visible_filter
-
-
- def row_changed(self, selection):
- try:
- model, treeiter = selection.get_selected()
- if treeiter is not None:
- component = model[treeiter][5]
- if component == 'Annotation':
- aid = model[treeiter][10]
- is_valid = self.annot.is_valid(aid)
- if is_valid:
- self.cb.action_annotation_edit(aid)
- else:
- aid = None
- self.uif.set_widget_visibility('gtk_vbox_container_annotations', False)
- except Exception as error:
- self.debug(error)
- self.print_traceback()
-
-
- def toggle_checkbox(self, cell, path):
- self.model[path][2] = not self.model[path][2]
-
-
- def get_node(self, key, icon, checkbox, sid, title, component, category='', sntype='', priority='', updated='', aid='', timestamp=''):
- node = []
- node.append(key)
- node.append(icon)
- node.append(checkbox)
- node.append(sid)
- node.append(title)
- node.append(component)
- node.append(category)
- node.append(sntype)
- node.append(priority)
- node.append(updated)
- node.append(aid) # Extra key for annotations id (aid)
- node.append(timestamp)
- return node
-
-
- def refresh(self):
- self.populate()
-
-
- def get_bag(self):
- return self.bag
-
-
- def populate(self, bag=None, cid=None):
- icon_annotation = self.im.get_pixbuf_icon('basico-annotation', 32, 32)
- icon_content = self.im.get_pixbuf_icon('basico-logviewer', 32, 32)
- icon_link = self.im.get_pixbuf_icon('basico-browser', 32, 32)
- icon_empty = self.im.get_pixbuf_icon('basico-empty', 32, 32)
- icon_sapnote = self.im.get_pixbuf_icon('basico-sapnote', 32, 32)
- icon_bookmark = self.im.get_pixbuf_icon('basico-bookmarks', 32, 32)
- self.column_sid.set_visible(True)
-
- if bag is None:
- bag = self.bag
- else:
- self.bag = bag
-
-
- self.model.clear()
-
- for sid in self.bag:
- metadata = self.db.get_sapnote_metadata(sid)
- if metadata is not None:
- bookmark = metadata['bookmark']
- title = escape(metadata['title'])
- sid = str(int(metadata['id']))
- if bookmark:
- icon = icon_bookmark
- title = "<b>%s</b>" % title
- sid = "<b>%s</b>" % sid
- else:
- icon = icon_sapnote
-
- timestamp = metadata['releaseon']
- timestamp = timestamp.replace('-', '')
- timestamp = timestamp.replace(':', '')
- timestamp = timestamp.replace('T', '_')
-
- node = self.get_node( int(metadata['id']),
- icon,
- bookmark,
- '<b>%s</b>' % sid,
- title,
- escape(metadata['componentkey']),
- escape(metadata['category']),
- escape(metadata['type']),
- escape(metadata['priority']),
- self.utils.fuzzy_date_from_timestamp(timestamp),
- '',
- timestamp
- )
- pid = self.model.append(None, node)
-
- # Load annotations
- files = self.annot.get_by_sid(metadata['id'])
- for fname in files:
- with open(fname, 'r') as fa:
- annotation = json.load(fa)
- self.debug("Populating annotation: %s" % annotation['aid'])
- node = self.get_node( 0,
- icon_annotation,
- False,
- '',
- annotation['title'],
- annotation['component'],
- '',
- annotation['type'],
- '',
- self.utils.fuzzy_date_from_timestamp(annotation['timestamp']),
- annotation['aid'],
- annotation['timestamp']
- )
- self.model.append(pid, node)
- self.treeview.set_model(self.sorted_model)
- self.update_total_sapnotes_count(len(self.model))
- self.show_widgets()
- stack = self.gui.get_widget('gtk_stack_main')
- stack.set_visible_child_name('visor')
- # ~ self.annot.get_all()
-
-
- def populate_annotations(self, annotations=None):
- icon_annotation = self.im.get_pixbuf_icon('basico-annotation', 32, 32)
- icon_content = self.im.get_pixbuf_icon('basico-logviewer', 32, 32)
- icon_link = self.im.get_pixbuf_icon('basico-browser', 32, 32)
- icon_empty = self.im.get_pixbuf_icon('basico-empty', 32, 32)
- icon_bookmark = self.im.get_pixbuf_icon('basico-bookmarks', 32, 32)
- icon_sapnote = self.im.get_pixbuf_icon('basico-sapnote', 32, 32)
- self.column_sid.set_visible(False)
-
- self.model.clear()
-
- if annotations is None:
- annotations = self.annot.get_all()
-
- snpids = {}
-
- for fname in annotations:
- with open(fname, 'r') as fa:
- annotation = json.load(fa)
- sid = self.annot.get_sid(annotation['aid'])
- # ~ if sid != '0000000000':
- # ~ try:
- # ~ pid = snpids[self.db.normalize_sid(sid)]
- # ~ except:
- # ~ metadata = self.db.get_sapnote_metadata(sid)
- # ~ if metadata is not None:
- # ~ bookmark = metadata['bookmark']
- # ~ title = escape(metadata['title'])
- # ~ sid = str(int(metadata['id']))
- # ~ if bookmark:
- # ~ icon = icon_bookmark
- # ~ title = "<b>%s</b>" % title
- # ~ sid = "<b>%s</b>" % sid
- # ~ else:
- # ~ icon = icon_sapnote
-
- # ~ timestamp = metadata['releaseon']
- # ~ timestamp = timestamp.replace('-', '')
- # ~ timestamp = timestamp.replace(':', '')
- # ~ timestamp = timestamp.replace('T', '_')
- # ~ node = self.get_node( int(metadata['id']),
- # ~ icon,
- # ~ bookmark,
- # ~ '<b>%s</b>' % sid,
- # ~ title,
- # ~ escape(metadata['componentkey']),
- # ~ escape(metadata['category']),
- # ~ escape(metadata['type']),
- # ~ escape(metadata['priority']),
- # ~ self.utils.fuzzy_date_from_timestamp(timestamp),
- # ~ '',
- # ~ timestamp
- # ~ )
- # ~ pid = self.model.append(None, node)
- # ~ snpids[self.db.normalize_sid(sid)] = pid
- # ~ else:
- # ~ pid = None
- node = self.get_node( 0,
- icon_annotation,
- False,
- str(int(sid)),
- annotation['title'],
- annotation['component'],
- '',
- annotation['type'],
- '',
- self.utils.fuzzy_date_from_timestamp(annotation['timestamp']),
- annotation['aid'],
- annotation['timestamp']
- )
- self.model.append(None, node)
- self.treeview.set_model(self.sorted_model)
- self.treeview.expand_all()
- self.update_total_sapnotes_count(len(self.model))
- self.show_widgets()
- stack = self.gui.get_widget('gtk_stack_main')
- stack.set_visible_child_name('visor')
-
-
- def show_widgets(self):
- button = self.gui.get_widget('gtk_button_total_notes')
- button.set_no_show_all(False)
- button.show_all()
-
-
- def right_click(self, treeview, event, data=None):
- if event.button == 3:
- rect = Gdk.Rectangle()
- rect.x = x = int(event.x)
- rect.y = y = int(event.y)
- pthinfo = self.treeview.get_path_at_pos(x,y)
- if pthinfo is not None:
- path,col,cellx,celly = pthinfo
- model = treeview.get_model()
- treeiter = model.get_iter(path)
- component = model[treeiter][5]
- sid = model[treeiter][0]
- sid = "0"*(10 - len(str(sid))) + str(sid)
- toolbar = self.gui.get_widget('visortoolbar')
- popover = self.gui.add_widget('gtk_popover_visor_row', Gtk.Popover.new(treeview))
- popover.set_position(Gtk.PositionType.TOP)
- popover.set_pointing_to(rect)
- box = self.build_popover(sid, popover, component)
- popover.add(box)
- self.cb.gui_show_popover(None, popover)
-
-
- def build_popover(self, sid, popover, component):
- box = Gtk.Box(spacing = 3, orientation="vertical")
-
- def get_popover_button(text, icon_name):
- button = Gtk.Button()
- button.set_relief(Gtk.ReliefStyle.NONE)
- hbox = Gtk.HBox()
- icon = self.im.get_new_image_icon(icon_name, 24, 24)
- lbltext = Gtk.Label()
- lbltext.set_xalign(0.0)
- lbltext.set_markup('%s' % text)
- hbox.pack_start(icon, False, False, 3)
- hbox.pack_start(lbltext, True, True, 3)
- button.add(hbox)
- return button
-
- if component == 'Annotation':
- # Popover button "Delete annotation"
- button = get_popover_button("Delete annotation", 'basico-delete')
- button.show_all()
- button.connect('clicked', self.cb.action_annotation_delete)
- box.pack_start(button, False, False, 0)
-
- # Popover button "Duplicate annotation"
- button = get_popover_button("Duplicate annotation", 'basico-duplicate')
- button.show_all()
- button.connect('clicked', self.cb.action_annotation_duplicate)
- box.pack_start(button, False, False, 0)
-
- else:
- # Popover button "Add an annotation"
- button = get_popover_button("Add an annotation", 'basico-annotation')
- button.show_all()
- button.connect('clicked', self.cb.gui_annotation_widget_show, sid, 'create')
- box.pack_start(button, False, False, 0)
-
- # Popover button "Open SAP Note"
- button = get_popover_button("See SAP Note", 'basico-browse')
- button.connect('clicked', self.cb.sapnote_browse, sid)
- box.pack_start(button, False, False, 0)
-
- # Popover button "Bookmark"
- button = get_popover_button("(Un)bookmark SAP Note", 'basico-bookmarks')
- button.connect('clicked', self.cb.switch_bookmark, [sid], popover)
- box.pack_start(button, False, False, 0)
-
- # Separator
- separator = Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)
- box.pack_start(separator, True, True, 0)
-
- # Popover button Collection Management
- button = get_popover_button("Manage collections", 'basico-collection')
- box.pack_start(button, False, False, 0)
- self.popcollections = self.gui.add_widget('gtk_popover_button_manage_collections_single_note', Gtk.Popover.new(button))
- self.popcollections.set_position(Gtk.PositionType.RIGHT)
- button.connect('clicked', self.cb.gui_show_popover, self.popcollections)
- self.popcollections.add(CollectionsMgtView(self.app, sid, self.popcollections))
-
- # Separator
- separator = Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)
- box.pack_start(separator, True, True, 0)
-
-
- # Popover button "Delete SAP Note"
- button = get_popover_button("Delete SAP Note", 'basico-delete')
- button.connect('clicked', self.cb.sapnote_delete, sid)
- box.pack_start(button, False, False, 0)
-
- return box
Index: branches/BR-0.3/basico/log.py
===================================================================
--- branches/BR-0.3/basico/log.py (revision 232)
+++ branches/BR-0.3/basico/log.py (nonexistent)
@@ -1,33 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: log.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: log service
-
-from os.path import sep as SEP
-import logging
-import inspect
-
-def get_logger(name, LOG_FILE):
- """Returns a new logger with personalized.
- @param name: logger name
- """
- log = logging.getLogger(name)
- log.setLevel(logging.DEBUG)
-
- ## Redirect log to stdout
- formatter = logging.Formatter("%(levelname)7s | %(asctime)s | %(message)s")
- # ~ formatter = logging.Formatter("%(levelname)7s | %(lineno)4d |%(name)15s | %(asctime)s | %(message)s")
- ch = logging.StreamHandler() # Create console handler and set level to debug
- ch.setLevel(logging.DEBUG) # Set logging devel
- ch.setFormatter(formatter) # add formatter to console handler
- log.addHandler(ch) # add console handler to logger
-
- #FIXME Redirect log to file
- fh = logging.FileHandler(LOG_FILE)
- fh.setFormatter(formatter)
- fh.setLevel(logging.DEBUG) # Set logging devel
- log.addHandler(fh) # add file handler to logger
-
- return log
/branches/BR-0.3/basico/log.py
Property changes:
Deleted: svn:executable
## -1 +0,0 ##
-*
\ No newline at end of property
Index: branches/BR-0.3/basico/env.py
===================================================================
--- branches/BR-0.3/basico/env.py (revision 232)
+++ branches/BR-0.3/basico/env.py (nonexistent)
@@ -1,67 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: env.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: Environment variables
-
-import sys
-import os
-from os.path import abspath, sep as SEP
-
-ROOT = abspath(sys.modules[__name__].__file__ + "/..")
-USER_DIR = os.path.expanduser('~')
-
-# App Info
-APP = {}
-APP['short'] = "basico"
-APP['name'] = "SAP Notes Manager for SAP Consultants"
-APP['license'] = "The code is licensed under the terms of the GPL v3\nso you're free to grab, extend, improve and fork the code\nas you want"
-APP['desc'] = "SAP Notes Manager for SAP Consultants\n\nThe code is licensed under the terms of the GPL v3 so you're free to grab, extend, improve and fork the code as you want"
-APP['version'] = "0.3"
-APP['authors'] = ["Tomás Vírseda <tomasvirseda@gmail.com>"]
-APP['documenters'] = ["Tomás Vírseda <tomasvirseda@gmail.com>"]
-APP['email'] = "t00m@t00mlabs.net"
-
-
-# Local paths
-LPATH = {}
-LPATH['ROOT'] = USER_DIR + SEP + '.basico' + SEP
-LPATH['ETC'] = LPATH['ROOT'] + 'etc' + SEP
-LPATH['VAR'] = LPATH['ROOT'] + 'var' + SEP
-LPATH['PLUGINS'] = LPATH['VAR'] + 'plugins' + SEP
-LPATH['LOG'] = LPATH['VAR'] + 'logs' + SEP
-LPATH['TMP'] = LPATH['VAR'] + 'tmp' + SEP
-LPATH['DB'] = LPATH['VAR'] + 'db' + SEP
-LPATH['CACHE'] = LPATH['DB'] + 'cache' + SEP
-LPATH['ANNOTATIONS'] = LPATH['DB'] + 'annotations' + SEP
-LPATH['RESOURCES'] = LPATH['DB'] + 'resources' + SEP
-LPATH['WWW'] = LPATH['VAR'] + 'www' + SEP
-LPATH['EXPORT'] = LPATH['VAR'] + 'export' + SEP
-LPATH['BACKUP'] = LPATH['EXPORT'] + 'backup' + SEP
-LPATH['PDF'] = LPATH['EXPORT'] + 'pdf' + SEP
-LPATH['OPT'] = LPATH['ROOT'] + 'opt' + SEP
-LPATH['DRIVERS'] = LPATH['OPT'] + 'webdrivers' + SEP
-
-
-# Global paths
-GPATH = {}
-GPATH['ROOT'] = ROOT
-GPATH['DATA'] = GPATH['ROOT'] + SEP + 'data' + SEP
-GPATH['UI'] = GPATH['DATA'] + 'ui' + SEP
-GPATH['ICONS'] = GPATH['DATA'] + 'icons' + SEP
-GPATH['PLUGINS'] = GPATH['DATA'] + 'plugins' + SEP
-GPATH['SHARE'] = GPATH['DATA'] + 'share' + SEP
-GPATH['DOC'] = GPATH['SHARE'] + 'docs' + SEP
-GPATH['RES'] = GPATH['DATA'] + 'res' + SEP
-
-# Configuration, SAP Notes Database and Log files
-FILE = {}
-FILE['COLLECTIONS'] = LPATH['DB'] + 'collections.json'
-FILE['CNF'] = LPATH['ETC'] + 'basico.ini'
-FILE['SAP'] = LPATH['DB'] + 'sapnotes.json'
-FILE['LOG'] = LPATH['LOG'] + 'basico.log'
-FILE['CREDITS'] = GPATH['DOC'] + 'CREDITS'
-
-# APP STATS
-STATS = {}
/branches/BR-0.3/basico/env.py
Property changes:
Deleted: svn:executable
## -1 +0,0 ##
-*
\ No newline at end of property
Index: branches/BR-0.3/basico/collections.py
===================================================================
--- branches/BR-0.3/basico/collections.py (revision 232)
+++ branches/BR-0.3/basico/collections.py (nonexistent)
@@ -1,111 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: collections.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: Collections service
-
-import os
-import json
-import uuid
-
-from .env import FILE
-from .service import Service
-
-HEADER = ['id', 'title']
-COLLECTIONS_FILE = FILE['COLLECTIONS']
-
-
-class Collections(Service):
- def initialize(self):
- '''
- Setup Collections Service
- '''
- self.get_services()
- self.clts = {}
- self.load()
-
-
- def get_services(self):
- self.gui = self.app.get_service("GUI")
- self.uif = self.app.get_service("UIF")
- self.cb = self.app.get_service('Callbacks')
- self.im = self.app.get_service('IM')
-
-
- def load(self):
- try:
- with open(COLLECTIONS_FILE, 'r') as ft:
- self.clts = json.load(ft)
- self.debug ("Loaded %d collections" % len(self.clts))
- except Exception as error:
- self.debug("Collections file not found. Creating a new one.")
- self.save()
-
-
- def save(self, collections={}):
- if len(collections) == 0:
- collections = self.clts
- with open(COLLECTIONS_FILE, 'w') as ft:
- json.dump(collections, ft)
- self.debug ("Saved %d collections" % (len(collections)))
-
-
- def get_all(self):
- return self.clts
-
-
- def get_collections_name(self):
- names = []
- for tid in self.clts:
- names.append(self.clts[tid])
- return names
-
-
- def get_collections_id(self):
- tids = []
- for tid in self.clts:
- tids.append(tid)
- return tids
-
-
- def create(self, entry):
- tid = str(uuid.uuid4())
- name = entry.get_text()
-
- if len(name) == 0:
- return
-
- tid_exists = tid in self.get_collections_id()
- name_exists = name in self.get_collections_name()
-
- if tid_exists:
- self.debug('Collision? :)')
- self.create(entry)
- else:
- if name_exists:
- return
- else:
- self.clts[tid] = name
- self.debug("Created collection: %s" % name)
- self.save()
-
-
- def delete(self, tid):
- try:
- name = self.get_name(tid)
- del(self.clts[tid])
- self.debug("Deleted Collection: %s" % name)
- self.save()
- return True
- except KeyError:
- self.debug("You can't delete a non existent collection...")
- except Exception as error:
- self.debug("Error deleting collection: %s" % error)
- self.debug(self.clts)
- raise
-
-
- def get_name(self, tid):
- return self.clts[tid]
-
Index: branches/BR-0.3/basico/iconmanager.py
===================================================================
--- branches/BR-0.3/basico/iconmanager.py (revision 232)
+++ branches/BR-0.3/basico/iconmanager.py (nonexistent)
@@ -1,88 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: menus.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: Icon manager service
-
-import os
-
-import pkg_resources
-
-from gi.repository import Gtk
-from gi.repository import Gio
-from gi.repository import Pango
-from gi.repository.GdkPixbuf import Pixbuf
-
-from .service import Service
-
-
-class IconManager(Service):
- def initialize(self):
- APP_DIR_ICONS = self.app.get_var('ICONS')
- self.icondict = {}
- self.imgdict = {}
- self.theme = Gtk.IconTheme.get_default()
- self.theme.prepend_search_path (APP_DIR_ICONS)
-
-
- def get_themed_icon(self, icon_name):
- APP_DIR_ICONS = self.app.get_var('ICONS')
- ICON = APP_DIR_ICONS + icon_name + '.png'
- icon = Gio.ThemedIcon.new(ICON)
-
- return icon
-
-
-
- def get_icon(self, name, width=24, height=24):
- key = "%s-%d-%d" % (name, width, height)
-
- # Get icon from cache if exists or add a new one
- try:
- icon = self.icondict[key]
- except:
- # ~ self.debug("Icon Key '%s' not found", key)
- iconinfo = self.theme.lookup_icon(name, width, Gtk.IconLookupFlags.GENERIC_FALLBACK)
- icon = iconinfo.load_icon()
- self.icondict[key] = icon
-
- return icon
-
-
- def get_pixbuf_icon(self, name, width=36, height=36):
- key = "%s-%d-%d" % (name, width, height)
-
- # Get icon from cache if exists or add a new one
- try:
- icon = self.icondict[key]
- except Exception as error:
- icon = None
- if name in self.theme.list_icons():
- icon = self.theme.load_icon(name, width, Gtk.IconLookupFlags.GENERIC_FALLBACK)
- self.icondict[key] = icon
-
- return icon
-
-
- def get_new_image_icon(self, name, width=36, height=36):
- pixbuf = self.get_pixbuf_icon(name, width, height)
- icon = Gtk.Image()
- icon.set_from_pixbuf(pixbuf)
-
- return icon
-
-
- def get_image_icon(self, name, width=36, height=36):
- key = "%s-%d-%d" % (name, width, height)
- try:
- icon = self.imgdict[key]
- except Exception as error:
- icon = None
- if name in self.theme.list_icons():
- pixbuf = self.theme.load_icon(name, width, Gtk.IconLookupFlags.GENERIC_FALLBACK)
- icon = Gtk.Image()
- icon.set_from_pixbuf(pixbuf)
- self.imgdict[key] = icon
-
- return icon
Index: branches/BR-0.3/basico/driver.py
===================================================================
--- branches/BR-0.3/basico/driver.py (revision 232)
+++ branches/BR-0.3/basico/driver.py (nonexistent)
@@ -1,98 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: driver.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: Selenium Driver service
-
-import os
-import selenium
-from selenium import webdriver
-from selenium.webdriver.support.wait import WebDriverWait
-from selenium import webdriver
-from selenium.webdriver.common.keys import Keys
-from selenium.common.exceptions import WebDriverException
-from selenium.common.exceptions import NoSuchElementException
-from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
-from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
-from selenium.webdriver.firefox.options import Options
-
-from .service import Service
-
-GECKODRIVER_URL = "https://github.com/mozilla/geckodriver/releases/download/v0.15.0/geckodriver-v0.15.0-linux64.tar.gz"
-
-
-class SeleniumDriver(Service):
- def open(self):
- '''
- In order to have selenium working with Firefox and be able to
- get SAP Notes from launchpad.support.sap.com you must:
- 1. Use a browser certificate (SAP Passport) in order to avoid
- renewed logons.
- You can apply for it at:
- https://support.sap.com/support-programs-services/about/getting-started/passport.html
- 2. Get certificate and import it into Firefox.
- Open menu -> Preferences -> Advanced -> View Certificates
- -> Your Certificates -> Import
- 3. Trust this certificate (auto select)
- 4. Check it. Visit some SAP Note url in Launchpad.
- No credentials will be asked.
- Launchpad must load target page successfully.
- '''
-
- utils = self.get_service('Utils')
- options = Options()
- # ~ Uncomment this line to set Firefox to headless
- options.add_argument('--headless')
- FIREFOX_PROFILE_DIR = utils.get_firefox_profile_dir()
- FIREFOX_PROFILE = webdriver.FirefoxProfile(FIREFOX_PROFILE_DIR)
- driver = webdriver.Firefox(firefox_profile=FIREFOX_PROFILE, firefox_options=options)
- self.debug("Webdriver initialited")
- return driver
-
-
- def close(self, driver):
- driver.quit()
- self.debug("Webdriver closed")
- driver = None
-
-
-
- def load(self, driver, URL):
- driver.get(URL)
- return driver
-
-
- def check(self):
- """
- Check gecko webdriver
- You must install geckodriver. It is mandatory
- Yo can download it from:
- https://github.com/mozilla/geckodriver/
- Then, extract the binary and copy it to somewhere in your $PATH.
- If OS is Linux: /usr/local/bin/geckodriver
- If OS is Windows: C:\Windows\System32 or elsewhere.
-
- Basico will try to do it for you.
- """
- utils = self.get_service('Utils')
-
- # First, add BASICO OPT Path to $PATH
- GECKO_INSTALL_DIR = self.get_var('DRIVERS', 'local')
- os.environ["PATH"] += os.pathsep + GECKO_INSTALL_DIR
- # Then, look for Geckodriver
- GECKODRIVER = utils.which('geckodriver')
-
- if not GECKODRIVER:
- self.debug("Attempting to download Gecko driver and install it.")
- utils.install_geckodriver()
-
- GECKODRIVER = utils.which('geckodriver')
- if GECKODRIVER is None:
- self.log.warning("Gecko driver not found.")
- return False
- else:
- self.debug("Gecko Webdriver found in: %s" % GECKODRIVER)
- return True
-
-
Index: branches/BR-0.3/basico/uiapp.py
===================================================================
--- branches/BR-0.3/basico/uiapp.py (revision 232)
+++ branches/BR-0.3/basico/uiapp.py (nonexistent)
@@ -1,79 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: uiapp.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: Gtk.Application instance
-
-import os
-import sys
-import subprocess
-from datetime import datetime
-
-from gi.repository import Gtk
-from gi.repository import Gdk
-from gi.repository.GdkPixbuf import Pixbuf
-from gi.repository import Pango
-from gi.repository import GLib
-from gi.repository import GObject
-from gi.repository import Gio
-
-from .service import Service
-from .window import GtkAppWindow
-from .env import FILE, APP
-
-class UIApp(Gtk.Application):
- """
- """
- def __init__(self, controller):
- Gtk.Application.__init__(self,
- application_id="net.t00mlabs.basico",
- flags=Gio.ApplicationFlags.FLAGS_NONE)
- GLib.set_application_name("Basico")
- GLib.set_prgname('basico')
- self.controller = controller
- self.get_services()
-
-
- def get_services(self):
- self.gui = self.controller.get_service('GUI')
- self.im = self.controller.get_service('IM')
- self.cb = self.controller.get_service('Callbacks')
-
-
- def do_activate(self):
- self.window = self.gui.add_widget('gtk_app_window_main', GtkAppWindow(self))
- self.window.connect("delete-event", self.gui.quit)
- self.window.show()
-
-
- def do_startup(self):
- Gtk.Application.do_startup(self)
-
- # show icons on the buttons
- #~ settings = Gtk.Settings.get_default()
- #~ DEPRECATED: settings.props.gtk_button_images = True
-
- # actions that control the application: create, connect their
- # signal to a callback method (see below), add the action to the
- # application
-
-
- def get_window(self):
- return self.window
-
-
- def get_controller(self):
- return self.controller
-
-
- def cb_hide_about(self, aboutdialog, user_data):
- aboutdialog.destroy()
-
-
- def cb_toggle_fullscreen(self, tgbutton, user_data=None):
- if tgbutton.get_active():
- self.window.fullscreen()
- else:
- self.window.unfullscreen()
-
Index: branches/BR-0.3/basico/window.py
===================================================================
--- branches/BR-0.3/basico/window.py (revision 232)
+++ branches/BR-0.3/basico/window.py (nonexistent)
@@ -1,401 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: window.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: Gtk.ApplicationWindow implementation
-
-import os
-import stat
-import time
-import platform
-
-import gi
-gi.require_version('Gtk', '3.0')
-
-from gi.repository import GLib
-from gi.repository import GObject
-from gi.repository import Gtk
-from gi.repository import Gdk
-from gi.repository import Gio
-from gi.repository import Pango
-from gi.repository.GdkPixbuf import Pixbuf
-
-from .sapnoteviewmenu import SAPNoteViewMenu
-from .sapnoteviewvisor import SAPNoteViewVisor, SAPNoteViewVisorToolbar
-from .widgets.about import About
-from .widgets.settings import SettingsView
-from .widgets.logviewer import LogViewer
-from .widgets.annotation import AnnotationWidget
-from .widgets.statusbar import Statusbar
-from .service import Service
-
-class GtkAppWindow(Gtk.ApplicationWindow, Service):
- def __init__(self, uiapp):
- self.setup_controller(uiapp)
- self.get_services()
- self.log = self.controller.log
- self.gui.add_widget('uiapp', uiapp)
- self.setup_window()
- self.controller.debug("Window setup correctly")
- self.setup_widgets()
- self.controller.debug("Widgets setup correctly")
- self.run()
-
-
- def get_services(self):
- self.gui = self.controller.get_service("GUI")
- self.app = self.gui.get_app()
- self.db = self.controller.get_service("DB")
- self.uif = self.controller.get_service("UIF")
- self.prefs = self.controller.get_service("Settings")
- self.im = self.controller.get_service('IM')
- self.cb = self.controller.get_service('Callbacks')
-
-
- def setup_controller(self, uiapp):
- self.uiapp = uiapp
- self.controller = uiapp.get_controller()
-
-
- def setup_window(self):
- app_title = self.controller.get_app_info('name')
- Gtk.Window.__init__(self, title=app_title, application=self.uiapp)
- icon = self.im.get_icon('basico-component', 48, 48)
- self.set_icon(icon)
- # FIXME
- # From docs: Don’t use this function. It sets the X xlib.Window
- # System “class” and “name” hints for a window.
- # But I have to do it or it doesn't shows the right title. ???
- self.set_wmclass (app_title, app_title)
- self.set_role(app_title)
- self.set_default_size(1024, 728)
- self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
- self.maximize ()
- self.setup_headerbar()
- self.show_all()
-
-
- def setup_headerbar(self):
- hb = self.gui.add_widget('gtk_headerbar_container', Gtk.HeaderBar())
- hb.set_show_close_button(True)
- hb.props.title = "Basico"
- hb.props.subtitle = "SAP Notes Manager for SAP Consultants"
- lhbox = self.setup_headerbar_left(hb)
- hb.pack_start(lhbox)
- rhbox = self.setup_headerbar_right(hb)
-
- hb.pack_end(rhbox)
- self.set_titlebar(hb)
- hb.show_all()
-
-
- def setup_headerbar_left(self, hb):
- # ~ '''Left headerbar side not used by now'''
- lhbox = Gtk.HBox()
-
- ### Dashboard / Visor
- hbox = Gtk.HBox()
- icon = self.im.get_pixbuf_icon('basico-dashboard', 24, 24)
- image = Gtk.Image()
- image.set_from_pixbuf(icon)
- label = Gtk.Label()
- hbox.pack_start(image, False, False, 3)
- hbox.pack_start(label, False, False, 3)
- button = self.gui.add_widget('gtk_button_dashboard', Gtk.Button())
- button.add(hbox)
- button.set_relief(Gtk.ReliefStyle.NONE)
- lhbox.pack_start(button, False, False, 0)
- button.connect('clicked', self.cb.gui_show_dashboard)
-
- return lhbox
-
-
- def setup_headerbar_right(self, hb):
- rhbox = Gtk.HBox()
-
- ## System Menu
- button = Gtk.Button()
- icon = self.im.get_pixbuf_icon('basico-menu-system', 24, 24)
- image = Gtk.Image()
- image.set_from_pixbuf(icon)
- button.set_image(image)
- button.set_relief(Gtk.ReliefStyle.NONE)
- popover = Gtk.Popover.new(button)
- self.gui.add_widget('gtk_popover_button_menu_system', popover)
- button.connect('clicked', self.cb.gui_show_popover, popover)
- rhbox.pack_end(button, False, False, 0)
-
- # Popover body
- box = Gtk.Box(spacing = 0, orientation="vertical")
-
- ### About
- hbox = Gtk.Box(spacing = 0, orientation="horizontal")
- icon = self.im.get_pixbuf_icon('basico-about', 24, 24)
- image = Gtk.Image()
- image.set_from_pixbuf(icon)
- label = Gtk.Label("About")
- hbox.pack_start(image, False, False, 3)
- hbox.pack_start(label, False, False, 3)
- button = Gtk.Button()
- button.add(hbox)
- button.set_relief(Gtk.ReliefStyle.NONE)
- button.connect('clicked', self.cb.gui_show_about)
- box.pack_end(button, False, False, 0)
-
- # ~ ### Help
- # ~ hbox = Gtk.Box(spacing = 0, orientation="horizontal")
- # ~ icon = self.im.get_pixbuf_icon('basico-help', 24, 24)
- # ~ image = Gtk.Image()
- # ~ image.set_from_pixbuf(icon)
- # ~ label = Gtk.Label("Help")
- # ~ hbox.pack_start(image, False, False, 3)
- # ~ hbox.pack_start(label, False, False, 3)
- # ~ button = Gtk.Button()
- # ~ button.add(hbox)
- # ~ button.set_relief(Gtk.ReliefStyle.NONE)
- # ~ button.connect('clicked', self.cb.gui_show_help)
- # ~ box.pack_end(button, False, False, 0)
-
- ### Log viewer
- hbox = Gtk.Box(spacing = 0, orientation="horizontal")
- icon = self.im.get_pixbuf_icon('basico-logviewer', 24, 24)
- image = Gtk.Image()
- image.set_from_pixbuf(icon)
- label = Gtk.Label("Log viewer")
- hbox.pack_start(image, False, False, 3)
- hbox.pack_start(label, False, False, 3)
- button = Gtk.Button()
- button.add(hbox)
- button.set_relief(Gtk.ReliefStyle.NONE)
- button.connect('clicked', self.cb.gui_show_log)
- box.pack_end(button, False, False, 0)
-
- ### Settings
- hbox = Gtk.Box(spacing = 0, orientation="horizontal")
- icon = self.im.get_pixbuf_icon('basico-settings', 24, 24)
- image = Gtk.Image()
- image.set_from_pixbuf(icon)
- label = Gtk.Label("Settings")
- hbox.pack_start(image, False, False, 3)
- hbox.pack_start(label, False, False, 3)
- button = Gtk.Button()
- button.add(hbox)
- button.set_relief(Gtk.ReliefStyle.NONE)
- button.connect('clicked', self.cb.gui_show_settings)
- box.pack_start(button, False, False, 0)
-
- ### Backup
- hbox = Gtk.Box(spacing = 0, orientation="horizontal")
- icon = self.im.get_pixbuf_icon('basico-backup', 24, 24)
- image = Gtk.Image()
- image.set_from_pixbuf(icon)
- label = Gtk.Label("Backup")
- hbox.pack_start(image, False, False, 3)
- hbox.pack_start(label, False, False, 3)
- button = Gtk.Button()
- button.add(hbox)
- button.set_relief(Gtk.ReliefStyle.NONE)
- button.connect('clicked', self.cb.action_backup)
- box.pack_start(button, False, False, 0)
-
-
- popover.add(box)
- return rhbox
-
-
- def setup_widgets(self):
- # Mainbox
- mainbox = self.gui.add_widget('gtk_vbox_container_main', Gtk.VBox())
- mainbox.set_hexpand(True)
- paned = self.gui.add_widget('gtk_hpaned', Gtk.HPaned())
- paned.set_property('margin-bottom', 6)
- paned.set_wide_handle(False)
- paned.set_position(300)
-
- # Paned
- ## Left view
- box = self.gui.add_widget('gtk_vbox_container_menu_view', Gtk.VBox())
- box.set_property('margin-left', 6)
- box.set_property('margin-right', 6)
- box.set_property('margin-bottom', 0)
-
- ### Toolbar
- toolbar = Gtk.Toolbar()
- toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
-
- #### Filter entry tool
- tool = Gtk.ToolItem.new()
-
- hbox = Gtk.HBox()
- viewfilter = self.gui.add_widget('gtk_entry_filter_view', Gtk.Entry())
- viewfilter.connect('activate', self.cb.gui_viewmenu_filter)
-
- icon = self.im.get_pixbuf_icon('basico-refresh')
- viewfilter.set_icon_from_pixbuf(Gtk.EntryIconPosition.PRIMARY, icon)
- viewfilter.set_icon_sensitive(Gtk.EntryIconPosition.PRIMARY, True)
- viewfilter.set_icon_tooltip_markup (Gtk.EntryIconPosition.PRIMARY, "Refresh and collapse")
-
- icon = self.im.get_pixbuf_icon('basico-filter')
- viewfilter.set_icon_from_pixbuf(Gtk.EntryIconPosition.SECONDARY, icon)
- viewfilter.set_icon_sensitive(Gtk.EntryIconPosition.SECONDARY, True)
- viewfilter.set_icon_tooltip_markup (Gtk.EntryIconPosition.SECONDARY, "Click here to expand the tree")
- viewfilter.set_placeholder_text("Filter this view...")
-
- def on_icon_pressed(entry, icon_pos, event):
- if icon_pos == Gtk.EntryIconPosition.PRIMARY:
- viewmenu = self.gui.get_widget('viewmenu')
- viewmenu.refresh()
- elif icon_pos == Gtk.EntryIconPosition.SECONDARY:
- self.cb.expand_menuview()
-
- viewfilter.connect("icon-press", on_icon_pressed)
-
- hbox.pack_start(viewfilter, True, True, 0)
- tool.add(hbox)
- tool.set_expand(True)
- toolbar.insert(tool, -1)
-
- box.pack_start(toolbar, False, False, 0)
-
- ### View treeview
- scr = Gtk.ScrolledWindow()
- scr.set_hexpand(True)
- scr.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
- scr.set_shadow_type(Gtk.ShadowType.IN)
- vwp = Gtk.Viewport()
- vwp.set_hexpand(True)
- viewsbox = self.gui.add_widget('gtk_box_container_views', Gtk.Box())
- viewsbox.set_hexpand(True)
- vwp.add(viewsbox)
- scr.add(vwp)
- box.pack_start(scr, True, True, 0)
- paned.add1(box)
-
- ## Right view
- box = Gtk.VBox()
- box.set_hexpand(True)
- stack_main = self.gui.add_widget('gtk_stack_main', Gtk.Stack())
- stack_main.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
- stack_main.set_transition_duration(2500)
- box.pack_start(stack_main, True, True, 0)
-
- ### Visor stack
- stack_child = self.setup_stack_visor()
- stack_main.add_titled(stack_child, "visor", "SAP Notes Visor")
-
- ### About stack
- stack_child = self.setup_stack_about()
- stack_main.add_titled(stack_child, "about", "About Basico")
-
- ### Log stack
- stack_child = self.setup_stack_log()
- stack_main.add_titled(stack_child, "log", "Log Viewer")
-
- ### Settings stack
- stack_child = self.setup_stack_settings()
- stack_main.add_titled(stack_child, "settings", "Basico Settings")
-
- ## Annotations
- boxannotations = self.gui.add_widget('gtk_vbox_container_annotations', Gtk.VBox())
-
- stack_annot = self.gui.add_widget('gtk_stack_annotation', Gtk.Stack())
- stack_annot.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
- stack_annot.set_transition_duration(2500)
-
- stack_child = self.setup_stack_annotation()
- stack_annot.add_titled(stack_child, "comment", "New comment")
- stack_annot.child_set_property (stack_child, "icon-name", "basico-comments")
-
- # ~ stack_child = self.setup_stack_annotation_link()
- # ~ stack_annot.add_titled(stack_child, "link", "New link")
- # ~ stack_annot.child_set_property (stack_child, "icon-name", "basico-browse")
-
- boxannotations.add(stack_annot)
- boxannotations.set_no_show_all(True)
- boxannotations.hide()
-
-
- box.pack_start(boxannotations, True, True, 6)
- paned.add2(box)
- mainbox.pack_start(paned, True, True, 0)
-
- # Statusbar
- statusbar = self.gui.add_widget('widget_statusbar', Statusbar(self.controller))
- mainbox.pack_start(statusbar, False, False, 0)
-
- # Menu Views
- vbox = Gtk.VBox()
- viewsbox = self.gui.get_widget('gtk_box_container_views')
- viewmenu = self.gui.add_widget('viewmenu', SAPNoteViewMenu(self.controller))
- viewmenu.set_hexpand(True)
- viewmenu.set_vexpand(True)
- vbox.pack_start(viewmenu, True, True, 0)
- self.gui.swap_widget(viewsbox, vbox)
-
- self.add(mainbox)
- self.show_all()
-
-
- def setup_stack_visor(self):
- box = Gtk.VBox()
- box.set_hexpand(True)
-
- ### Toolbar
- boxtoolbar = self.gui.add_widget('gtk_hbox_container_toolbar', Gtk.HBox())
- box.pack_start(boxtoolbar, False, False, 0)
- visortoolbar = self.gui.add_widget('visortoolbar', SAPNoteViewVisorToolbar(self.controller))
- self.gui.swap_widget(boxtoolbar, visortoolbar)
-
- ### Visor
- scr = Gtk.ScrolledWindow()
- scr.set_hexpand(True)
- scr.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
- vwp = Gtk.Viewport()
- vwp.set_hexpand(True)
- visor = self.gui.add_widget('visor', SAPNoteViewVisor(self.controller))
- visor.set_hexpand(True)
- visor.set_vexpand(True)
- vwp.add(visor)
- scr.add(vwp)
- box.pack_start(scr, True, True, 0)
- visor.show_all()
- box.show_all()
- return box
-
-
- def setup_stack_about(self):
- box = Gtk.VBox()
- box.set_hexpand(True)
- about = self.gui.add_widget('widget_about', About())
- box.pack_start(about, True, True, 0)
- box.show_all()
- return box
-
-
- def setup_stack_settings(self):
- box = Gtk.VBox()
- box.set_hexpand(True)
- settings = self.gui.add_widget('widget_settings', SettingsView(self.controller))
- box.pack_start(settings, True, True, 0)
- box.show_all()
- return box
-
- def setup_stack_log(self):
- box = Gtk.VBox()
- box.set_hexpand(True)
- logviewer = self.gui.add_widget('widget_logviewer', LogViewer(self.controller))
- box.pack_start(logviewer, True, True, 0)
- box.show_all()
- return box
-
-
- def setup_stack_annotation(self):
- return self.gui.add_widget('widget_annotation', AnnotationWidget(self.controller))
-
-
- def run(self):
- viewmenu = self.gui.get_widget('viewmenu')
- viewmenu.set_view('annotation')
- self.cb.gui_show_dashboard()
- # ~ self.cb.gui_show_about()
Index: branches/BR-0.3/basico/database.py
===================================================================
--- branches/BR-0.3/basico/database.py (revision 232)
+++ branches/BR-0.3/basico/database.py (nonexistent)
@@ -1,342 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: database.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: Database service
-
-import os
-from os.path import basename
-import json
-import glob
-from cgi import escape
-
-from .env import LPATH
-from .service import Service
-
-SEP = os.path.sep
-
-class Database(Service):
- def initialize(self):
- '''
- Setup AppLogic Service
- '''
- self.sapnotes = {}
- self.stats = {}
- self.stats['maincomp'] = {}
- self.stats['cats'] = {}
- self.stats['component'] = set()
- self.stats['category'] = set()
- self.stats['priority'] = set()
- self.stats['type'] = set()
- self.stats['version'] = set()
- #FIXME self.stats['releaseon'] = set()
-
-
- def store(self, sapnote, html):
- CACHE_DIR = self.get_var('CACHE', 'local')
- FSAPNOTE = CACHE_DIR + sapnote + '.xml'
-
- try:
- f = open(FSAPNOTE, 'w')
- f.write(html)
- f.close()
- self.debug("\tSAP Note %s stored in %s" % (sapnote, FSAPNOTE))
- except Exception as error:
- self.log.error(error)
-
-
- def get_sapnote_content(self, sid):
- CACHE_DIR = self.get_var('CACHE', 'local')
- FSAPNOTE = CACHE_DIR + sid + '.xml'
- content = open(FSAPNOTE, 'r').read()
- return content
-
-
- def is_saved(self, sid):
- metadata = self.get_sapnote_metadata(sid)
- if metadata is None:
- return False
- else:
- return True
-
-
- def is_valid(self, sid):
- valid = True
- for ch in sid:
- if not ch.isdigit():
- return False
- return valid
-
-
- def is_stored(self, sid):
- CACHE_DIR = self.get_var('CACHE', 'local')
- fsapnote = CACHE_DIR + sid + '.xml'
- stored = os.path.exists(fsapnote)
-
- return stored
-
-
- def build_stats(self, bag=None):
- if bag is None:
- bag = self.sapnotes
- self.dstats = {}
- self.compstats = {}
- allcollections = set()
-
- for sid in bag:
- try:
- collections = self.sapnotes[sid]['collections']
- for collection in collections:
- allcollections.add(collection)
- except: pass
-
- #COMMENT Components
- compkey = self.sapnotes[sid]['componentkey']
- comptxt = self.sapnotes[sid]['componenttxt']
- component = escape("%s (%s)" % (compkey, comptxt))
- sep = compkey.find('-')
- if sep > 0:
- maincomp = compkey[0:sep]
- else:
- maincomp = compkey
-
- #COMMENT Categories
- category = escape(self.sapnotes[sid]['category'])
- try:
- cont = self.stats['cats'][category]
- self.stats['cats'][category] = cont + 1
- except:
- self.stats['cats'][category] = 1
-
- #COMMENT Build all (sub)keys from given component key
- #COMMENT useful for display stats with pygal
- compkeys = compkey.split('-')
- total = len(compkeys)
- key = ''
- i = 0
- for subkey in compkeys:
- key = key + '-' + subkey
- if key[:1] == '-':
- key = key[1:]
-
- # update stats
- try:
- count = self.compstats[key]
- self.compstats[key] = count + 1
- except Exception as error:
- self.compstats[key] = 1
-
- try:
- cont = self.stats['maincomp'][maincomp]
- self.stats['maincomp'][maincomp] = cont + 1
- except:
- self.stats['maincomp'][maincomp] = 1
-
- category = escape(self.sapnotes[sid]['category'])
- priority = escape(self.sapnotes[sid]['priority'])
- ntype = escape(self.sapnotes[sid]['type'])
- version = escape(self.sapnotes[sid]['version'])
- releaseon = escape(self.sapnotes[sid]['releaseon'])
- self.stats['component'].add(component)
- self.stats['category'].add(category)
- self.stats['priority'].add(priority)
- self.stats['type'].add(ntype)
- self.stats['version'].add(version)
- #FIXME self.stats['releaseon'].add(releaseon)
- #FIXME self.stats[''].add(version)
-
-
- def add(self, sapnote):
- sid = sapnote['id']
- self.sapnotes[sid] = sapnote
-
-
- def add_list(self, sapnotes):
- for sapnote in sapnotes:
- self.add(sapnote)
-
-
- def get_notes(self):
- '''
- Return all sapnotes
- '''
- return self.sapnotes
-
-
- def get_notes_by_node(self, key, value):
- '''
- Return a set of sapnotes which matches with an specific key/value
- '''
- bag = set()
- sapnotes = self.get_notes()
- for sapnote in sapnotes:
- if key.startswith('component') or \
- key in ['category', 'priority', 'type']:
- if sapnotes[sapnote][key].startswith(value):
- bag.add(sapnote)
- elif key == 'collection':
- if len(value) == 0:
- try:
- collections = sapnotes[sapnote]['collections']
- if len(collections) > 0:
- bag.add(sapnote)
- except:
- pass
- else:
- try:
- if value in sapnotes[sapnote]['collections']:
- bag.add(sapnote)
- except:
- pass
- elif key in ['date-year', 'date-month', 'date-day']:
- if len(value) == 6:
- value = value[0:4] + '-' + value[4:6]
- elif len(value) == 8:
- value = value[0:4] + '-' + value[4:6] + '-' + value[6:8]
-
- if sapnotes[sapnote]['feedupdate'].startswith(value):
- bag.add(sapnote)
- else:
- pass
-
- return bag
-
-
- def get_total(self):
- '''
- Return total sapnotes
- '''
- return len(self.sapnotes)
-
-
- def load_notes(self):
- '''
- If notes.json exists, load notes
- '''
- try:
- fnotes = self.get_file('SAP')
- with open(fnotes, 'r') as fp:
- self.sapnotes = json.load(fp)
- self.debug ("Loaded %d notes" % len(self.sapnotes))
- except Exception as error:
- self.debug("SAP Notes database not found. Creating a new one")
- self.save_notes()
-
-
- def get_sapnote_metadata(self, sid):
- sid = self.normalize_sid(sid)
-
- try:
- return self.sapnotes[sid]
- except KeyError as error:
- self.debug("SAP Note %s doesn't exist in the database" % sid)
- return None
-
-
- def get_title(self, sid):
- title = ''
- metadata = self.get_sapnote_metadata(sid)
- if metadata is not None:
- title = metadata['title']
-
- return title
-
-
- def save_notes(self, bag={}, export_path=None):
- '''
- Save SAP Notes to json database file
- '''
- if export_path is None:
- export_path = self.get_file('SAP')
-
- if len(bag) == 0:
- bag = self.get_notes()
-
- fdb = open(export_path, 'w')
- json.dump(bag, fdb)
- fdb.close()
- self.debug ("Saved %d notes to %s" % (len(bag), export_path))
-
-
- def set_bookmark(self, lsid):
- for sid in lsid:
- self.sapnotes[sid]['bookmark'] = True
- self.debug("SAP Note %s bookmarked: True" % sid)
- self.save_notes()
-
-
- def set_no_bookmark(self, lsid):
- for sid in lsid:
- self.sapnotes[sid]['bookmark'] = False
- self.debug("SAP Note %s bookmarked: False" % sid)
- self.save_notes()
-
-
- def is_bookmark(self, sapnote):
- try:
- return self.sapnotes[sapnote]['bookmark']
- except:
- return False
-
- def get_collections(self, sid):
- try:
- return self.sapnotes[sid]['collections']
- except:
- return []
-
-
- def search(self, term):
- bag = []
-
- for sid in self.get_notes():
- values = []
- sapnote = self.sapnotes[sid]
- for key in sapnote:
- # ~ self.debug(key)
- values.append(str(sapnote[key]))
- text = ' '.join(values)
- if term.upper() in text.upper():
- bag.append(sapnote['id'])
- self.debug("Found term '%s' in %d SAP Notes" % (term, len(bag)))
- return bag
-
-
- def normalize_sid(self, sid):
- if isinstance(sid, int):
- sid = "0"*(10 - len(str(sid))) + str(sid)
- elif isinstance(sid, str):
- sid = "0"*(10 - len(sid)) + sid
-
- return sid
-
-
- def set_collections(self, sid, collections):
- if sid != '0000000000':
- sid = self.normalize_sid(sid)
- self.sapnotes[sid]['collections'] = collections
- self.debug("SAP Note %s in collections: %s" % (sid, collections))
- self.save_notes()
-
-
- def delete(self, sapnote):
- deleted = False
- try:
- del (self.sapnotes[sapnote])
- deleted = True
- self.debug("SAP Note %s deleted" % sapnote)
- self.save_notes()
- except:
- deleted = False
-
- return deleted
-
-
- def run(self):
- self.load_notes()
- self.build_stats()
-
-
- def end(self):
- self.save_notes()
-
Index: branches/BR-0.3/basico/service.py
===================================================================
--- branches/BR-0.3/basico/service.py (revision 232)
+++ branches/BR-0.3/basico/service.py (nonexistent)
@@ -1,118 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: service.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: Service class
-
-import sys
-import traceback as tb
-
-from .env import FILE
-
-class Service(object):
- """
- Service class is the base class for the rest of main classes used in
- the application.
- Different modules (GUI, Database, Ask, etc...) share same methods
- which is useful to start/stop them, simplify logging and, comunicate
- each other easily.
- """
-
- def __init__(self, app=None):
- """Initialize Service instance
- @type app: Basico instance
- @param app: current Basico instance reference
- """
- self.started = False
-
-
- def is_started(self):
- """Return True or False if service is running / not running
- """
- return self.started
-
-
- def print_traceback(self):
- self.debug(tb.format_exc())
-
-
- def start(self, app, logname=None):
- """Start service.
- Use initialize for writting a custom init method
- @type app: basico
- @param app: basico Class pointer.
- @type logname: string
- @param logname: name of associated logger. It is used aswell to
- identify configuration section name
- """
- self.started = True
- self.app = app
- self.log = self.app.log
- self.section = logname
- self.init_section(self.section)
-
- try:
- self.initialize()
- self.debug("Service %s loaded" % logname)
- except Exception as error:
- self.debug (self.get_traceback())
-
-
- def get_var(self, name, scope='global'):
- return self.app.get_var(name, scope)
-
-
- def get_app_info(self, name):
- return self.app.get_app_info(name)
-
-
- def get_file(self, name):
- return self.app.get_file(name)
-
-
- def end(self):
- """End service
- Use finalize for writting a custom end method
- """
- self.started = False
- try:
- self.finalize()
- except Exception as error:
- self.debug (self.get_traceback())
-
-
- def initialize(self):
- """Initialize service.
- All clases derived from Service class must implement this method
- """
- pass
-
-
- def finalize(self):
- """Finalize service.
- All clases derived from Service class must implement this method
- """
- pass
-
-
- def init_section(self, section):
- """Check if section exists in config. If not, create it"""
- self.settings = self.app.get_service('Settings')
- config = self.settings.load()
- try:
- config[section]
- except:
- config[section] = {}
- self.settings.save(config)
- self.debug("Section '%s' initialized in config file" % section)
-
- def debug(self, message):
- self.app.log.debug("%18s | %s" % (self.__class__.__name__, message))
-
- def get_traceback(self):
- return self.app.get_traceback()
-
-
- def get_service(self, name):
- return self.app.get_service(name)
/branches/BR-0.3/basico/service.py
Property changes:
Deleted: svn:executable
## -1 +0,0 ##
-*
\ No newline at end of property
Index: branches/BR-0.3/basico/sap.py
===================================================================
--- branches/BR-0.3/basico/sap.py (revision 232)
+++ branches/BR-0.3/basico/sap.py (nonexistent)
@@ -1,262 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: sap.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: SAP service
-
-import time
-import traceback
-from shutil import which
-# ~ from datetime.datetime import now
-
-from .service import Service
-from .env import LPATH
-
-# Default settings for SAP module
-LOGIN_PAGE_URL = "https://accounts.sap.com"
-LOGOUT_PAGE_URL = "https://accounts.sap.com/ui/logout"
-ODATA_NOTE_URL = "https://launchpad.support.sap.com/services/odata/svt/snogwscorr/TrunkSet(SapNotesNumber='%s',Version='0',Language='E')"
-ODATA_NOTE_URL_LONGTEXT = "https://launchpad.support.sap.com/services/odata/svt/snogwscorr/TrunkSet(SapNotesNumber='%s',Version='0',Language='E')?$expand=LongText"
-SAP_NOTE_URL = "https://launchpad.support.sap.com/#/notes/%s"
-SAP_NOTE_URL_PDF = "https://launchpad.support.sap.com/services/pdf/notes/%s/E"
-TIMEOUT = 10
-
-DSTART = None
-DEND = None
-DYNTMOUT = 0
-AVERAGE = 0
-
-"""
-<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/RefBy"
-<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/RefTo"
-<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/CorrIns"
-<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/Patch"
-<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/Sp"
-<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/SoftCom"
-<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/Attach"
-<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/LongText"
-<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/Languages"
-<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/SideCau"
-<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/SideSol"
-
-"""
-
-
-class SAP(Service):
- def initialize(self):
- '''
- Setup AppLogic Service
- '''
- self.settings = self.get_service('Settings')
- self.__init_config_section()
-
-
- def __init_config_section(self):
- settings = self.settings.load()
- settings[self.section]
- try:
- settings[self.section]['LOGIN_PAGE_URL']
- except:
- settings[self.section]['LOGIN_PAGE_URL'] = LOGIN_PAGE_URL
-
- try:
- settings[self.section]['LOGOUT_PAGE_URL']
- except:
- settings[self.section]['LOGOUT_PAGE_URL'] = LOGOUT_PAGE_URL
-
- try:
- settings[self.section]['ODATA_NOTE_URL']
- except:
- settings[self.section]['ODATA_NOTE_URL'] = ODATA_NOTE_URL
-
- try:
- settings[self.section]['ODATA_NOTE_URL_LONGTEXT']
- except:
- settings[self.section]['ODATA_NOTE_URL_LONGTEXT'] = ODATA_NOTE_URL_LONGTEXT
-
- try:
- settings[self.section]['SAP_NOTE_URL']
- except:
- settings[self.section]['SAP_NOTE_URL'] = SAP_NOTE_URL
-
- try:
- settings[self.section]['SAP_NOTE_URL_PDF']
- except:
- settings[self.section]['SAP_NOTE_URL_PDF'] = SAP_NOTE_URL_PDF
-
- try:
- settings[self.section]['TIMEOUT']
- except:
- settings[self.section]['TIMEOUT'] = TIMEOUT
-
- self.settings.save(settings)
-
-
- def analyze_sapnote_metadata(self, sid, content):
- '''
- Get metadata details from SAP Note
- '''
- try:
- utils = self.get_service('Utils')
- f = utils.feedparser_parse(content)
- sid = f.entries[0].d_sapnotesnumber
- sapnote = {}
- sapnote['id'] = sid
- sapnote['componentkey'] = f.entries[0].d_componentkey
- comptxt = f.entries[0].d_componenttext
- if comptxt == "Please use note 1433157 for finding the right component":
- comptxt = ""
- sapnote['componenttxt'] = comptxt
- sapnote['category'] = f.entries[0].d_category_detail['value']
- sapnote['language'] = f.entries[0].d_languagetext_detail['value']
- sapnote['title'] = f.entries[0].d_title_detail['value']
- sapnote['priority'] = f.entries[0].d_priority_detail['value']
- sapnote['releaseon'] = f.entries[0].d_releasedon
- sapnote['type'] = f.entries[0].d_type_detail['value']
- sapnote['version'] = f.entries[0].d_version_detail['value']
- sapnote['feedupdate'] = f.entries[0].updated
- sapnote['bookmark'] = False
- self.debug ("\tSAP Note %s analyzed successfully" % sid)
- except Exception as error:
- sapnote = {}
- self.log.error("\tError while analyzing data for SAP Note %s" % sid)
-
- return sapnote
-
-
- def analyze_sapnote_metadata_details(self, sid, content):
- '''
- Get metadata details from SAP Note
- '''
- try:
- utils = self.get_service('Utils')
- f = utils.feedparser_parse(content)
- sid = f.entries[0].d_sapnotesnumber
- sapnote = {}
- sapnote['id'] = sid
- sapnote['componentkey'] = f.entries[0].d_componentkey
- comptxt = f.entries[0].d_componenttext
- if comptxt == "Please use note 1433157 for finding the right component":
- comptxt = ""
- sapnote['componenttxt'] = comptxt
- sapnote['category'] = f.entries[0].d_category_detail['value']
- sapnote['language'] = f.entries[0].d_languagetext_detail['value']
- sapnote['title'] = f.entries[0].d_title_detail['value']
- sapnote['priority'] = f.entries[0].d_priority_detail['value']
- sapnote['releaseon'] = f.entries[0].d_releasedon
- sapnote['type'] = f.entries[0].d_type_detail['value']
- sapnote['version'] = f.entries[0].d_version_detail['value']
- sapnote['feedupdate'] = f.entries[0].updated
- sapnote['bookmark'] = False
- self.debug ("\tSAP Note %s analyzed successfully" % sid)
- except Exception as error:
- sapnote = {}
- self.log.error("\tError while analyzing data for SAP Note %s" % sid)
-
- return sapnote
-
- def fetch(self, driver, sid):
- db = self.get_service('DB')
- valid = False
-
- if not db.is_stored(sid):
- self.debug("%3d/%3d - SAP Note %s must be downloaded" % (self.notes_fetched+1, self.notes_total, sid))
- # ~ DSTART = datetime.now()
- content = self.download(driver, sid)
- if len(content) > 0:
- self.debug("%3d/%3d - SAP Note %s fetched" % (self.notes_fetched+1, self.notes_total, sid))
- else:
- self.debug("%3d/%3d - SAP Note %s not feched" % (self.notes_fetched+1, self.notes_total, sid))
- else:
- self.debug("%3d/%3d - SAP Note %s will be analyzed again" % (self.notes_fetched+1, self.notes_total, sid))
- content = db.get_sapnote_content(sid)
-
- self.fetched()
-
- sapnote = self.analyze_sapnote_metadata_details(sid, content)
- if len(sapnote) > 0:
- db = self.get_service('DB')
- db.add(sapnote)
- db.store(sid, content)
- valid = True
- # ~ DEND = datetime.now()
- # ~ DYNTMOUT = DEND - DSTART
- # ~ self.debug(DYNTMOUT.seconds)
- return valid, sid
-
-
- def start_fetching(self, total):
- self.notes_fetched = 0
- self.notes_total = total
-
-
- def fetched(self):
- self.notes_fetched += 1
-
-
- def stop_fetching(self):
- self.notes_fetched = 0
- self.notes_total = 0
-
- def browse(self, sid):
- webdriver = self.get_service('Driver')
- driver = webdriver.open()
- url = SAP_NOTE_URL % sid
- webdriver.load(driver, url)
-
-
- def download(self, driver, sapnote=None):
- try:
- webdriver = self.get_service('Driver')
- ODATA_NOTE_URL = self.settings.get('SAP', 'ODATA_NOTE_URL')
- timeout = self.settings.get('SAP', 'TIMEOUT')
- self.debug("Downloading SAP Note %s" % sapnote)
- browser = webdriver.load(driver, ODATA_NOTE_URL % sapnote)
- time.sleep(timeout)
- content = browser.page_source
- fsn = LPATH['CACHE'] + sapnote + '.xml'
- with open(fsn, 'w') as fxml:
- fxml.write(content)
- except Exception as error:
- self.log.error(error)
- content = ''
-
- return content
-
-
- def set_bookmark(self, bag):
- db = self.get_service('DB')
- sapnotes = db.get_notes()
- mylist = []
- for tid in bag:
- sid = "0"*(10 - len(tid)) + tid
- sapnotes[sid]['bookmark'] = True
- mylist.append(sapnotes[sid])
- self.debug("SAP Note %s bookmarked" % sid)
- db.add_list(mylist)
-
-
- def set_no_bookmark(self, bag):
- db = self.get_service('DB')
- sapnotes = db.get_notes()
- mylist = []
- for tid in bag:
- sid = "0"*(10 - len(tid)) + tid
- sapnotes[sid]['bookmark'] = False
- mylist.append(sapnotes[sid])
- self.debug("SAP Note %s unbookmarked" % sid)
- db.add_list(mylist)
-
-
- def is_bookmark(self, sapnote):
- try:
- return self.sapnotes[sapnote]['bookmark']
- except:
- return False
-
-
- def run(self):
- db = self.get_service('DB')
- db.load_notes()
- db.build_stats()
Index: branches/BR-0.3/basico/utils.py
===================================================================
--- branches/BR-0.3/basico/utils.py (revision 232)
+++ branches/BR-0.3/basico/utils.py (nonexistent)
@@ -1,198 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: utils.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: Generic functions service
-
-import os
-import sys
-import subprocess
-import tarfile
-import zipfile
-import shutil
-import urllib.request
-import requests
-import webbrowser
-import feedparser
-from datetime import datetime
-
-from .service import Service
-
-class Utils(Service):
- def initialize(self):
- self.uas = []
-
- def timestamp(self):
- now = datetime.now()
- timestamp = "%4d%02d%02d_%02d%02d%02d" % (now.year, now.month, now.day, now.hour, now.minute, now.second)
-
- return timestamp
-
-
- def get_datetime(self, timestamp):
- adate = datetime.strptime(timestamp, "%Y%m%d_%H%M%S")
- return adate
-
-
- def fuzzy_date_from_timestamp(self, timestamp):
- # ~ date = self.get_datetime(timestamp)
- d1 = self.get_datetime(timestamp)
- d2 = datetime.now()
- rdate = d2 - d1 # DateTimeDelta
- if int(rdate.days) > 0:
- if (rdate.days >= 365):
- return "%d years ago" % int((rdate.days/365))
- else:
- return "%d days ago" % int(rdate.days)
-
- hours = rdate.seconds / 3600
- if int(hours) > 0:
- return "%d hours ago" % int(hours)
-
- minutes = rdate.seconds / 60
- if int(minutes) > 0:
- return "%d minutes ago" % int(minutes)
-
- if int(rdate.seconds) > 0:
- return "%d seconds ago" % int(rdate.seconds)
-
-
- def browse(self, url):
- if sys.platform in ['linux', 'linux2']:
- browser = webbrowser.get('firefox')
- elif sys.platform == 'win32':
- browser = webbrowser.get('windows-default')
-
- browser.open_new_tab(url)
-
-
- def which(self, program):
- if sys.platform == 'win32':
- program = program + '.exe'
-
- def is_exe(fpath):
- return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
-
- fpath, fname = os.path.split(program)
- if fpath:
- if is_exe(program):
- return program
- else:
- for path in os.environ["PATH"].split(os.pathsep):
- path = path.strip('"')
- exe_file = os.path.join(path, program)
- if is_exe(exe_file):
- return exe_file
-
- return None
-
-
- def install_geckodriver(self):
- """Get last version of Gecko webdriver from github"""
- self.debug("OS Platform: %s" % sys.platform)
- if sys.platform in ['linux', 'linux2']:
- GECKO_SOURCE = "https://github.com/mozilla/geckodriver/releases/download/v0.23.0/geckodriver-v0.23.0-linux64.tar.gz"
- GECKO_TARGET = self.get_var('TMP', scope='local') + 'gecko.tar.gz'
- elif sys.platform == 'win32':
- GECKO_SOURCE = "https://github.com/mozilla/geckodriver/releases/download/v0.23.0/geckodriver-v0.23.0-win64.zip"
- GECKO_TARGET = self.get_var('TMP', scope='local') + 'gecko.zip'
-
- GECKO_INSTALL_DIR = self.get_var('DRIVERS', 'local')
-
-
- if os.path.exists(GECKO_TARGET):
- self.debug("Gecko webdriver already downloaded")
- downloaded = True
- else:
- downloaded = self.download('Gecko', GECKO_SOURCE, GECKO_TARGET)
-
- if downloaded:
- if sys.platform in ['linux', 'linux2']:
- extracted = self.extract(GECKO_TARGET, GECKO_INSTALL_DIR, 'tar.gz')
- elif sys.platform == 'win32':
- extracted = self.extract(GECKO_TARGET, GECKO_INSTALL_DIR, 'zip')
- if extracted:
- self.debug("Gecko webdriver deployed successfully")
- else:
- self.log.error("Gecko could not be deployed")
- self.log.error("Tip: maybe %s is corrupt. Delete it" % GECKO_TARGET)
- #FIXME: stop application gracefully
- exit(-1)
-
-
- def download(self, prgname, source, target):
- try:
- self.debug ("Downloading %s from: %s" % (prgname, source))
- response = requests.get(source, stream=True)
- with open(target, 'wb') as out_file:
- shutil.copyfileobj(response.raw, out_file)
- del response
- self.debug ("%s downloaded to %s" % (prgname, target))
- return True
- except Exception as error:
- self.log.error(error)
- return False
-
-
- def extract(self, filename, target_path, protocol):
- self.debug("Extracting %s to %s using protocol %s" % (filename, target_path, protocol))
- if protocol in ['tar.gz', 'bz2']:
- try:
- tar = tarfile.open(filename, "r:*")
- tar.extractall(target_path)
- tar.close()
- self.debug("Extracted successfully")
- return True
- except Exception as error:
- self.log.error(error)
- return False
- elif protocol == 'zip':
- try:
- self.unzip(filename, target_path)
- self.debug("Extracted successfully")
- return True
- except Exception as error:
- self.log.error(error)
- return False
-
- def zip(self, filename, directory):
- # http://stackoverflow.com/a/25650295
- #~ make_archive(archive_name, 'gztar', root_dir)
- res = shutil.make_archive(filename, 'gztar', directory)
- self.debug("%s - %s" % (filename, directory))
- self.debug("zip res: %s" % res)
-
-
- def unzip(self, target, install_dir):
- zip_archive = zipfile.ZipFile(target, "r")
- zip_archive.extractall(path=install_dir)
- zip_archive.close()
-
-
- def get_firefox_profile_dir(self):
- if sys.platform in ['linux', 'linux2']:
- cmd = "ls -d /home/$USER/.mozilla/firefox/*.default/"
- p = subprocess.Popen([cmd], shell=True, stdout=subprocess.PIPE)
- FF_PRF_DIR = p.communicate()[0][0:-2]
- FF_PRF_DIR_DEFAULT = str(FF_PRF_DIR,'utf-8')
- elif sys.platform == 'win32':
- import glob
- APPDATA = os.getenv('APPDATA')
- FF_PRF_DIR = "%s\\Mozilla\\Firefox\\Profiles\\" % APPDATA
- PATTERN = FF_PRF_DIR + "*default*"
- FF_PRF_DIR_DEFAULT = glob.glob(PATTERN)[0]
-
- return FF_PRF_DIR_DEFAULT
-
-
- def feedparser_parse(self, thing):
- try:
- return feedparser.parse(thing)
- except TypeError:
- if 'drv_libxml2' in feedparser.PREFERRED_XML_PARSERS:
- feedparser.PREFERRED_XML_PARSERS.remove('drv_libxml2')
- return feedparser.parse(thing)
- else:
- self.log.error(self.get_traceback())
- return None
Index: branches/BR-0.3/basico/callbacks.py
===================================================================
--- branches/BR-0.3/basico/callbacks.py (revision 232)
+++ branches/BR-0.3/basico/callbacks.py (nonexistent)
@@ -1,428 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: callbacks.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: UI and related callbacks service
-
-import os
-import json
-import time
-
-import gi
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk
-
-
-from concurrent.futures import ThreadPoolExecutor as Executor
-
-from .service import Service
-from .sapnoteviewvisor import SAPNoteViewVisor, SAPNoteViewVisorToolbar
-from .widgets.collections import CollectionsMgtView
-from .widgets.annotation import AnnotationWidget
-from .widgets.settings import SettingsView
-from .env import FILE, LPATH
-
-
-# PROPKEYS = CSV headers. SAP Note metadata
-PROPKEYS = ['id', 'title', 'type', 'componentkey',
- 'componenttxt', 'category', 'priority', 'releaseon',
- 'language', 'version']
-
-# Extend PROPKEYS with custom basico metadata
-PROPKEYS.extend (['Bookmark'])
-
-class Callback(Service):
- def initialize(self):
- self.get_services()
-
- def get_services(self):
- self.settings = self.app.get_service('Settings')
- self.db = self.app.get_service('DB')
- self.gui = self.app.get_service('GUI')
- self.uif = self.app.get_service("UIF")
- self.sap = self.app.get_service('SAP')
- self.alert = self.app.get_service('Notify')
- self.im = self.app.get_service('IM')
- self.utils = self.app.get_service('Utils')
- self.annot = self.app.get_service('Annotation')
-
-
- def action_search(self, entry):
- visor = self.gui.get_widget('visor')
-
- term = entry.get_text()
- bag = self.db.search(term)
- visor.populate(bag)
-
-
- def sapnote_browse(self, button, sid):
- self.debug("Browsing SAP Note %d" % int(sid))
- SAP_NOTE_URL = self.settings.get('SAP', 'SAP_NOTE_URL')
- url = SAP_NOTE_URL % sid
- self.utils.browse(url)
- # ~ popover = self.gui.get_widget('poprowsid')
- # ~ popover.hide()
-
-
- def sapnote_delete(self, button, lsid):
- self.debug("Deleting SAP Notes %s" % lsid)
- if lsid == '0000000000':
- visor = self.gui.get_widget('visor')
- bag = visor.get_bag()
- else:
- bag = [lsid]
- answer = self.uif.warning_message(button, 'Deleting SAP Notes', 'Are you sure?', bag)
- if answer is True:
- for sid in bag:
- self.db.delete(sid)
- viewmenu = self.gui.get_widget('viewmenu')
- viewmenu.refresh()
- else:
- self.debug("Nothing deleted")
-
-
- def gui_hide_popover(self, popover):
- popover.hide()
-
-
- def gui_show_about(self, *args):
- stack = self.gui.get_widget('gtk_stack_main')
- stack.set_visible_child_name('about')
- self.gui_toggle_menu_view(False)
- self.gui_hide_popover(self.gui.get_widget('gtk_popover_button_menu_system'))
- button = self.gui.get_widget('gtk_button_total_notes')
- button.set_no_show_all(True)
- button.hide()
- self.uif.set_widget_visibility('gtk_button_dashboard', True)
-
-
- def gui_show_log(self, *args):
- logviewer = self.gui.get_widget('widget_logviewer')
- stack = self.gui.get_widget('gtk_stack_main')
- button_total_notes = self.gui.get_widget('gtk_button_total_notes')
-
-
- logviewer.update()
- self.gui_hide_popover(self.gui.get_widget('gtk_popover_button_menu_system'))
- stack.set_visible_child_name('log')
- self.uif.set_widget_visibility('gtk_button_dashboard', True)
- # ~ self.uif.set_widget_visibility(button_total_notes, False)
-
-
- def gui_show_settings(self, button):
- stack = self.gui.get_widget('gtk_stack_main')
- stack.set_visible_child_name('settings')
- view_settings = self.gui.get_widget('widget_settings')
- view_settings.update()
- self.gui_hide_popover(self.gui.get_widget('gtk_popover_button_menu_system'))
- button = self.gui.get_widget('gtk_button_total_notes')
- button.set_no_show_all(True)
- button.hide()
- self.uif.set_widget_visibility('gtk_button_dashboard', True)
-
-
- def gui_show_dashboard(self, *args):
- stack = self.gui.get_widget('gtk_stack_main')
- stack.set_visible_child_name('visor')
- self.gui_hide_popover(self.gui.get_widget('gtk_popover_button_menu_system'))
- self.uif.set_widget_visibility('gtk_button_dashboard', False)
- # ~ self.uif.set_widget_visibility(button_home, False)
- # ~ button = self.gui.get_widget('gtk_button_total_notes')
- # ~ button.set_no_show_all(True)
- # ~ button.hide()
-
-
- def gui_annotation_widget_show(self, widget, sid='0000000000', action='create'):
- widget_annotation = self.gui.get_widget('widget_annotation')
- widget = self.gui.get_widget('gtk_label_aid')
-
-
- if action == 'create':
- self.gui_annotation_widget_clear()
- aid = self.annot.gen_aid(sid)
- elif action == 'edit':
- aid = sid
- widget_annotation.set_metadata(aid, action)
- self.uif.set_widget_visibility('gtk_vbox_container_annotations', True)
- widget.grab_focus()
-
-
- def gui_show_popover(self, button, popover):
- if popover.get_visible():
- popover.hide()
- else:
- popover.show_all()
-
-
- def switch_bookmark_current_set(self, button, popover):
- visor = self.gui.get_widget('visor')
- bag = visor.get_bag()
- try:
- for sid in bag:
- metadata = self.db.get_sapnote_metadata(sid)
- bookmark = metadata['bookmark']
- if bookmark:
- self.sapnote_unbookmark([sid])
- else:
- self.sapnote_bookmark([sid])
- popover.hide()
- except:
- self.debug("Could not bookmark SAP Note %s" % sid)
- visor.populate()
-
-
- def switch_bookmark(self, button, lsid, popover):
- visor = self.gui.get_widget('visor')
- try:
- for sid in lsid:
- metadata = self.db.get_sapnote_metadata(sid)
- bookmark = metadata['bookmark']
- if bookmark:
- self.sapnote_unbookmark([sid])
- else:
- self.sapnote_bookmark([sid])
- popover.hide()
- except:
- self.debug("Could not bookmark SAP Note %s" % sid)
- visor.populate()
-
- def sapnote_bookmark(self, lsid):
- self.db.set_bookmark(lsid)
-
-
- def sapnote_unbookmark(self, lsid):
- self.db.set_no_bookmark(lsid)
-
-
- def sapnote_import_from_launchpad(self, *args):
- db = self.get_service('DB')
- webdriver = self.get_service('Driver')
- textview = self.gui.get_widget('gtk_textview_download_launchpad')
- visor = self.gui.get_widget('visor')
-
- bag = []
- all_notes = []
- sapnotes = []
-
- dlbuffer = textview.get_buffer()
- istart, iend = dlbuffer.get_bounds()
- text = dlbuffer.get_text(istart, iend, False)
- lines = text.replace(' ', ',')
- lines = lines.replace('\n', ',')
- sapnotes.extend(lines.split(','))
- for sapnote in sapnotes:
- sid = sapnote.strip()
- is_valid = self.db.is_valid(sid)
- is_saved = self.db.get_sapnote_metadata(sid)
- if is_valid and not is_saved:
- bag.append(sid)
- if is_valid:
- all_notes.append(sid)
- lbag = list(bag)
- lbag.sort()
-
- if len(bag)> 0:
- driver = webdriver.open()
-
- winroot = self.gui.get_widget('gtk_app_window_main')
- self.debug("%d SAP Notes to be downloaded: %s" % (len(bag), ', '.join(list(bag))))
-
- result = {}
-
- self.sap.start_fetching(len(bag))
- dlbag = []
-
- # FIXME: max_workers = 1 = Threads disabled
- # Indeed, I think this is the best option right now.
- with Executor(max_workers=1) as exe:
- jobs = []
- for sapnote in lbag:
- job = exe.submit(self.sap.fetch, driver, sapnote)
- jobs.append(job)
-
- for job in jobs:
- rc, sapnote = job.result()
- self.debug("\tRC SAP Note %s: %s" % (sapnote, rc))
- result[sapnote] = rc
- if rc:
- sid = "0"*(10 - len(sapnote)) + sapnote
- dlbag.append(sid)
- time.sleep(0.2)
-
- dlbuffer.set_text('')
- popover = self.gui.get_widget('gtk_popover_toolbutton_import')
- self.gui_hide_popover(popover)
- if len(bag) > 0:
- webdriver.close(driver)
-
- self.sap.stop_fetching()
- db.save_notes()
- db.build_stats()
- self.debug("Collection completed.")
- # ~ self.gui_display_visor
- viewmenu = self.gui.get_widget('viewmenu')
- viewmenu.row_changed(None)
- visor.populate(all_notes)
- return result
-
-
- def expand_menuview(self):
- viewmenu = self.gui.get_widget('viewmenu')
- viewmenu.expand_all()
-
-
- def gui_viewmenu_filter(self, *args):
- entry = self.gui.get_widget('gtk_entry_filter_view')
- filter = entry.get_text()
- viewmenu = self.gui.get_widget('viewmenu')
- selection = viewmenu.get_selection()
-
- def gui_iterate_over_data(model, path, itr):
- rowkey = model.get(itr, 0)[0]
- rowtype, rowval = rowkey.split('@')
- dsc = model.get(itr, 3)[0]
- contents = model.get(itr, 3)[0]
- cleanstr = contents.replace('<b>', '')
- cleanstr = cleanstr.replace('</b>', '')
- model.set(itr, 3, '%s' % cleanstr)
- viewmenu.collapse_row(path)
-
- if len(filter) > 0:
- if filter.upper() in rowval.upper() or filter.upper() in dsc.upper():
- viewmenu.expand_to_path (path)
- selection.select_path(path)
- model.set(itr, 3, '<b>%s</b>' % contents)
- else:
- return
-
- model = viewmenu.get_model()
- model.foreach(gui_iterate_over_data)
-
-
- def gui_filter_visor(self, entry):
- visor = self.gui.get_widget('visor')
- visible_filter = visor.get_visible_filter()
- visible_filter.refilter()
- visor.update_total_sapnotes_count(len(visible_filter))
-
-
- def gui_refresh_view(self, button, view=None):
- self.debug("Refresh & clear view (II). View(%s)" % (view))
- window = self.gui.get_widget('gtk_app_window_main')
- viewmenu = self.gui.get_widget('viewmenu')
- if view is None:
- view = viewmenu.get_view()
-
- if view is not None:
- viewlabel = self.gui.get_widget('gtk_label_current_view')
- name = "<b>%-10s</b>" % view.capitalize()
- viewlabel.set_markup(name)
- viewmenu.set_view(view)
- popover = self.gui.get_widget('gtk_popover_button_menu_views')
- popover.hide()
-
-
- def gui_toggle_menu_view(self, obj):
- paned = self.gui.get_widget('gtk_vbox_container_menu_view')
- button = self.gui.get_widget('gtk_toogletoolbutton_menu_view')
-
- if isinstance(obj, Gtk.ToggleToolButton):
- if button.get_active():
- paned.show_all()
- else:
- paned.hide()
- elif isinstance(obj, bool):
- if obj == True:
- # ~ paned.show_all()
- button.set_active(True)
- else:
- # ~ paned.hide()
- button.set_active(False)
-
-
- def gui_toggle_fullscreen(self, button):
- icon_container = self.gui.get_widget('gtk_box_container_icon_fullscreen')
- icon_fullscreen = self.im.get_new_image_icon('basico-fullscreen', 24, 24)
- icon_unfullscreen = self.im.get_new_image_icon('basico-unfullscreen', 24, 24)
- active = button.get_active()
- ui = self.gui.get_window()
- window = ui.get_window()
- if active:
- self.gui.swap_widget(icon_container, icon_unfullscreen)
- window.fullscreen()
- else:
- self.gui.swap_widget(icon_container, icon_fullscreen)
- window.unfullscreen()
-
-
- def action_annotation_edit(self, aid):
- self.gui_annotation_widget_show(None, aid, 'edit')
-
-
- def action_annotation_duplicate(self, *args):
- self.debug("ACTION-DUPLICATE: %s" % args)
-
-
- def action_annotation_delete(self, *args):
- statusbar = self.gui.get_widget('widget_statusbar')
- visor = self.gui.get_widget('visor')
- widget_annotation = self.gui.get_widget('widget_annotation')
- aid = widget_annotation.get_aid()
- self.annot.delete(aid)
- self.gui_annotation_widget_clear()
- self.uif.set_widget_visibility('gtk_vbox_container_annotations', False)
- # ~ visor.refresh()
- visor.populate_annotations()
- statusbar.message("Annotation deleted")
-
-
- def action_annotation_accept(self, button, sid):
- statusbar = self.gui.get_widget('widget_statusbar')
- widget_annotation = self.gui.get_widget('widget_annotation')
- visor = self.gui.get_widget('visor')
-
- aid = widget_annotation.get_aid()
-
- annotation = widget_annotation.get_metadata(aid)
-
- if self.annot.is_valid(aid):
- self.annot.update(annotation)
- statusbar.message('<b>Anotation edited</b>')
- self.debug("Updated annotation: %s" % aid)
- else:
- self.annot.create(annotation)
- statusbar.message('<b>New annotation added to SAP Note %d</b>' % int(sid))
- self.debug("Created annotation: %s" % aid)
-
- self.gui_annotation_widget_clear()
- visor.populate_annotations()
-
-
- def action_annotation_cancel(self, *args):
- statusbar = self.gui.get_widget('widget_statusbar')
- self.gui_annotation_widget_clear()
- self.uif.set_widget_visibility('gtk_vbox_container_annotations', False)
- statusbar.message('<b>Annotation canceled</b>')
-
-
- def action_backup(self, *args):
- TIMESTAMP = self.utils.timestamp()
- SOURCE_DIRECTORY = LPATH['DB']
- TARGET_DIRECTORY = LPATH['EXPORT']
- TARGET_FILE = TARGET_DIRECTORY + 'basico-' + TIMESTAMP
- self.utils.zip(TARGET_FILE, SOURCE_DIRECTORY)
-
-
- def gui_annotation_widget_clear(self):
- a_wdg_title = self.gui.get_widget('gtk_entry_annotation_title')
- a_wdg_type = self.gui.get_widget('gtk_combobox_annotation_type')
- a_wdg_text = self.gui.get_widget('gtk_textview_annotation_text')
- a_wdg_link = self.gui.get_widget('gtk_entry_annotation_link')
- a_wdg_link_type = self.gui.get_widget('gtk_combobox_annotation_link_type')
-
- a_wdg_title.set_text('')
- textbuffer = a_wdg_text.get_buffer()
- textbuffer.set_text('')
- a_wdg_link.set_text('')
- self.uif.set_widget_visibility('gtk_vbox_container_annotations', False)
Index: branches/BR-0.3/basico/annotations.py
===================================================================
--- branches/BR-0.3/basico/annotations.py (revision 232)
+++ branches/BR-0.3/basico/annotations.py (nonexistent)
@@ -1,132 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: annotations.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: Annotations service
-
-import os
-import json
-import uuid
-import glob
-from os.path import sep as SEP
-from .env import FILE, LPATH
-from .service import Service
-
-
-class Annotation(Service):
- def initialize(self):
- '''
- Setup Annotation Service
- '''
- self.get_services()
-
-
- def get_services(self):
- self.utils = self.app.get_service('Utils')
-
-
- def get_timestamp(self, aid):
- return aid[aid.find('@')+1:aid.rfind('-')]
-
-
- def get_aid_from_filename(self, filename):
- ANNOTATION_DIR = LPATH['ANNOTATIONS']
- filename = filename.replace(ANNOTATION_DIR, '')
- filename = filename.replace('.json', '')
- sid = filename[:filename.rfind('/')]
- fid = filename[filename.rfind('/')+1:]
- aid = "%s@%s" % (sid, fid)
- self.debug("\t%s" % aid)
- return aid
-
-
- def gen_aid(self, sid):
- '''
- Generate new annotation id
- '''
- return "%s@%s" % (sid, str(uuid.uuid4()))
-
-
- def create(self, annotation):
- ANNOTATION_FILE_METADATA = LPATH['ANNOTATIONS'] + annotation['aid'] + '.json'
- ANNOTATION_FILE_CONTENT = LPATH['ANNOTATIONS'] + annotation['aid'] + '.adoc'
- annotation['timestamp'] = self.utils.timestamp()
- # Write annotation content first and delete it
- with open(ANNOTATION_FILE_CONTENT, 'w') as fc:
- fc.write(annotation['content'])
- del annotation['content']
-
- # Write annotation metadata
- with open(ANNOTATION_FILE_METADATA, 'w') as fa:
- json.dump(annotation, fa)
-
-
- def update(self, annotation):
- ANNOTATION_FILE_METADATA = LPATH['ANNOTATIONS'] + annotation['aid'] + '.json'
- ANNOTATION_FILE_CONTENT = LPATH['ANNOTATIONS'] + annotation['aid'] + '.adoc'
-
- annotation['timestamp'] = self.utils.timestamp()
- # Write updated annotation first and delete the old one after
- with open(ANNOTATION_FILE_CONTENT, 'w') as fc:
- fc.write(annotation['content'])
- del annotation['content']
-
- # Write annotation metadata
- with open(ANNOTATION_FILE_METADATA, 'w') as fa:
- json.dump(annotation, fa)
-
-
- def delete(self, aid):
- sid = self.get_sid(aid)
- ANNOTATION_FILE_METADATA = LPATH['ANNOTATIONS'] + aid + '.json'
- ANNOTATION_FILE_CONTENT = LPATH['ANNOTATIONS'] + aid + '.adoc'
-
- if os.path.exists(ANNOTATION_FILE_METADATA):
- os.unlink(ANNOTATION_FILE_METADATA)
-
- if os.path.exists(ANNOTATION_FILE_CONTENT):
- os.unlink(ANNOTATION_FILE_CONTENT)
-
-
- def get_by_sid(self, sid):
- ANNOTATION_FILES = LPATH['ANNOTATIONS'] + '%s*.json' % sid
- annotations = glob.glob(ANNOTATION_FILES)
- annotations.sort(reverse=True)
-
- return annotations
-
-
- def get_all(self):
- return glob.glob(LPATH['ANNOTATIONS'] + '*.json')
- return annotations
-
-
- def get_sid(self, aid):
- if '@' in aid:
- return aid[:aid.find('@')]
- else:
- return aid # aid = sid
-
-
- def get_fid(self, aid):
- return aid[aid.find('@')+1:]
-
-
- def get_metadata(self, aid=None):
- if aid is not None:
- ANNOTATION_FILE_METADATA = LPATH['ANNOTATIONS'] + aid + '.json'
- with open(ANNOTATION_FILE_METADATA, 'r') as fa:
- annotation = json.load(fa)
- return annotation
- else:
- return None
-
-
- def is_valid(self, aid):
- ANNOTATION_FILE = LPATH['ANNOTATIONS'] + aid + '.json'
- valid = os.path.exists(ANNOTATION_FILE)
- if valid is False:
- self.debug("Annotation %s is not valid? Please, check files in %s." % (aid, LPATH['ANNOTATIONS']))
-
- return valid
Index: branches/BR-0.3/basico/sapnoteviewmenu.py
===================================================================
--- branches/BR-0.3/basico/sapnoteviewmenu.py (revision 232)
+++ branches/BR-0.3/basico/sapnoteviewmenu.py (nonexistent)
@@ -1,411 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: sapnoteviewmenu.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: SAPNoteViewMenu Widget
-
-# ~ from enum import IntEnum
-import traceback as tb
-from cgi import escape
-from collections import OrderedDict
-
-import gi
-gi.require_version('Gdk', '3.0')
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gdk
-from gi.repository import Gio
-from gi.repository import Gtk
-from gi.repository import Pango
-from gi.repository.GdkPixbuf import Pixbuf
-from gi.repository import Pango
-from datetime import datetime
-from dateutil import parser as dateparser
-
-from .service import Service
-from .sapnoteview import SAPNoteView
-
-
-class SAPNoteViewMenu(SAPNoteView, Service):
- view = 'component'
-
- def __init__(self, app):
- SAPNoteView.__init__(self, app)
- self.app = app
- self.row_type = None
-
-
- def prepare(self):
- self.column_rowtype.set_visible(False)
- self.column_checkbox.set_visible(False)
- self.column_icon.set_visible(False)
- self.column_component.set_visible(True)
- self.column_cat.set_visible(False)
- self.column_sntype.set_visible(False)
- self.column_sid.set_visible(False)
- self.column_priority.set_visible(True)
- self.column_lang.set_visible(False)
- self.column_rel.set_visible(False)
-
- # TreeView common
- self.set_can_focus(True)
- self.set_headers_visible(False)
- self.set_enable_search(True)
- self.set_hover_selection(False)
- self.set_grid_lines(Gtk.TreeViewGridLines.NONE)
- self.set_level_indentation(0)
- self.sig_selection_changed = self.selection.connect('changed', self.row_changed)
-
-
- def refresh(self):
- visor = self.gui.get_widget('visor')
- try:
- self.set_view(self.view)
- if self.row_type is not None:
- matches = self.db.get_notes_by_node(self.row_type, self.cid)
- visor.populate(matches)
- except Exception as error:
- self.debug(error)
- self.print_traceback()
-
-
- def row_changed(self, selection):
- if self.current_status is None:
- visor = self.gui.get_widget('visor')
- try:
- model, treeiters = selection.get_selected()
- row = model[treeiters][0]
- self.row_type, self.cid = row.split('@')
- matches = self.db.get_notes_by_node(self.row_type, self.cid)
- visor.populate(matches, self.cid)
- except AttributeError as error:
- self.debug(error)
- except Exception as error:
- self.debug(error)
- self.print_traceback()
-
-
- def set_view(self, view=None):
- statusbar = self.gui.get_widget('widget_statusbar')
- # FIXME: Get last view visited from config
- if view is None:
- view ='chronologic'
-
- iconview = self.gui.get_widget('gtk_image_current_view')
- icon = self.im.get_pixbuf_icon('basico-%s' % view, 24, 24)
- iconview.set_from_pixbuf(icon)
- iconview.show_all()
- self.view = view
- self.debug("Set current view to: %s" % view)
-
- # Change label
- viewlabel = self.gui.get_widget('gtk_label_current_view')
- name = "<b>%s</b>" % view.capitalize()
- viewlabel.set_markup(name)
- viewlabel.set_xalign(0.0)
- viewlabel.set_justify(Gtk.Justification.LEFT)
- viewlabel.show_all()
- self.populate([])
-
- statusbar.message('<b>Displaying view: %s</b>' % view.capitalize())
-
-
- def get_view(self):
- return self.view
-
-
- def populate(self, sapnotes):
- self.current_status = "working"
- self.column_component.set_title('Categories')
- self.set_headers_visible(False) # Set
- self.column_sid.set_visible(True)
- self.column_icon.set_visible(False)
- self.column_component.set_visible(True)
- self.column_component.set_expand(True)
- self.column_title.set_visible(False)
- self.column_title.set_expand(True)
- self.column_priority.set_expand(False)
- self.column_cat.set_visible(False)
- self.column_sid.set_expand(False)
- tgbshowmenu = self.gui.get_widget('gtk_toogletoolbutton_menu_view')
- tgbshowmenu.set_active(True)
-
- if len(sapnotes) == 0:
- sapnotes = self.db.get_notes()
-
- if self.view == 'component':
- self.populate_by_components(sapnotes)
- elif self.view == 'description':
- self.populate_by_component_descriptions(sapnotes)
- elif self.view == 'bookmarks':
- self.populate_by_bookmarks()
- elif self.view == 'projects':
- self.populate_by_projects(sapnotes)
- elif self.view == 'collections':
- self.populate_by_collections(sapnotes)
- elif self.view == 'tags':
- self.populate_by_tags(sapnotes)
- elif self.view == 'category':
- self.populate_by_category(sapnotes)
- elif self.view == 'chronologic':
- self.populate_by_chronologic(sapnotes)
- elif self.view == 'priority':
- self.populate_by_priority(sapnotes)
- elif self.view == 'type':
- self.populate_by_type(sapnotes)
- elif self.view == 'collection':
- self.populate_by_collection(sapnotes)
- elif self.view == 'annotation':
- self.populate_annotations()
- else:
- self.populate_by_components(sapnotes)
-
- self.debug("View '%s' populated" % (self.view))
- self.current_status = None
-
-
- def populate_by_bookmarks(self):
- matches = []
- sapnotes = self.db.get_notes()
- for sid in sapnotes:
- if sapnotes[sid]['bookmark']:
- matches.append(sid)
- matches.sort()
-
- visor = self.gui.get_widget('visor')
- self.cb.gui_toggle_menu_view(False)
- visor.populate(matches)
- statusbar = self.gui.get_widget('widget_statusbar')
- statusbar.message("<b>View %s populated with %d SAP Notes</b>" % (self.view.capitalize(), len(matches)))
-
-
- def populate_by_components(self, sapnotes, only_bookmarks=False):
- self.model.clear()
- self.treepids = {}
-
- if len(sapnotes) == 0:
- return
-
- scomp = set()
- dcomp = {}
-
- for sid in sapnotes:
- compkey = escape(sapnotes[sid]['componentkey'])
- comptxt = escape(sapnotes[sid]['componenttxt'])
- scomp.add(compkey)
- dcomp[compkey] = comptxt
- lcomp = list(scomp)
- lcomp.sort()
-
- for compkey in lcomp:
- subkeys = compkey.split('-')
- ppid = None
- for i in range(1, len(subkeys)+1):
- key = ('-').join(subkeys[0:i])
- try:
- ppid = self.treepids[key]
- except:
- if i == len(subkeys):
- title = dcomp[compkey]
- else:
- title = ""
- node = self.get_node_component(key, title)
- ppid = self.model.append(ppid, node)
- self.treepids[key] = ppid
-
-
- def populate_by_priority(self, sapnotes):
- self.model.clear()
- treepids = {}
-
- if len(sapnotes) == 0:
- return
-
- scomp = set()
- dcomp = {}
- pset = set()
-
- for sid in sapnotes:
- try:
- priority = sapnotes[sid]['priority']
- pset.add(priority)
- except: pass
-
- plist = []
- plist.extend(pset)
- plist.sort()
-
- for priority in plist:
- node = self.get_node_priority(priority)
- pid = self.model.append(None, node)
- treepids[priority] = pid
-
-
- def populate_by_type(self, sapnotes):
- self.model.clear()
- treepids = {}
-
- if len(sapnotes) == 0:
- return
-
- scomp = set()
- dcomp = {}
- pset = set()
-
- for sid in sapnotes:
- try:
- sntype = sapnotes[sid]['type']
- pset.add(sntype)
- except: pass
-
- plist = []
- plist.extend(pset)
- plist.sort()
-
- for sntype in plist:
- node = self.get_node_type(sntype)
- pid = self.model.append(None, node)
- treepids[sntype] = pid
-
-
- def populate_by_component_descriptions(self, sapnotes, only_bookmarks=False):
- self.model.clear()
- self.treepids = {}
-
- if len(sapnotes) == 0:
- return
-
- scomp = set()
- dcomp = {}
-
- for sid in sapnotes:
- compkey = escape(sapnotes[sid]['componentkey'])
- comptxt = escape(sapnotes[sid]['componenttxt'])
- scomp.add(compkey)
- dcomp[compkey] = comptxt
- lcomp = list(scomp)
- lcomp.sort()
-
- for compkey in lcomp:
- subkeys = compkey.split('-')
- ppid = None
- for i in range(1, len(subkeys)+1):
- key = ('-').join(subkeys[0:i])
- try:
- ppid = self.treepids[key]
- except:
- if i == len(subkeys):
- title = dcomp[compkey]
- else:
- title = ""
- node = self.get_node_component_desc(key, title)
- ppid = self.model.append(ppid, node)
- self.treepids[key] = ppid
-
-
- def populate_by_category(self, sapnotes):
- self.model.clear()
- treepids = {}
-
- if len(sapnotes) == 0:
- return
-
- scomp = set()
- dcomp = {}
- catset = set()
-
- for sid in sapnotes:
- try:
- cat = sapnotes[sid]['category']
- catset.add(cat)
- except: pass
-
- catlist = []
- catlist.extend(catset)
- catlist.sort()
-
- for cat in catlist:
- node = self.get_node_category(cat)
- pid = self.model.append(None, node)
- treepids[cat] = pid
-
-
- def populate_by_collection(self, sapnotes):
- #FIXME: manage collections
- self.model.clear()
- self.clts = self.app.get_service('Collections')
- collections = self.clts.get_all()
- od = OrderedDict(sorted(collections.items(), key=lambda t: t[1]))
- for tid in od:
- node = self.get_node_collection(tid, od[tid])
- pid = self.model.append(None, node)
-
-
- def populate_annotations(self, annotations=None):
- visor = self.gui.get_widget('visor')
- self.cb.gui_toggle_menu_view(False)
- visor.populate_annotations(annotations)
-
-
- def populate_by_chronologic(self, sapnotes):
- self.model.clear()
- treepids = {}
-
- if len(sapnotes) == 0:
- return
-
- years = set()
- months = set()
- days = set()
- for sid in sapnotes:
- try:
- downloaded = dateparser.parse(sapnotes[sid]['feedupdate'])
- year = "%d" % downloaded.year
- month = "%02d" % downloaded.month
- day = "%02d" % downloaded.day
- key_year = year
- key_month = year + month
- key_day = year + month + day
- years.add(key_year)
- months.add(key_month)
- days.add(key_day)
- except:
- pass
- years = list(years)
- years.sort(reverse=True)
- months = list(months)
- months.sort(reverse=True)
- days = list(days)
- days.sort(reverse=True)
-
- for key_year in years:
- try:
- treepids[key_year]
- except:
- adate = key_year + '0101'
- downloaded = dateparser.parse(adate)
- node = self.get_node_date_year(downloaded, key_year)
- treepids[key_year] = self.model.append(None, node)
-
- for key_month in months:
- try:
- treepids[key_month]
- except:
- adate = key_month + '01'
- downloaded = dateparser.parse(adate)
- node = self.get_node_date_month(downloaded, key_month)
- key_year = key_month[0:4]
- treepids[key_month] = self.model.append(treepids[key_year], node)
-
- for key_day in days:
- try:
- treepids[key_day]
- except:
- downloaded = dateparser.parse(key_day)
- key_month = key_day[0:6]
- node = self.get_node_date_day(downloaded, key_day)
- treepids[key_day] = self.model.append(treepids[key_month], node)
-
-
-
-
Index: branches/BR-0.3/basico/uifuncs.py
===================================================================
--- branches/BR-0.3/basico/uifuncs.py (revision 232)
+++ branches/BR-0.3/basico/uifuncs.py (nonexistent)
@@ -1,160 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: uifuncs.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: Generic UI functions service
-
-
-from gi.repository import Gtk
-from gi.repository import Gio
-from gi.repository import Pango
-from gi.repository.GdkPixbuf import Pixbuf
-
-from .service import Service
-
-class UIFuncs(Service):
- def initialize(self):
- self.get_services()
-
-
- def get_services(self):
- self.gui = self.app.get_service('GUI')
- self.cb = self.app.get_service('Callbacks')
- self.im = self.app.get_service('IM')
- self.db = self.app.get_service('DB')
-
-
- def get_gtk_version(self):
- return Gtk.get_major_version(), Gtk.get_minor_version(), Gtk.get_micro_version()
-
-
- def check_gtk_version(self):
- vmajor, vminor, vmicro = self.get_gtk_version()
- self.debug("GTK+ Version: %d.%d.%d" % (vmajor, vminor, vmicro))
-
- if vmajor == 3 and vminor >= 18:
- self.debug("GTK+ version supported")
- return True
- else:
- self.log.error("Please, install a modern version of GTK+ (>= 3.18)")
- return False
-
-
- def message_dialog(self, head, body):
- dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, "%s" % head)
- dialog.format_secondary_text("%s" % body)
- dialog.run()
- dialog.destroy()
-
-
- def statusbar_msg(self, message):
- statusbar = self.gui.get_widget('widget_statusbar')
- context = statusbar.get_context_id("basico")
- statusbar.push(context, message)
-
-
- def create_menuview_button(self, view):
- hbox = Gtk.Box(spacing = 0, orientation="horizontal")
- icon = self.im.get_pixbuf_icon('basico-%s' % view, 24, 24)
- image = Gtk.Image()
- image.set_from_pixbuf(icon)
- label = Gtk.Label("View by %s" % view)
- hbox.pack_start(image, False, False, 3)
- hbox.pack_start(label, False, False, 3)
- button = Gtk.Button()
- button.add(hbox)
- button.set_relief(Gtk.ReliefStyle.NONE)
- button.connect('clicked', self.cb.gui_refresh_view, '%s' % view)
-
- return button
-
-
- def create_button(self, icon_name=None, icon_width=32, icon_heigth=32, title=''):
- button = Gtk.Button()
- button.set_relief(Gtk.ReliefStyle.NONE)
- hbox = Gtk.HBox()
- icon = self.im.get_new_image_icon(icon_name, icon_width, icon_heigth)
- label = Gtk.Label()
- label.set_markup(title)
- label.set_xalign(0.0)
- hbox.pack_start(icon, False, False, 6)
- hbox.pack_start(label, True, True, 0)
- button.add(hbox)
-
- return button
-
-
- def warning_message(self, widget, question, explanation, bag):
- self.debug("DBAG: %s(%d)" % (bag, len(bag)))
- window = self.gui.get_widget('gtk_app_window_main')
- icon = self.im.get_new_image_icon('basico-delete', 96, 96)
- icon.show_all()
- dialog = Gtk.MessageDialog(window, 0, Gtk.MessageType.QUESTION,
- Gtk.ButtonsType.YES_NO, question)
- dialog.set_size_request(800, 600);
- content_area = dialog.get_content_area()
- vbox = Gtk.VBox()
- label = Gtk.Label()
- label.set_markup('<b>This is the list of SAP Notes to be deleted</b>')
- vbox.pack_start(label, False, False, 0)
- scr = Gtk.ScrolledWindow()
- scr.set_shadow_type(Gtk.ShadowType.NONE)
- scr.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
- label = Gtk.Label()
- label.set_property('ellipsize', Pango.EllipsizeMode.MIDDLE)
- label.set_property('selectable', True)
- label.grab_focus()
- msg = '\n'
- if len(bag) == 1:
- label.set_text(bag[0])
- else:
- for sid in bag:
- msg += ' %s - %s \n' % (sid, self.db.get_title(sid))
- msg += '\n'
- label.set_markup(msg)
- scr.add(label)
- vbox.pack_start(scr, True, True, 0)
- content_area.pack_start(vbox, True, True, 0)
- content_area.show_all()
- dialog.set_image(icon)
- dialog.format_secondary_text(explanation)
- response = dialog.run()
- if response == Gtk.ResponseType.YES:
- answer = True
- elif response == Gtk.ResponseType.NO:
- answer = False
- dialog.destroy()
-
- return answer
-
-
- def set_widget_visibility(self, widget_name, visibility):
- widget = self.gui.get_widget(widget_name)
- if visibility:
- widget.set_no_show_all(False)
- widget.show_all()
- else:
- widget.set_no_show_all(True)
- widget.hide()
-
-
- def get_combobox_text(self, combobox, col):
- treeiter = combobox.get_active_iter()
- model = combobox.get_model()
- return model[treeiter][col]
-
-
- def set_combobox_active(self, combobox, value):
- model = combobox.get_model()
- for treeiter in model:
- self.debug (model[treeiter][0])
-
- def get_textview_text(self, textview):
- textbuffer = textview.get_buffer()
- istart, iend = textbuffer.get_bounds()
- return textbuffer.get_text(istart, iend, False)
-
- def set_textview_text(self, textview, text):
- textbuffer = textview.get_buffer()
- textbuffer.set_text(text)
/branches/BR-0.3/basico/uifuncs.py
Property changes:
Deleted: svn:executable
## -1 +0,0 ##
-*
\ No newline at end of property
Index: branches/BR-0.3/basico/sapnoteview.py
===================================================================
--- branches/BR-0.3/basico/sapnoteview.py (revision 232)
+++ branches/BR-0.3/basico/sapnoteview.py (nonexistent)
@@ -1,331 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: sapnoteview.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: SAPNoteView GTK.TreeView widget
-
-from enum import IntEnum
-import traceback as tb
-from cgi import escape
-
-import gi
-gi.require_version('Gdk', '3.0')
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gdk
-from gi.repository import Gio
-from gi.repository import Gtk
-from gi.repository import Pango
-from gi.repository.GdkPixbuf import Pixbuf
-from gi.repository import Pango
-from datetime import datetime
-from dateutil import parser as dateparser
-
-from .service import Service
-
-class Column(IntEnum):
- rowtype = 0
- checkbox = 1
- icon = 2
- component = 3
- category = 4
- type = 5
- id = 6
- title = 7
- priority = 8
- language = 9
- released = 10
-
-
-class SAPNoteView(Gtk.TreeView, Service):
- def __init__(self, app):
- Gtk.TreeView.__init__(self)
- self.app = app
-
- self.get_services()
- self.toggled = 0
- self.selected = set()
- self.count = 0
-
-
- # Setup treeview and model
- Gtk.TreeView.__init__(self)
- self.model = Gtk.TreeStore(
- str, # RowType@RowId
- bool, # CheckBox
- Pixbuf, # Icon
- str, # Component key
- str, # Category
- str, # Type
- str, # SAP Note Id
- str, # Title
- str, # Priority
- str, # Language
- str # Release date
- )
- self.set_model(self.model)
-
- # Setup columns
- # RowType
- self.renderer_rowtype = Gtk.CellRendererText()
- self.column_rowtype = Gtk.TreeViewColumn('', self.renderer_rowtype, text=Column.rowtype.value)
- self.column_rowtype.set_visible(False)
- self.column_rowtype.set_expand(False)
- self.column_rowtype.set_clickable(False)
- self.column_rowtype.set_sort_indicator(False)
- self.append_column(self.column_rowtype)
-
- # Checkbox
- self.renderer_checkbox = Gtk.CellRendererToggle()
- #~ self.renderer_checkbox.connect("toggled", self.on_cell_toggled)
- self.column_checkbox = Gtk.TreeViewColumn('', self.renderer_checkbox, active=1)
- self.column_checkbox.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- self.column_checkbox.set_visible(False)
- self.column_checkbox.set_expand(False)
- self.column_checkbox.set_clickable(False)
- self.column_checkbox.set_sort_indicator(False)
- self.append_column(self.column_checkbox)
- #~ self.column_checkbox.set_cell_data_func(self.renderer_checkbox, self.cell_data_func)
-
- # Icon
- self.renderer_icon = Gtk.CellRendererPixbuf()
- self.renderer_icon.set_alignment(0.0, 0.5)
- self.column_icon = Gtk.TreeViewColumn('', self.renderer_icon, pixbuf=2)
- self.column_icon.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- self.column_icon.set_visible(False)
- self.column_icon.set_expand(False)
- self.column_icon.set_clickable(False)
- self.column_icon.set_sort_indicator(False)
- self.append_column(self.column_icon)
-
- # Component key
- self.renderer_compkey = Gtk.CellRendererText()
- # ~ self.renderer_compkey.set_editable(True)
- #~ self.renderer_compkey.set_property('background', 'Lemon Chiffon')
- self.column_component = Gtk.TreeViewColumn('SAP Notes', self.renderer_compkey, markup=3)
- self.column_component.set_visible(True)
- self.column_component.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- self.column_component.set_clickable(True)
- self.column_component.set_sort_indicator(True)
- self.column_component.set_sort_column_id(3)
- self.column_component.set_sort_order(Gtk.SortType.ASCENDING)
- self.column_component.set_expand(True)
- self.append_column(self.column_component)
- expander_column = self.column_component
-
- # Category
- self.renderer_cat = Gtk.CellRendererText()
- self.column_cat = Gtk.TreeViewColumn('Category', self.renderer_cat, markup=4)
- self.column_cat.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- self.column_cat.set_visible(False)
- self.column_cat.set_expand(False)
- self.column_cat.set_clickable(True)
- self.column_cat.set_sort_indicator(True)
- self.column_cat.set_sort_column_id(4)
- self.column_cat.set_sort_order(Gtk.SortType.ASCENDING)
- self.append_column(self.column_cat)
-
- # Type
- self.renderer_sntype = Gtk.CellRendererText()
- self.column_sntype = Gtk.TreeViewColumn('Type', self.renderer_sntype, markup=5)
- self.column_sntype.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- self.column_sntype.set_visible(False)
- self.column_sntype.set_expand(False)
- self.column_sntype.set_clickable(True)
- self.column_sntype.set_sort_indicator(True)
- self.column_sntype.set_sort_column_id(5)
- self.column_sntype.set_sort_order(Gtk.SortType.ASCENDING)
- self.append_column(self.column_sntype)
-
- # SAP Note Id
- self.renderer_sid = Gtk.CellRendererText()
- self.renderer_sid.set_alignment(1.0, 0.5)
- self.column_sid = Gtk.TreeViewColumn('SAP Note', self.renderer_sid, markup=6)
- self.column_sid.set_visible(False)
- self.column_sid.set_expand(False)
- self.column_sid.set_clickable(True)
- self.column_sid.set_sort_indicator(True)
- self.column_sid.set_sort_column_id(6)
- self.column_sid.set_sort_order(Gtk.SortType.ASCENDING)
- self.append_column(self.column_sid)
-
- # Title
- self.renderer_title = Gtk.CellRendererText()
- self.renderer_title.set_property('ellipsize', Pango.EllipsizeMode.MIDDLE)
- self.column_title = Gtk.TreeViewColumn('Title', self.renderer_title, markup=7)
- self.column_title.set_visible(False)
- self.column_title.set_expand(True)
- self.column_title.set_clickable(True)
- self.column_title.set_sort_indicator(True)
- self.column_title.set_sort_column_id(7)
- self.column_title.set_sort_order(Gtk.SortType.ASCENDING)
- self.append_column(self.column_title)
-
- # Priority
- self.renderer_priority = Gtk.CellRendererText()
- self.column_priority = Gtk.TreeViewColumn('Priority', self.renderer_priority, markup=8)
- self.column_priority.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- self.column_priority.set_visible(True)
- self.column_priority.set_expand(False)
- self.column_priority.set_clickable(True)
- self.column_priority.set_sort_indicator(True)
- self.column_priority.set_sort_column_id(8)
- self.column_priority.set_sort_order(Gtk.SortType.ASCENDING)
- self.append_column(self.column_priority)
-
- # Language
- self.renderer_lang = Gtk.CellRendererText()
- self.column_lang = Gtk.TreeViewColumn('Language', self.renderer_lang, markup=9)
- self.column_lang.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- self.column_lang.set_visible(False)
- self.column_lang.set_expand(False)
- self.column_lang.set_clickable(True)
- self.column_lang.set_sort_indicator(True)
- self.column_lang.set_sort_column_id(9)
- self.column_lang.set_sort_order(Gtk.SortType.ASCENDING)
- self.append_column(self.column_lang)
-
- # Release date
- self.renderer_reldate = Gtk.CellRendererText()
- self.column_rel = Gtk.TreeViewColumn('Released on', self.renderer_reldate, markup=10)
- self.column_rel.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- self.column_rel.set_visible(False)
- self.column_rel.set_expand(False)
- self.column_rel.set_clickable(True)
- self.column_rel.set_sort_indicator(True)
- self.column_rel.set_sort_column_id(10)
- self.column_rel.set_sort_order(Gtk.SortType.ASCENDING)
- self.append_column(self.column_rel)
-
- # TreeView common
- #~ self.set_level_indentation(6)
- self.set_can_focus(True)
- self.set_headers_visible(False)
- self.set_enable_search(True)
- self.set_hover_selection(False)
- self.set_grid_lines(Gtk.TreeViewGridLines.NONE)
- self.set_search_entry(self.gui.get_widget('stySearchInfo'))
- self.set_search_column(3)
- #~ self.set_row_separator_func(self.row_separator_func)
-
- # Selection
- self.selection = self.get_selection()
- #~ self.selection.set_select_function(self.select_function)
- self.selection.set_mode(Gtk.SelectionMode.SINGLE)
-
- # Font
- font_desc = Pango.FontDescription('Monospace 10')
- if font_desc:
- self.modify_font(font_desc)
-
- # Go live
- # FIXME: right click not working
- #~ self.connect('button_press_event', self.right_click)
- #~ self.connect('row-activated', self.double_click)
- #~ self.connect('cursor-changed', self.row_changed)
- #~ self.sig_selection_changed = self.selection.connect('changed', self.row_changed) #self.selection_changed)
-
- # Custom method to be implemented.
- self.prepare()
- self.show_all()
-
-
- def get_renderer_compkey(self):
- return self.renderer_compkey
-
-
- def get_services(self):
- self.gui = self.app.get_service("GUI")
- self.cb = self.app.get_service('Callbacks')
- self.sap = self.app.get_service('SAP')
- self.im = self.app.get_service('IM')
- self.settings = self.app.get_service('Settings')
- self.db = self.app.get_service('DB')
- self.annot = self.app.get_service('Annotation')
-
-
- def get_node(self, key, checkbox, icon, component, cat='', sntype='', sid='', title='', priority='', lang='', rel=''):
- node = []
- node.append(key)
- node.append(checkbox)
- node.append(icon)
- node.append(component)
- node.append(cat)
- node.append(sntype)
- node.append(sid)
- node.append(title)
- node.append(priority)
- node.append(lang)
- node.append(rel)
- return node
-
-
- def get_node_date_year(self, date, token_date):
- title = "%s" % token_date
- return self.get_node('date-year@%s' % token_date, 0, None, title)
-
-
- def get_node_date_month(self, date, token_date):
- title = "%s" % date.strftime("%B")
- return self.get_node('date-month@%s' % token_date, 0, None, title)
-
-
- def get_node_date_day(self, date, token_date):
- title = "%s" % date.strftime("%d - %A")
- return self.get_node('date-day@%s' % token_date, 0, None, title)
-
-
- def get_node_priority(self, priority):
- icon = None
- if len(priority) == 0:
- title = "No priority assigned"
- else:
- title = "%s" % priority
-
- return self.get_node('priority@%s' % priority, 0, icon, title)
-
-
- def get_node_collection(self, collection_id, collection_name, icon=None):
- icon = None
- return self.get_node('collection@%s' % collection_id, 0, icon, collection_name)
-
-
- def get_node_type(self, sntype):
- icon = self.im.get_icon('basico-type', 32, 32)
- if len(sntype) == 0:
- title = "SAP Note type not found"
- else:
- title = "%s" % sntype
- return self.get_node('type@%s' % sntype, 0, icon, title)
-
-
- def get_node_category(self, category=''):
- icon = None
- if len(category) == 0:
- catname = "No category assigned"
- else:
- catname = "%s" % category
- return self.get_node('category@%s' % catname, 0, icon, catname)
-
-
- def get_node_component(self, compkey, comptxt):
- icon = self.im.get_icon('basico-component', 32, 32)
- node = []
- count = len(self.db.get_notes_by_node("componentkey", compkey))
- component = "%s" % (compkey)
- return self.get_node('componentkey@%s' % compkey, 0, icon, component)
-
-
- def get_node_component_desc(self, compkey, comptxt):
- icon = self.im.get_icon('basico-description', 32, 32)
- node = []
- count = len(self.db.get_notes_by_node("componentkey", compkey))
- if len(comptxt) == 0:
- component = "%s" % (compkey)
- else:
- component = "%s" % (comptxt)
- return self.get_node('componentkey@%s' % compkey, 0, icon, component)
-
-
-
-
Index: branches/BR-0.3/basico/notify.py
===================================================================
--- branches/BR-0.3/basico/notify.py (revision 232)
+++ branches/BR-0.3/basico/notify.py (nonexistent)
@@ -1,33 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# File: notify.py
-# Author: Tomás Vírseda
-# License: GPL v3
-# Description: notifications service
-
-import sys
-import gi
-
-try:
- gi.require_version('Notify', '0.7')
- from gi.repository import Notify
- NOTIFY_INSTALLED = True
-except:
- NOTIFY_INSTALLED = False
-
-from .service import Service
-
-
-class Notification(Service):
- def initialize(self):
- if NOTIFY_INSTALLED:
- Notify.init('Basico')
-
- def show(self, module, message, icon_name):
- if NOTIFY_INSTALLED:
- icon = "dialog-%s" % icon_name # information | question | warning | error
- notification = Notify.Notification.new (module, message, icon)
- notification.show()
- else:
- return
-
Index: branches/BR-0.3/basico/widgets/sapnoteview.py
===================================================================
--- branches/BR-0.3/basico/widgets/sapnoteview.py (nonexistent)
+++ branches/BR-0.3/basico/widgets/sapnoteview.py (revision 233)
@@ -0,0 +1,331 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# File: sapnoteview.py
+# Author: Tomás Vírseda
+# License: GPL v3
+# Description: SAPNoteView GTK.TreeView widget
+
+from enum import IntEnum
+import traceback as tb
+from cgi import escape
+
+import gi
+gi.require_version('Gdk', '3.0')
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gdk
+from gi.repository import Gio
+from gi.repository import Gtk
+from gi.repository import Pango
+from gi.repository.GdkPixbuf import Pixbuf
+from gi.repository import Pango
+from datetime import datetime
+from dateutil import parser as dateparser
+
+from basico.core.service import Service
+
+class Column(IntEnum):
+ rowtype = 0
+ checkbox = 1
+ icon = 2
+ component = 3
+ category = 4
+ type = 5
+ id = 6
+ title = 7
+ priority = 8
+ language = 9
+ released = 10
+
+
+class SAPNoteView(Gtk.TreeView, Service):
+ def __init__(self, app):
+ Gtk.TreeView.__init__(self)
+ self.app = app
+
+ self.get_services()
+ self.toggled = 0
+ self.selected = set()
+ self.count = 0
+
+
+ # Setup treeview and model
+ Gtk.TreeView.__init__(self)
+ self.model = Gtk.TreeStore(
+ str, # RowType@RowId
+ bool, # CheckBox
+ Pixbuf, # Icon
+ str, # Component key
+ str, # Category
+ str, # Type
+ str, # SAP Note Id
+ str, # Title
+ str, # Priority
+ str, # Language
+ str # Release date
+ )
+ self.set_model(self.model)
+
+ # Setup columns
+ # RowType
+ self.renderer_rowtype = Gtk.CellRendererText()
+ self.column_rowtype = Gtk.TreeViewColumn('', self.renderer_rowtype, text=Column.rowtype.value)
+ self.column_rowtype.set_visible(False)
+ self.column_rowtype.set_expand(False)
+ self.column_rowtype.set_clickable(False)
+ self.column_rowtype.set_sort_indicator(False)
+ self.append_column(self.column_rowtype)
+
+ # Checkbox
+ self.renderer_checkbox = Gtk.CellRendererToggle()
+ #~ self.renderer_checkbox.connect("toggled", self.on_cell_toggled)
+ self.column_checkbox = Gtk.TreeViewColumn('', self.renderer_checkbox, active=1)
+ self.column_checkbox.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.column_checkbox.set_visible(False)
+ self.column_checkbox.set_expand(False)
+ self.column_checkbox.set_clickable(False)
+ self.column_checkbox.set_sort_indicator(False)
+ self.append_column(self.column_checkbox)
+ #~ self.column_checkbox.set_cell_data_func(self.renderer_checkbox, self.cell_data_func)
+
+ # Icon
+ self.renderer_icon = Gtk.CellRendererPixbuf()
+ self.renderer_icon.set_alignment(0.0, 0.5)
+ self.column_icon = Gtk.TreeViewColumn('', self.renderer_icon, pixbuf=2)
+ self.column_icon.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.column_icon.set_visible(False)
+ self.column_icon.set_expand(False)
+ self.column_icon.set_clickable(False)
+ self.column_icon.set_sort_indicator(False)
+ self.append_column(self.column_icon)
+
+ # Component key
+ self.renderer_compkey = Gtk.CellRendererText()
+ # ~ self.renderer_compkey.set_editable(True)
+ #~ self.renderer_compkey.set_property('background', 'Lemon Chiffon')
+ self.column_component = Gtk.TreeViewColumn('SAP Notes', self.renderer_compkey, markup=3)
+ self.column_component.set_visible(True)
+ self.column_component.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.column_component.set_clickable(True)
+ self.column_component.set_sort_indicator(True)
+ self.column_component.set_sort_column_id(3)
+ self.column_component.set_sort_order(Gtk.SortType.ASCENDING)
+ self.column_component.set_expand(True)
+ self.append_column(self.column_component)
+ expander_column = self.column_component
+
+ # Category
+ self.renderer_cat = Gtk.CellRendererText()
+ self.column_cat = Gtk.TreeViewColumn('Category', self.renderer_cat, markup=4)
+ self.column_cat.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.column_cat.set_visible(False)
+ self.column_cat.set_expand(False)
+ self.column_cat.set_clickable(True)
+ self.column_cat.set_sort_indicator(True)
+ self.column_cat.set_sort_column_id(4)
+ self.column_cat.set_sort_order(Gtk.SortType.ASCENDING)
+ self.append_column(self.column_cat)
+
+ # Type
+ self.renderer_sntype = Gtk.CellRendererText()
+ self.column_sntype = Gtk.TreeViewColumn('Type', self.renderer_sntype, markup=5)
+ self.column_sntype.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.column_sntype.set_visible(False)
+ self.column_sntype.set_expand(False)
+ self.column_sntype.set_clickable(True)
+ self.column_sntype.set_sort_indicator(True)
+ self.column_sntype.set_sort_column_id(5)
+ self.column_sntype.set_sort_order(Gtk.SortType.ASCENDING)
+ self.append_column(self.column_sntype)
+
+ # SAP Note Id
+ self.renderer_sid = Gtk.CellRendererText()
+ self.renderer_sid.set_alignment(1.0, 0.5)
+ self.column_sid = Gtk.TreeViewColumn('SAP Note', self.renderer_sid, markup=6)
+ self.column_sid.set_visible(False)
+ self.column_sid.set_expand(False)
+ self.column_sid.set_clickable(True)
+ self.column_sid.set_sort_indicator(True)
+ self.column_sid.set_sort_column_id(6)
+ self.column_sid.set_sort_order(Gtk.SortType.ASCENDING)
+ self.append_column(self.column_sid)
+
+ # Title
+ self.renderer_title = Gtk.CellRendererText()
+ self.renderer_title.set_property('ellipsize', Pango.EllipsizeMode.MIDDLE)
+ self.column_title = Gtk.TreeViewColumn('Title', self.renderer_title, markup=7)
+ self.column_title.set_visible(False)
+ self.column_title.set_expand(True)
+ self.column_title.set_clickable(True)
+ self.column_title.set_sort_indicator(True)
+ self.column_title.set_sort_column_id(7)
+ self.column_title.set_sort_order(Gtk.SortType.ASCENDING)
+ self.append_column(self.column_title)
+
+ # Priority
+ self.renderer_priority = Gtk.CellRendererText()
+ self.column_priority = Gtk.TreeViewColumn('Priority', self.renderer_priority, markup=8)
+ self.column_priority.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.column_priority.set_visible(True)
+ self.column_priority.set_expand(False)
+ self.column_priority.set_clickable(True)
+ self.column_priority.set_sort_indicator(True)
+ self.column_priority.set_sort_column_id(8)
+ self.column_priority.set_sort_order(Gtk.SortType.ASCENDING)
+ self.append_column(self.column_priority)
+
+ # Language
+ self.renderer_lang = Gtk.CellRendererText()
+ self.column_lang = Gtk.TreeViewColumn('Language', self.renderer_lang, markup=9)
+ self.column_lang.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.column_lang.set_visible(False)
+ self.column_lang.set_expand(False)
+ self.column_lang.set_clickable(True)
+ self.column_lang.set_sort_indicator(True)
+ self.column_lang.set_sort_column_id(9)
+ self.column_lang.set_sort_order(Gtk.SortType.ASCENDING)
+ self.append_column(self.column_lang)
+
+ # Release date
+ self.renderer_reldate = Gtk.CellRendererText()
+ self.column_rel = Gtk.TreeViewColumn('Released on', self.renderer_reldate, markup=10)
+ self.column_rel.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.column_rel.set_visible(False)
+ self.column_rel.set_expand(False)
+ self.column_rel.set_clickable(True)
+ self.column_rel.set_sort_indicator(True)
+ self.column_rel.set_sort_column_id(10)
+ self.column_rel.set_sort_order(Gtk.SortType.ASCENDING)
+ self.append_column(self.column_rel)
+
+ # TreeView common
+ #~ self.set_level_indentation(6)
+ self.set_can_focus(True)
+ self.set_headers_visible(False)
+ self.set_enable_search(True)
+ self.set_hover_selection(False)
+ self.set_grid_lines(Gtk.TreeViewGridLines.NONE)
+ self.set_search_entry(self.gui.get_widget('stySearchInfo'))
+ self.set_search_column(3)
+ #~ self.set_row_separator_func(self.row_separator_func)
+
+ # Selection
+ self.selection = self.get_selection()
+ #~ self.selection.set_select_function(self.select_function)
+ self.selection.set_mode(Gtk.SelectionMode.SINGLE)
+
+ # Font
+ font_desc = Pango.FontDescription('Monospace 10')
+ if font_desc:
+ self.modify_font(font_desc)
+
+ # Go live
+ # FIXME: right click not working
+ #~ self.connect('button_press_event', self.right_click)
+ #~ self.connect('row-activated', self.double_click)
+ #~ self.connect('cursor-changed', self.row_changed)
+ #~ self.sig_selection_changed = self.selection.connect('changed', self.row_changed) #self.selection_changed)
+
+ # Custom method to be implemented.
+ self.prepare()
+ self.show_all()
+
+
+ def get_renderer_compkey(self):
+ return self.renderer_compkey
+
+
+ def get_services(self):
+ self.gui = self.app.get_service("GUI")
+ self.cb = self.app.get_service('Callbacks')
+ self.sap = self.app.get_service('SAP')
+ self.im = self.app.get_service('IM')
+ self.settings = self.app.get_service('Settings')
+ self.db = self.app.get_service('DB')
+ self.annot = self.app.get_service('Annotation')
+
+
+ def get_node(self, key, checkbox, icon, component, cat='', sntype='', sid='', title='', priority='', lang='', rel=''):
+ node = []
+ node.append(key)
+ node.append(checkbox)
+ node.append(icon)
+ node.append(component)
+ node.append(cat)
+ node.append(sntype)
+ node.append(sid)
+ node.append(title)
+ node.append(priority)
+ node.append(lang)
+ node.append(rel)
+ return node
+
+
+ def get_node_date_year(self, date, token_date):
+ title = "%s" % token_date
+ return self.get_node('date-year@%s' % token_date, 0, None, title)
+
+
+ def get_node_date_month(self, date, token_date):
+ title = "%s" % date.strftime("%B")
+ return self.get_node('date-month@%s' % token_date, 0, None, title)
+
+
+ def get_node_date_day(self, date, token_date):
+ title = "%s" % date.strftime("%d - %A")
+ return self.get_node('date-day@%s' % token_date, 0, None, title)
+
+
+ def get_node_priority(self, priority):
+ icon = None
+ if len(priority) == 0:
+ title = "No priority assigned"
+ else:
+ title = "%s" % priority
+
+ return self.get_node('priority@%s' % priority, 0, icon, title)
+
+
+ def get_node_collection(self, collection_id, collection_name, icon=None):
+ icon = None
+ return self.get_node('collection@%s' % collection_id, 0, icon, collection_name)
+
+
+ def get_node_type(self, sntype):
+ icon = self.im.get_icon('basico-type', 32, 32)
+ if len(sntype) == 0:
+ title = "SAP Note type not found"
+ else:
+ title = "%s" % sntype
+ return self.get_node('type@%s' % sntype, 0, icon, title)
+
+
+ def get_node_category(self, category=''):
+ icon = None
+ if len(category) == 0:
+ catname = "No category assigned"
+ else:
+ catname = "%s" % category
+ return self.get_node('category@%s' % catname, 0, icon, catname)
+
+
+ def get_node_component(self, compkey, comptxt):
+ icon = self.im.get_icon('basico-component', 32, 32)
+ node = []
+ count = len(self.db.get_notes_by_node("componentkey", compkey))
+ component = "%s" % (compkey)
+ return self.get_node('componentkey@%s' % compkey, 0, icon, component)
+
+
+ def get_node_component_desc(self, compkey, comptxt):
+ icon = self.im.get_icon('basico-description', 32, 32)
+ node = []
+ count = len(self.db.get_notes_by_node("componentkey", compkey))
+ if len(comptxt) == 0:
+ component = "%s" % (compkey)
+ else:
+ component = "%s" % (comptxt)
+ return self.get_node('componentkey@%s' % compkey, 0, icon, component)
+
+
+
+
Index: branches/BR-0.3/basico/widgets/sapnoteviewvisor.py
===================================================================
--- branches/BR-0.3/basico/widgets/sapnoteviewvisor.py (nonexistent)
+++ branches/BR-0.3/basico/widgets/sapnoteviewvisor.py (revision 233)
@@ -0,0 +1,808 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# File: sapnoteviewvisor.py
+# Author: Tomás Vírseda
+# License: GPL v3
+# Description: SAPNoteViewVisor widget
+
+import os
+from os.path import sep as SEP
+from cgi import escape
+import glob
+import json
+
+import gi
+gi.require_version('Gdk', '3.0')
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gdk
+from gi.repository import Gio
+from gi.repository import Gtk
+from gi.repository.GdkPixbuf import Pixbuf
+from gi.repository import Pango
+
+from basico.core.env import LPATH
+from basico.core.service import Service
+from basico.widgets.collections import CollectionsMgtView
+from basico.widgets.annotation import AnnotationWidget
+from basico.widgets.importwdg import ImportWidget
+
+class SAPNoteViewVisorToolbar(Gtk.HBox, Service):
+ def __init__(self, app):
+ Gtk.Box.__init__(self, app)
+ self.app = app
+ self.get_services()
+ self.set_homogeneous(False)
+ self.tool_bar = Gtk.Toolbar()
+ self.pack_start(self.tool_bar, False, True, 0)
+ self.tool_bar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
+ self.tool_bar.set_property('margin-bottom', 0)
+
+ # Toggle Views button
+ tool = self.gui.add_widget('gtk_toogletoolbutton_menu_view', Gtk.ToggleToolButton.new())
+ tool.set_icon_name('gtk-select-color')
+ tool.connect('toggled', self.cb.gui_toggle_menu_view)
+ tool.set_active(False)
+ tool.set_hexpand(False)
+ self.tool_bar.insert(tool, -1)
+
+ # View combobox button/popover
+ tool = Gtk.ToolItem.new()
+ lhbox = Gtk.HBox()
+ menuviews = self.gui.add_widget('gtk_button_menu_views', Gtk.Button())
+ hbox = Gtk.HBox()
+ label = self.gui.add_widget('gtk_label_current_view', Gtk.Label())
+ label.set_xalign(0.0)
+ image = self.gui.add_widget('gtk_image_current_view', Gtk.Image())
+ hbox.pack_start(image, False, False, 3)
+ hbox.pack_start(label, True, True, 3)
+ menuviews.add(hbox)
+ lhbox.pack_start(menuviews, True, True, 3)
+ lhbox.show_all()
+ tool.add(lhbox)
+ tool.set_expand(False)
+ self.tool_bar.insert(tool, -1)
+
+ ### Popover menuviews
+ popover = self.gui.add_widget('gtk_popover_button_menu_views', Gtk.Popover.new(menuviews))
+ menuviews.connect('clicked', self.cb.gui_show_popover, popover)
+ box = Gtk.Box(spacing = 0, orientation="vertical")
+ popover.add(box)
+
+
+ box.pack_start(self.uif.create_menuview_button('annotation'), False, False, 0)
+ separator = Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)
+ box.pack_start(separator, False, False, 0)
+ box.pack_start(self.uif.create_menuview_button('collection'), False, False, 0)
+ box.pack_start(self.uif.create_menuview_button('component'), False, False, 0)
+ box.pack_start(self.uif.create_menuview_button('description'), False, False, 0)
+ box.pack_start(self.uif.create_menuview_button('bookmarks'), False, False, 0)
+ box.pack_start(self.uif.create_menuview_button('category'), False, False, 0)
+ box.pack_start(self.uif.create_menuview_button('chronologic'), False, False, 0)
+ box.pack_start(self.uif.create_menuview_button('priority'), False, False, 0)
+ box.pack_start(self.uif.create_menuview_button('type'), False, False, 0)
+
+ # Import button
+ tool = Gtk.ToolButton()
+ tool.set_icon_name('basico-add')
+ popover = self.gui.add_widget('gtk_popover_toolbutton_import', Gtk.Popover.new(tool))
+ tool.connect('clicked', self.cb.gui_show_popover, popover)
+ self.tool_bar.insert(tool, -1)
+
+ ## Popover body
+ box = Gtk.VBox(spacing = 0, orientation="vertical")
+ box.set_property('margin', 3)
+ widget_import = self.gui.add_widget('widget_import', ImportWidget(self.app))
+ box.pack_start(widget_import, True, True, 6)
+ popover.add(box)
+
+ # Annotation button
+ tool = Gtk.ToolButton()
+ tool.set_icon_name('basico-annotation')
+ popover = self.gui.add_widget('gtk_popover_annotation', Gtk.Popover.new(tool))
+ # ~ tool.connect('clicked', self.cb.gui_show_popover, popover)
+ tool.connect('clicked', self.cb.gui_annotation_widget_show)
+ self.tool_bar.insert(tool, -1)
+
+ ## Popover body
+ box = Gtk.VBox(spacing = 0, orientation="vertical")
+ box.set_property('margin', 3)
+ widget_import = self.gui.add_widget('widget_annotation', AnnotationWidget(self.app))
+ box.pack_start(widget_import, True, True, 6)
+ popover.add(box)
+
+
+ # Filter entry
+ tool = Gtk.ToolItem.new()
+
+ hbox = Gtk.HBox()
+ entry = Gtk.Entry()
+ entry.connect('activate', self.cb.gui_filter_visor)
+ self.gui.add_widget('gtk_entry_filter_visor', entry)
+
+ icon = self.im.get_pixbuf_icon('basico-find')
+ entry.set_icon_from_pixbuf(Gtk.EntryIconPosition.PRIMARY, icon)
+ entry.set_icon_sensitive(Gtk.EntryIconPosition.PRIMARY, True)
+ entry.set_icon_tooltip_markup (Gtk.EntryIconPosition.PRIMARY, "Search in the whole database")
+
+ icon = self.im.get_pixbuf_icon('basico-filter')
+ entry.set_icon_from_pixbuf(Gtk.EntryIconPosition.SECONDARY, icon)
+ entry.set_icon_sensitive(Gtk.EntryIconPosition.SECONDARY, True)
+ entry.set_icon_tooltip_markup (Gtk.EntryIconPosition.SECONDARY, "Click here to filter results")
+ entry.set_placeholder_text("Filter results...")
+
+ def on_icon_pressed(entry, icon_pos, event):
+ if icon_pos == Gtk.EntryIconPosition.PRIMARY:
+ self.cb.action_search(entry)
+ elif icon_pos == Gtk.EntryIconPosition.SECONDARY:
+ self.cb.gui_filter_visor(entry)
+
+ entry.connect('changed', self.cb.gui_filter_visor)
+ entry.connect("icon-press", on_icon_pressed)
+ hbox.pack_start(entry, True, True, 0)
+ tool.add(hbox)
+ tool.set_expand(True)
+ self.tool_bar.insert(tool, -1)
+
+ # ~ Separator
+ tool = Gtk.SeparatorToolItem.new()
+ tool.set_draw(False)
+ tool.set_expand(True)
+ self.tool_bar.insert(tool, -1)
+
+ # Button Total SAP Notes
+ tool = Gtk.ToolItem()
+ tool.set_expand(False)
+ button = self.gui.add_widget('gtk_button_total_notes', Gtk.Button())
+ button.set_relief(Gtk.ReliefStyle.NONE)
+
+ popover = self.gui.add_widget('gtk_popover_button_total_notes', Gtk.Popover.new(button))
+ label = self.gui.add_widget('gtk_label_total_notes', Gtk.Label())
+ hbox = Gtk.HBox()
+ hbox.pack_start(label, False, False, 0)
+ button.add(hbox)
+ button.connect('clicked', self.cb.gui_show_popover, popover)
+ tool.add(button)
+ self.tool_bar.insert(tool, -1)
+
+ ## Popover body
+ box = self.build_popover(popover)
+ popover.add(box)
+
+ # Fullscreen toggle button
+ tool = Gtk.ToolItem()
+ tool.set_expand(False)
+ icon = self.im.get_new_image_icon('basico-fullscreen', 24, 24)
+ box = self.gui.add_widget('gtk_box_container_icon_fullscreen', Gtk.Box())
+ box.pack_start(icon, False, False, 0)
+ button = Gtk.ToggleButton()
+ button.set_relief(Gtk.ReliefStyle.NONE)
+ button.connect('toggled', self.cb.gui_toggle_fullscreen)
+ button.add(box)
+ tool.add(button)
+ self.tool_bar.insert(tool, -1)
+
+ # Toolbar initial settings
+ self.set_visible(True)
+ self.set_no_show_all(False)
+ self.tool_bar.set_hexpand(True)
+
+
+ def get_services(self):
+ self.gui = self.app.get_service("GUI")
+ self.cb = self.app.get_service('Callbacks')
+ self.sap = self.app.get_service('SAP')
+ self.im = self.app.get_service('IM')
+ self.settings = self.app.get_service('Settings')
+ self.db = self.app.get_service('DB')
+ self.uif = self.app.get_service("UIF")
+
+
+ def build_popover(self, popover):
+ box = Gtk.Box(spacing = 3, orientation="vertical")
+ sid = '0000000000'
+
+ def get_popover_button(text, icon_name):
+ button = Gtk.Button()
+ button.set_relief(Gtk.ReliefStyle.NONE)
+ hbox = Gtk.HBox()
+ icon = self.im.get_new_image_icon(icon_name, 24, 24)
+ lbltext = Gtk.Label()
+ lbltext.set_xalign(0.0)
+ lbltext.set_markup('%s' % text)
+ hbox.pack_start(icon, False, False, 3)
+ hbox.pack_start(lbltext, True, True, 3)
+ button.add(hbox)
+ return button
+
+ # Popover button "Bookmark"
+ button = get_popover_button("(Un)bookmark SAP Notes", 'basico-bookmarks')
+ button.connect('clicked', self.cb.switch_bookmark_current_set, popover)
+ box.pack_start(button, False, False, 0)
+
+ # Separator
+ separator = Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)
+ box.pack_start(separator, True, True, 0)
+
+ # Popover button Collection Management
+ button = get_popover_button("Manage collections", 'basico-collection')
+ box.pack_start(button, False, False, 0)
+ self.popcollections = self.gui.add_widget('gtk_popover_button_manage_collections_selected_notes', Gtk.Popover.new(button))
+ self.popcollections.set_position(Gtk.PositionType.RIGHT)
+ button.connect('clicked', self.cb.gui_show_popover, self.popcollections)
+ self.popcollections.add(CollectionsMgtView(self.app, sid, self.popcollections))
+
+ # Separator
+ separator = Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)
+ box.pack_start(separator, True, True, 0)
+
+
+ # Popover button "Delete SAP Notes"
+ button = get_popover_button("Delete SAP Notes", 'basico-delete')
+ button.connect('clicked', self.cb.sapnote_delete, sid)
+ box.pack_start(button, False, False, 0)
+
+ return box
+
+class SAPNoteViewVisor(Gtk.Box, Service):
+ def __init__(self, app):
+ Gtk.Box.__init__(self, app)
+ self.app = app
+ self.bag = []
+ self.get_services()
+ self.setup()
+
+
+ def get_services(self):
+ self.gui = self.app.get_service("GUI")
+ self.cb = self.app.get_service('Callbacks')
+ self.sap = self.app.get_service('SAP')
+ self.im = self.app.get_service('IM')
+ self.settings = self.app.get_service('Settings')
+ self.db = self.app.get_service('DB')
+ self.uif = self.app.get_service("UIF")
+ self.utils = self.app.get_service("Utils")
+ self.annot = self.app.get_service('Annotation')
+
+ def get_treeview(self):
+ return self.treeview
+
+
+ def sort_by_timestamp(self):
+ self.sorted_model.set_sort_column_id(11, Gtk.SortType.DESCENDING)
+
+
+ def setup(self):
+ scr = Gtk.ScrolledWindow()
+ scr.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
+ scr.set_shadow_type(Gtk.ShadowType.IN)
+ viewport = Gtk.Viewport()
+ self.treeview = Gtk.TreeView()
+ viewport.add(self.treeview)
+ scr.add(viewport)
+ self.pack_start(scr, True, True, 6)
+
+ # Setup model
+ self.model = Gtk.TreeStore(
+ int, # key
+ Pixbuf, # Icon
+ int, # checkbox
+ str, # sid
+ str, # title
+ str, # component
+ str, # category
+ str, # type
+ str, # priority
+ str, # last update
+ str, # Annotation Id (extra key)
+ str, # Timestamp
+ )
+
+ # Setup columns
+ def get_column_header_widget(title, icon_name=None, width=24, height=24):
+ hbox = Gtk.HBox()
+ icon = self.im.get_new_image_icon(icon_name, width, height)
+ label = Gtk.Label()
+ label.set_markup("<b>%s</b>" % title)
+ label.modify_font(Pango.FontDescription('Monospace 10'))
+ hbox.pack_start(icon, False, False, 3)
+ hbox.pack_start(label, True, True, 3)
+ hbox.show_all()
+ return hbox
+
+ # SAP Note key
+ self.renderer_key = Gtk.CellRendererText()
+ self.renderer_key.set_property('height', 32)
+ self.column_key = Gtk.TreeViewColumn('Key', self.renderer_key, text=0)
+ self.column_key.set_visible(False)
+ self.column_key.set_expand(False)
+ self.column_key.set_clickable(False)
+ self.column_key.set_sort_indicator(False)
+ self.treeview.append_column(self.column_key)
+
+ # Icon
+ self.renderer_icon = Gtk.CellRendererPixbuf()
+ self.renderer_icon.set_alignment(0.0, 0.5)
+ self.column_icon = Gtk.TreeViewColumn('Bookmark', self.renderer_icon, pixbuf=1)
+ widget = get_column_header_widget('', 'basico-bookmarks')
+ self.column_icon.set_widget(widget)
+ self.column_icon.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.column_icon.set_visible(True)
+ self.column_icon.set_expand(False)
+ self.column_icon.set_clickable(False)
+ self.column_icon.set_sort_indicator(False)
+ self.treeview.append_column(self.column_icon)
+
+ # SAP Note Checkbox
+ self.renderer_checkbox = Gtk.CellRendererToggle()
+ self.renderer_checkbox.connect("toggled", self.toggle_checkbox)
+ self.column_checkbox = Gtk.TreeViewColumn('', self.renderer_checkbox, active=2)
+ self.column_checkbox.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.column_checkbox.set_visible(False)
+ self.column_checkbox.set_expand(False)
+ self.column_checkbox.set_clickable(False)
+ self.column_checkbox.set_sort_indicator(False)
+ self.column_checkbox.set_property('spacing', 50)
+ self.treeview.append_column(self.column_checkbox)
+
+ # SAP Note Id
+ self.renderer_sid = Gtk.CellRendererText()
+ self.renderer_sid.set_property('xalign', 1.0)
+ self.renderer_sid.set_property('height', 36)
+ self.renderer_sid.set_property('background', '#F0E3E3')
+ self.column_sid = Gtk.TreeViewColumn('SAP Note Id', self.renderer_sid, markup=3)
+ widget = get_column_header_widget('SAP Note Id', 'basico-sid')
+ self.column_sid.set_widget(widget)
+ self.column_sid.set_visible(True)
+ self.column_sid.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.column_sid.set_expand(False)
+ self.column_sid.set_clickable(True)
+ self.column_sid.set_sort_indicator(True)
+ self.column_sid.set_sort_column_id(0)
+ self.column_sid.set_sort_order(Gtk.SortType.ASCENDING)
+ self.model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
+ self.treeview.append_column(self.column_sid)
+
+ # SAP Note title
+ self.renderer_title = Gtk.CellRendererText()
+ self.renderer_title.set_property('background', '#FFFEEA')
+ self.renderer_title.set_property('ellipsize', Pango.EllipsizeMode.MIDDLE)
+ self.column_title = Gtk.TreeViewColumn('Title', self.renderer_title, markup=4)
+ widget = get_column_header_widget('Title', 'basico-tag')
+ self.column_title.set_widget(widget)
+ self.column_title.set_visible(True)
+ self.column_title.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.column_title.set_expand(True)
+ self.column_title.set_clickable(True)
+ self.column_title.set_sort_indicator(True)
+ self.column_title.set_sort_column_id(4)
+ self.treeview.append_column(self.column_title)
+
+ # SAP Note Component
+ self.renderer_component = Gtk.CellRendererText()
+ self.renderer_component.set_property('background', '#E3E3F0')
+ self.column_component = Gtk.TreeViewColumn('Component', self.renderer_component, markup=5)
+ widget = get_column_header_widget('Component', 'basico-component')
+ self.column_component.set_widget(widget)
+ self.column_component.set_visible(True)
+ self.column_component.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.column_component.set_expand(False)
+ self.column_component.set_clickable(True)
+ self.column_component.set_sort_indicator(True)
+ self.column_component.set_sort_column_id(5)
+ self.treeview.append_column(self.column_component)
+
+ # SAP Note Category
+ self.renderer_category = Gtk.CellRendererText()
+ self.renderer_category.set_property('background', '#E3F1E3')
+ self.column_category = Gtk.TreeViewColumn('Category', self.renderer_category, markup=6)
+ widget = get_column_header_widget('Category', 'basico-category')
+ self.column_category.set_widget(widget)
+ self.column_category.set_visible(False)
+ self.column_category.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.column_category.set_expand(False)
+ self.column_category.set_clickable(True)
+ self.column_category.set_sort_indicator(True)
+ self.column_category.set_sort_column_id(6)
+ self.treeview.append_column(self.column_category)
+
+ # SAP Note Type
+ self.renderer_type = Gtk.CellRendererText()
+ self.renderer_type.set_property('background', '#e4f1f1')
+ self.column_type = Gtk.TreeViewColumn('Type', self.renderer_type, markup=7)
+ self.column_type.set_visible(True)
+ self.column_type.set_expand(False)
+ self.column_type.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.column_type.set_clickable(True)
+ self.column_type.set_sort_indicator(True)
+ self.column_type.set_sort_column_id(7)
+ self.treeview.append_column(self.column_type)
+
+ # SAP Note Priority
+ self.renderer_priority = Gtk.CellRendererText()
+ self.column_priority = Gtk.TreeViewColumn('Priority', self.renderer_priority, markup=8)
+ self.column_priority.set_visible(False)
+ self.column_priority.set_expand(True)
+ self.column_priority.set_clickable(True)
+ self.column_priority.set_sort_indicator(True)
+ self.column_priority.set_sort_column_id(8)
+ self.treeview.append_column(self.column_priority)
+
+ # SAP Note UpdatedOn
+ self.renderer_updated = Gtk.CellRendererText()
+ self.renderer_updated.set_property('background', '#FFE6D1')
+ self.column_updated = Gtk.TreeViewColumn('Updated On', self.renderer_updated, markup=9)
+ self.column_updated.set_visible(True)
+ self.column_updated.set_expand(False)
+ self.column_updated.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.column_updated.set_clickable(True)
+ self.column_updated.set_sort_indicator(True)
+ self.column_updated.set_sort_column_id(11)
+ self.treeview.append_column(self.column_updated)
+
+ # Annotation Id
+ self.renderer_annotation = Gtk.CellRendererText()
+ self.column_annotation = Gtk.TreeViewColumn('Annotation Id', self.renderer_annotation, markup=10)
+ self.column_annotation.set_visible(False)
+ self.column_annotation.set_expand(False)
+ self.column_annotation.set_clickable(False)
+ self.column_annotation.set_sort_indicator(False)
+ self.treeview.append_column(self.column_annotation)
+
+ # Timestamp
+ self.renderer_timestamp = Gtk.CellRendererText()
+ self.column_timestamp = Gtk.TreeViewColumn('Annotation Id', self.renderer_timestamp, text=11)
+ self.column_timestamp.set_visible(False)
+ self.column_timestamp.set_expand(False)
+ self.column_timestamp.set_clickable(False)
+ self.column_timestamp.set_sort_indicator(False)
+ self.treeview.append_column(self.column_timestamp)
+
+ # Treeview properties
+ self.treeview.set_can_focus(False)
+ self.treeview.set_enable_tree_lines(True)
+ self.treeview.set_headers_visible(True)
+ self.treeview.set_enable_search(True)
+ self.treeview.set_hover_selection(False)
+ self.treeview.set_grid_lines(Gtk.TreeViewGridLines.NONE)
+ self.treeview.set_enable_tree_lines(True)
+ self.treeview.set_level_indentation(10)
+ self.treeview.modify_font(Pango.FontDescription('Monospace 10'))
+ self.treeview.connect('button_press_event', self.right_click)
+
+ # DOC: In order to have a Gtk.Widged with sorting and filtering
+ # capabilities, you have to filter the model first, and use this
+ # new model to create the sorted model. Then, attach the sorted
+ # model to the treeview...
+
+ # Treeview filtering:
+ self.visible_filter = self.model.filter_new()
+ self.visible_filter.set_visible_func(self.visible_function)
+ # ~ treeview.set_search_equal_func(self.search_function)
+ # https://stackoverflow.com/questions/23355866/user-search-collapsed-rows-in-a-gtk-treeview
+
+ # TreeView sorting
+ self.sorted_model = Gtk.TreeModelSort(model=self.visible_filter)
+ self.sorted_model.set_sort_func(0, self.sort_function, None)
+
+ # Selection
+ self.selection = self.treeview.get_selection()
+ self.selection.set_mode(Gtk.SelectionMode.SINGLE)
+ self.selection.connect('changed', self.row_changed)
+
+ # Set model (filtered and sorted)
+ self.treeview.set_model(self.sorted_model)
+
+ self.show_all()
+
+
+ def sort_function(self, model, row1, row2, user_data):
+ sort_column = 0
+
+ value1 = model.get_value(row1, sort_column)
+ value2 = model.get_value(row2, sort_column)
+
+ if value1 < value2:
+ return -1
+ elif value1 == value2:
+ return 0
+ else:
+ return 1
+
+
+ def visible_function(self, model, itr, data):
+ entry = self.gui.get_widget('gtk_entry_filter_visor')
+ text = entry.get_text()
+ sid = str(model.get(itr, 3)[0])
+ if sid.startswith('<'):
+ sid = sid[3:-4]
+ title = model.get(itr, 4)[0]
+ component = str(model.get(itr, 5)[0])
+ category = model.get(itr, 6)[0]
+ rtype = model.get(itr, 7)[0]
+ string = ' '.join(['SAP Note %s' % sid, title, component, category, rtype])
+ match = text.upper() in string.upper()
+ # ~ self.debug("%s in %s? %s" % (text, string, match))
+
+ return match
+
+
+ def update_total_sapnotes_count(self, count=0):
+ statusbar = self.gui.get_widget('widget_statusbar')
+ lblnotescount = self.gui.get_widget('gtk_label_total_notes')
+ total = self.db.get_total()
+
+ lblnotescount.set_markup("<b>%d/<big>%d</big></b>" % (count, total))
+ statusbar.message("<b>View populated with %d SAP Notes</b>" % count)
+
+
+ def get_visible_filter(self):
+ return self.visible_filter
+
+
+ def row_changed(self, selection):
+ try:
+ model, treeiter = selection.get_selected()
+ if treeiter is not None:
+ component = model[treeiter][5]
+ if component == 'Annotation':
+ aid = model[treeiter][10]
+ is_valid = self.annot.is_valid(aid)
+ if is_valid:
+ self.cb.action_annotation_edit(aid)
+ else:
+ aid = None
+ self.uif.set_widget_visibility('gtk_vbox_container_annotations', False)
+ except Exception as error:
+ self.debug(error)
+ self.print_traceback()
+
+
+ def toggle_checkbox(self, cell, path):
+ self.model[path][2] = not self.model[path][2]
+
+
+ def get_node(self, key, icon, checkbox, sid, title, component, category='', sntype='', priority='', updated='', aid='', timestamp=''):
+ node = []
+ node.append(key)
+ node.append(icon)
+ node.append(checkbox)
+ node.append(sid)
+ node.append(title)
+ node.append(component)
+ node.append(category)
+ node.append(sntype)
+ node.append(priority)
+ node.append(updated)
+ node.append(aid) # Extra key for annotations id (aid)
+ node.append(timestamp)
+ return node
+
+
+ def refresh(self):
+ self.populate()
+ self.sort_by_timestamp()
+ self.debug("Sorted treeview by timestamp")
+
+ def get_bag(self):
+ return self.bag
+
+
+ def populate(self, bag=None, cid=None):
+ icon_annotation = self.im.get_pixbuf_icon('basico-annotation', 32, 32)
+ icon_content = self.im.get_pixbuf_icon('basico-logviewer', 32, 32)
+ icon_link = self.im.get_pixbuf_icon('basico-browser', 32, 32)
+ icon_empty = self.im.get_pixbuf_icon('basico-empty', 32, 32)
+ icon_sapnote = self.im.get_pixbuf_icon('basico-sapnote', 32, 32)
+ icon_bookmark = self.im.get_pixbuf_icon('basico-bookmarks', 32, 32)
+ self.column_sid.set_visible(True)
+
+ # ~ self.cb.gui_show_dashboard()
+
+ if bag is None:
+ bag = self.bag
+ else:
+ self.bag = bag
+
+
+ self.model.clear()
+
+ for sid in self.bag:
+ metadata = self.db.get_sapnote_metadata(sid)
+ if metadata is not None:
+ bookmark = metadata['bookmark']
+ title = escape(metadata['title'])
+ sid = str(int(metadata['id']))
+ if bookmark:
+ icon = icon_bookmark
+ title = "<b>%s</b>" % title
+ sid = "<b>%s</b>" % sid
+ else:
+ icon = icon_sapnote
+
+ timestamp = metadata['releaseon']
+ timestamp = timestamp.replace('-', '')
+ timestamp = timestamp.replace(':', '')
+ timestamp = timestamp.replace('T', '_')
+
+ node = self.get_node( int(metadata['id']),
+ icon,
+ bookmark,
+ '<b>%s</b>' % sid,
+ title,
+ escape(metadata['componentkey']),
+ escape(metadata['category']),
+ escape(metadata['type']),
+ escape(metadata['priority']),
+ self.utils.fuzzy_date_from_timestamp(timestamp),
+ '',
+ timestamp
+ )
+ pid = self.model.append(None, node)
+
+ # Load annotations
+ files = self.annot.get_by_sid(metadata['id'])
+ for fname in files:
+ with open(fname, 'r') as fa:
+ annotation = json.load(fa)
+ self.debug("Populating annotation: %s" % annotation['aid'])
+ node = self.get_node( 0,
+ icon_annotation,
+ False,
+ '',
+ annotation['title'],
+ annotation['component'],
+ '',
+ annotation['type'],
+ '',
+ self.utils.fuzzy_date_from_timestamp(annotation['timestamp']),
+ annotation['aid'],
+ annotation['timestamp']
+ )
+ self.model.append(pid, node)
+ self.treeview.set_model(self.sorted_model)
+ self.update_total_sapnotes_count(len(self.model))
+ self.show_widgets()
+ stack = self.gui.get_widget('gtk_stack_main')
+ stack.set_visible_child_name('visor')
+ # ~ self.annot.get_all()
+
+
+ def populate_annotations(self, annotations=None):
+ icon_annotation = self.im.get_pixbuf_icon('basico-annotation', 32, 32)
+ icon_content = self.im.get_pixbuf_icon('basico-logviewer', 32, 32)
+ icon_link = self.im.get_pixbuf_icon('basico-browser', 32, 32)
+ icon_empty = self.im.get_pixbuf_icon('basico-empty', 32, 32)
+ icon_bookmark = self.im.get_pixbuf_icon('basico-bookmarks', 32, 32)
+ icon_sapnote = self.im.get_pixbuf_icon('basico-sapnote', 32, 32)
+ self.column_sid.set_visible(False)
+ # ~ self.cb.gui_show_dashboard()
+
+ self.model.clear()
+
+ if annotations is None:
+ annotations = self.annot.get_all()
+
+ snpids = {}
+
+ for fname in annotations:
+ with open(fname, 'r') as fa:
+ annotation = json.load(fa)
+ sid = self.annot.get_sid(annotation['aid'])
+ node = self.get_node( 0,
+ icon_annotation,
+ False,
+ str(int(sid)),
+ annotation['title'],
+ annotation['component'],
+ '',
+ annotation['type'],
+ '',
+ self.utils.fuzzy_date_from_timestamp(annotation['timestamp']),
+ annotation['aid'],
+ annotation['timestamp']
+ )
+ self.model.append(None, node)
+ self.treeview.set_model(self.sorted_model)
+ self.sort_by_timestamp()
+ self.treeview.expand_all()
+ self.update_total_sapnotes_count(len(self.model))
+ self.show_widgets()
+ stack = self.gui.get_widget('gtk_stack_main')
+ stack.set_visible_child_name('visor')
+
+
+ def show_widgets(self):
+ self.uif.set_widget_visibility('gtk_button_total_notes', True)
+
+
+ def right_click(self, treeview, event, data=None):
+ if event.button == 3:
+ rect = Gdk.Rectangle()
+ rect.x = x = int(event.x)
+ rect.y = y = int(event.y)
+ pthinfo = self.treeview.get_path_at_pos(x,y)
+ if pthinfo is not None:
+ path,col,cellx,celly = pthinfo
+ model = treeview.get_model()
+ treeiter = model.get_iter(path)
+ component = model[treeiter][5]
+ sid = model[treeiter][0]
+ sid = "0"*(10 - len(str(sid))) + str(sid)
+ toolbar = self.gui.get_widget('visortoolbar')
+ popover = self.gui.add_widget('gtk_popover_visor_row', Gtk.Popover.new(treeview))
+ popover.set_position(Gtk.PositionType.TOP)
+ popover.set_pointing_to(rect)
+ box = self.build_popover(sid, popover, component)
+ popover.add(box)
+ self.cb.gui_show_popover(None, popover)
+
+
+ def build_popover(self, sid, popover, component):
+ box = Gtk.Box(spacing = 3, orientation="vertical")
+
+ def get_popover_button(text, icon_name):
+ button = Gtk.Button()
+ button.set_relief(Gtk.ReliefStyle.NONE)
+ hbox = Gtk.HBox()
+ icon = self.im.get_new_image_icon(icon_name, 24, 24)
+ lbltext = Gtk.Label()
+ lbltext.set_xalign(0.0)
+ lbltext.set_markup('%s' % text)
+ hbox.pack_start(icon, False, False, 3)
+ hbox.pack_start(lbltext, True, True, 3)
+ button.add(hbox)
+ return button
+
+ if component == 'Annotation':
+ # Popover button "Delete annotation"
+ button = get_popover_button("Delete annotation", 'basico-delete')
+ button.show_all()
+ button.connect('clicked', self.cb.action_annotation_delete)
+ box.pack_start(button, False, False, 0)
+
+ # Popover button "Duplicate annotation"
+ button = get_popover_button("Duplicate annotation", 'basico-duplicate')
+ button.show_all()
+ button.connect('clicked', self.cb.action_annotation_duplicate)
+ box.pack_start(button, False, False, 0)
+
+ else:
+ # Popover button "Add an annotation"
+ button = get_popover_button("Add an annotation", 'basico-annotation')
+ button.show_all()
+ button.connect('clicked', self.cb.gui_annotation_widget_show, sid, 'create')
+ box.pack_start(button, False, False, 0)
+
+ # Popover button "Open SAP Note"
+ button = get_popover_button("See SAP Note", 'basico-browse')
+ button.connect('clicked', self.cb.sapnote_browse, sid)
+ box.pack_start(button, False, False, 0)
+
+ # Popover button "Bookmark"
+ button = get_popover_button("(Un)bookmark SAP Note", 'basico-bookmarks')
+ button.connect('clicked', self.cb.switch_bookmark, [sid], popover)
+ box.pack_start(button, False, False, 0)
+
+ # Separator
+ separator = Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)
+ box.pack_start(separator, True, True, 0)
+
+ # Popover button Collection Management
+ button = get_popover_button("Manage collections", 'basico-collection')
+ box.pack_start(button, False, False, 0)
+ self.popcollections = self.gui.add_widget('gtk_popover_button_manage_collections_single_note', Gtk.Popover.new(button))
+ self.popcollections.set_position(Gtk.PositionType.RIGHT)
+ button.connect('clicked', self.cb.gui_show_popover, self.popcollections)
+ self.popcollections.add(CollectionsMgtView(self.app, sid, self.popcollections))
+
+ # Separator
+ separator = Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)
+ box.pack_start(separator, True, True, 0)
+
+
+ # Popover button "Delete SAP Note"
+ button = get_popover_button("Delete SAP Note", 'basico-delete')
+ button.connect('clicked', self.cb.sapnote_delete, sid)
+ box.pack_start(button, False, False, 0)
+
+ return box
Index: branches/BR-0.3/basico/widgets/statusbar.py
===================================================================
--- branches/BR-0.3/basico/widgets/statusbar.py (revision 232)
+++ branches/BR-0.3/basico/widgets/statusbar.py (revision 233)
@@ -14,8 +14,8 @@
from gi.repository import Gdk
from gi.repository import Pango
-from basico.env import ROOT, USER_DIR, APP, LPATH, GPATH, FILE
-from basico.service import Service
+from basico.core.env import ROOT, USER_DIR, APP, LPATH, GPATH, FILE
+from basico.core.service import Service
class Statusbar(Gtk.HBox, Service):
/branches/BR-0.3/basico/basico.py
23,20 → 23,20
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
 
from .utils import Utils
from .log import get_logger
from .gui import GUI
from .iconmanager import IconManager
from .sap import SAP
from .settings import Settings
from .uifuncs import UIFuncs
from .callbacks import Callback
from .notify import Notification
from .database import Database
from .driver import SeleniumDriver
from .collections import Collections
from .annotations import Annotation
from .env import ROOT, APP, LPATH, GPATH, FILE, STATS
from basico.core.utils import Utils
from basico.core.log import get_logger
from basico.core.gui import GUI
from basico.core.iconmanager import IconManager
from basico.core.sap import SAP
from basico.core.settings import Settings
from basico.core.uifuncs import UIFuncs
from basico.core.callbacks import Callback
from basico.core.notify import Notification
from basico.core.database import Database
from basico.core.driver import SeleniumDriver
from basico.core.collections import Collections
from basico.core.annotations import Annotation
from basico.core.env import ROOT, APP, LPATH, GPATH, FILE, STATS
 
 
class Basico(object):
/branches/BR-0.3/basico/core/__init__.py
--- branches/BR-0.3/basico/core/annotations.py (nonexistent)
+++ branches/BR-0.3/basico/core/annotations.py (revision 233)
@@ -0,0 +1,114 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# File: annotations.py
+# Author: Tomás Vírseda
+# License: GPL v3
+# Description: Annotations service
+
+import os
+import json
+import uuid
+import glob
+from os.path import sep as SEP
+
+from basico.core.env import FILE, LPATH
+from basico.core.service import Service
+
+
+class Annotation(Service):
+ def initialize(self):
+ '''
+ Setup Annotation Service
+ '''
+ self.get_services()
+
+
+ def get_services(self):
+ self.utils = self.app.get_service('Utils')
+
+
+ def gen_aid(self, sid):
+ '''
+ Generate new annotation id
+ '''
+ return "%s@%s" % (sid, str(uuid.uuid4()))
+
+
+ def create(self, annotation):
+ ANNOTATION_FILE_METADATA = LPATH['ANNOTATIONS'] + annotation['aid'] + '.json'
+ ANNOTATION_FILE_CONTENT = LPATH['ANNOTATIONS'] + annotation['aid'] + '.adoc'
+ annotation['timestamp'] = self.utils.timestamp()
+ # Write annotation content first and delete it
+ with open(ANNOTATION_FILE_CONTENT, 'w') as fc:
+ fc.write(annotation['content'])
+ del annotation['content']
+
+ # Write annotation metadata
+ with open(ANNOTATION_FILE_METADATA, 'w') as fa:
+ json.dump(annotation, fa)
+
+
+ def update(self, annotation):
+ ANNOTATION_FILE_METADATA = LPATH['ANNOTATIONS'] + annotation['aid'] + '.json'
+ ANNOTATION_FILE_CONTENT = LPATH['ANNOTATIONS'] + annotation['aid'] + '.adoc'
+
+ annotation['timestamp'] = self.utils.timestamp()
+ # Write updated annotation first and delete the old one after
+ with open(ANNOTATION_FILE_CONTENT, 'w') as fc:
+ fc.write(annotation['content'])
+ del annotation['content']
+
+ # Write annotation metadata
+ with open(ANNOTATION_FILE_METADATA, 'w') as fa:
+ json.dump(annotation, fa)
+
+
+ def delete(self, aid):
+ sid = self.get_sid(aid)
+ ANNOTATION_FILE_METADATA = LPATH['ANNOTATIONS'] + aid + '.json'
+ ANNOTATION_FILE_CONTENT = LPATH['ANNOTATIONS'] + aid + '.adoc'
+
+ if os.path.exists(ANNOTATION_FILE_METADATA):
+ os.unlink(ANNOTATION_FILE_METADATA)
+
+ if os.path.exists(ANNOTATION_FILE_CONTENT):
+ os.unlink(ANNOTATION_FILE_CONTENT)
+
+
+ def get_by_sid(self, sid):
+ ANNOTATION_FILES = LPATH['ANNOTATIONS'] + '%s*.json' % sid
+ annotations = glob.glob(ANNOTATION_FILES)
+ annotations.sort(reverse=True)
+
+ return annotations
+
+
+ def get_all(self):
+ return glob.glob(LPATH['ANNOTATIONS'] + '*.json')
+ return annotations
+
+
+ def get_sid(self, aid):
+ if '@' in aid:
+ return aid[:aid.find('@')]
+ else:
+ return aid # aid = sid
+
+
+ def get_metadata_from_file(self, aid=None):
+ if aid is not None:
+ ANNOTATION_FILE_METADATA = LPATH['ANNOTATIONS'] + aid + '.json'
+ with open(ANNOTATION_FILE_METADATA, 'r') as fa:
+ annotation = json.load(fa)
+ return annotation
+ else:
+ return None
+
+
+ def is_valid(self, aid):
+ ANNOTATION_FILE = LPATH['ANNOTATIONS'] + aid + '.json'
+ valid = os.path.exists(ANNOTATION_FILE)
+ if valid is False:
+ self.debug("Annotation %s is not valid? Please, check files in %s." % (aid, LPATH['ANNOTATIONS']))
+
+ return valid
/branches/BR-0.3/basico/core/callbacks.py
0,0 → 1,434
#!/usr/bin/python
# -*- coding: utf-8 -*-
# File: callbacks.py
# Author: Tomás Vírseda
# License: GPL v3
# Description: UI and related callbacks service
 
import os
import json
import time
 
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
 
 
from concurrent.futures import ThreadPoolExecutor as Executor
 
from basico.core.service import Service
from basico.widgets.sapnoteviewvisor import SAPNoteViewVisor, SAPNoteViewVisorToolbar
from basico.widgets.collections import CollectionsMgtView
from basico.widgets.annotation import AnnotationWidget
from basico.widgets.settings import SettingsView
from basico.core.env import FILE, LPATH
 
 
# PROPKEYS = CSV headers. SAP Note metadata
PROPKEYS = ['id', 'title', 'type', 'componentkey',
'componenttxt', 'category', 'priority', 'releaseon',
'language', 'version']
 
# Extend PROPKEYS with custom basico metadata
PROPKEYS.extend (['Bookmark'])
 
class Callback(Service):
def initialize(self):
self.get_services()
 
def get_services(self):
self.settings = self.app.get_service('Settings')
self.db = self.app.get_service('DB')
self.gui = self.app.get_service('GUI')
self.uif = self.app.get_service("UIF")
self.sap = self.app.get_service('SAP')
self.alert = self.app.get_service('Notify')
self.im = self.app.get_service('IM')
self.utils = self.app.get_service('Utils')
self.annot = self.app.get_service('Annotation')
 
 
def action_search(self, entry):
visor = self.gui.get_widget('visor')
viewmenu = self.gui.get_widget('viewmenu')
view = viewmenu.get_view()
 
if view != 'annotation':
term = entry.get_text()
bag = self.db.search(term)
visor.populate(bag)
 
 
def sapnote_browse(self, button, sid):
self.debug("Browsing SAP Note %d" % int(sid))
SAP_NOTE_URL = self.settings.get('SAP', 'SAP_NOTE_URL')
url = SAP_NOTE_URL % sid
self.utils.browse(url)
# ~ popover = self.gui.get_widget('poprowsid')
# ~ popover.hide()
 
 
def sapnote_delete(self, button, lsid):
self.debug("Deleting SAP Notes %s" % lsid)
if lsid == '0000000000':
visor = self.gui.get_widget('visor')
bag = visor.get_bag()
else:
bag = [lsid]
answer = self.uif.warning_message(button, 'Deleting SAP Notes', 'Are you sure?', bag)
if answer is True:
for sid in bag:
self.db.delete(sid)
viewmenu = self.gui.get_widget('viewmenu')
viewmenu.refresh()
else:
self.debug("Nothing deleted")
 
 
def gui_hide_popover(self, popover):
popover.hide()
 
 
def gui_show_about(self, *args):
stack = self.gui.get_widget('gtk_stack_main')
stack.set_visible_child_name('about')
self.gui_toggle_menu_view(False)
self.gui_hide_popover(self.gui.get_widget('gtk_popover_button_menu_system'))
self.uif.set_widget_visibility('gtk_button_total_notes', False)
self.uif.set_widget_visibility('gtk_button_dashboard', True)
 
 
def gui_show_log(self, *args):
logviewer = self.gui.get_widget('widget_logviewer')
stack = self.gui.get_widget('gtk_stack_main')
button_total_notes = self.gui.get_widget('gtk_button_total_notes')
 
 
logviewer.update()
self.gui_hide_popover(self.gui.get_widget('gtk_popover_button_menu_system'))
stack.set_visible_child_name('log')
self.uif.set_widget_visibility('gtk_button_dashboard', True)
# ~ self.uif.set_widget_visibility(button_total_notes, False)
 
 
def gui_show_settings(self, button):
stack = self.gui.get_widget('gtk_stack_main')
stack.set_visible_child_name('settings')
view_settings = self.gui.get_widget('widget_settings')
view_settings.update()
self.gui_hide_popover(self.gui.get_widget('gtk_popover_button_menu_system'))
self.uif.set_widget_visibility('gtk_button_total_notes', False)
self.uif.set_widget_visibility('gtk_button_dashboard', True)
 
 
def gui_show_dashboard(self, *args):
stack = self.gui.get_widget('gtk_stack_main')
stack.set_visible_child_name('visor')
self.gui_hide_popover(self.gui.get_widget('gtk_popover_button_menu_system'))
self.uif.set_widget_visibility('gtk_button_dashboard', False)
viewmenu = self.gui.get_widget('viewmenu')
current_view = viewmenu.get_view()
self.debug("SHOW-DASHBOARD::CURRENT-VIEW: %s" % current_view)
if current_view == 'annotation':
self.uif.set_widget_visibility('gtk_button_total_notes', False)
self.debug("Hidding button")
else:
self.uif.set_widget_visibility('gtk_button_total_notes', True)
self.debug("Showing button")
 
 
def gui_annotation_widget_show(self, widget, sid='0000000000', action='create'):
widget_annotation = self.gui.get_widget('widget_annotation')
widget = self.gui.get_widget('gtk_label_annotation_sid')
 
 
if action == 'create':
self.gui_annotation_widget_clear()
aid = self.annot.gen_aid(sid)
elif action == 'edit':
aid = sid
widget_annotation.set_metadata_to_widget(aid, action)
self.uif.set_widget_visibility('gtk_vbox_container_annotations', True)
widget.grab_focus()
 
 
def gui_show_popover(self, button, popover):
if popover.get_visible():
popover.hide()
else:
popover.show_all()
 
 
def switch_bookmark_current_set(self, button, popover):
visor = self.gui.get_widget('visor')
bag = visor.get_bag()
try:
for sid in bag:
metadata = self.db.get_sapnote_metadata(sid)
bookmark = metadata['bookmark']
if bookmark:
self.sapnote_unbookmark([sid])
else:
self.sapnote_bookmark([sid])
popover.hide()
except:
self.debug("Could not bookmark SAP Note %s" % sid)
visor.populate()
 
 
def switch_bookmark(self, button, lsid, popover):
visor = self.gui.get_widget('visor')
try:
for sid in lsid:
metadata = self.db.get_sapnote_metadata(sid)
bookmark = metadata['bookmark']
if bookmark:
self.sapnote_unbookmark([sid])
else:
self.sapnote_bookmark([sid])
popover.hide()
except:
self.debug("Could not bookmark SAP Note %s" % sid)
visor.populate()
 
def sapnote_bookmark(self, lsid):
self.db.set_bookmark(lsid)
 
 
def sapnote_unbookmark(self, lsid):
self.db.set_no_bookmark(lsid)
 
 
def sapnote_import_from_launchpad(self, *args):
db = self.get_service('DB')
webdriver = self.get_service('Driver')
textview = self.gui.get_widget('gtk_textview_download_launchpad')
visor = self.gui.get_widget('visor')
 
bag = []
all_notes = []
sapnotes = []
 
dlbuffer = textview.get_buffer()
istart, iend = dlbuffer.get_bounds()
text = dlbuffer.get_text(istart, iend, False)
lines = text.replace(' ', ',')
lines = lines.replace('\n', ',')
sapnotes.extend(lines.split(','))
for sapnote in sapnotes:
sid = sapnote.strip()
is_valid = self.db.is_valid(sid)
is_saved = self.db.get_sapnote_metadata(sid)
if is_valid and not is_saved:
bag.append(sid)
if is_valid:
all_notes.append(sid)
lbag = list(bag)
lbag.sort()
 
if len(bag)> 0:
driver = webdriver.open()
 
winroot = self.gui.get_widget('gtk_app_window_main')
self.debug("%d SAP Notes to be downloaded: %s" % (len(bag), ', '.join(list(bag))))
 
result = {}
 
self.sap.start_fetching(len(bag))
dlbag = []
 
# FIXME: max_workers = 1 = Threads disabled
# Indeed, I think this is the best option right now.
with Executor(max_workers=1) as exe:
jobs = []
for sapnote in lbag:
job = exe.submit(self.sap.fetch, driver, sapnote)
jobs.append(job)
 
for job in jobs:
rc, sapnote = job.result()
self.debug("\tRC SAP Note %s: %s" % (sapnote, rc))
result[sapnote] = rc
if rc:
sid = "0"*(10 - len(sapnote)) + sapnote
dlbag.append(sid)
time.sleep(0.2)
 
dlbuffer.set_text('')
popover = self.gui.get_widget('gtk_popover_toolbutton_import')
self.gui_hide_popover(popover)
if len(bag) > 0:
webdriver.close(driver)
 
self.sap.stop_fetching()
db.save_notes()
db.build_stats()
self.debug("Collection completed.")
# ~ self.gui_display_visor
viewmenu = self.gui.get_widget('viewmenu')
viewmenu.row_changed(None)
visor.populate(all_notes)
return result
 
 
def expand_menuview(self):
viewmenu = self.gui.get_widget('viewmenu')
viewmenu.expand_all()
 
 
def gui_viewmenu_filter(self, *args):
entry = self.gui.get_widget('gtk_entry_filter_view')
filter = entry.get_text()
viewmenu = self.gui.get_widget('viewmenu')
selection = viewmenu.get_selection()
 
def gui_iterate_over_data(model, path, itr):
rowkey = model.get(itr, 0)[0]
rowtype, rowval = rowkey.split('@')
dsc = model.get(itr, 3)[0]
contents = model.get(itr, 3)[0]
cleanstr = contents.replace('<b>', '')
cleanstr = cleanstr.replace('</b>', '')
model.set(itr, 3, '%s' % cleanstr)
viewmenu.collapse_row(path)
 
if len(filter) > 0:
if filter.upper() in rowval.upper() or filter.upper() in dsc.upper():
viewmenu.expand_to_path (path)
selection.select_path(path)
model.set(itr, 3, '<b>%s</b>' % contents)
else:
return
 
model = viewmenu.get_model()
model.foreach(gui_iterate_over_data)
 
 
def gui_filter_visor(self, entry):
visor = self.gui.get_widget('visor')
visible_filter = visor.get_visible_filter()
visible_filter.refilter()
visor.update_total_sapnotes_count(len(visible_filter))
 
 
def gui_refresh_view(self, button, view=None):
self.debug("Refresh & clear view (II). View(%s)" % (view))
window = self.gui.get_widget('gtk_app_window_main')
viewmenu = self.gui.get_widget('viewmenu')
if view is None:
view = viewmenu.get_view()
 
if view is not None:
viewlabel = self.gui.get_widget('gtk_label_current_view')
name = "<b>%-10s</b>" % view.capitalize()
viewlabel.set_markup(name)
viewmenu.set_view(view)
popover = self.gui.get_widget('gtk_popover_button_menu_views')
popover.hide()
 
 
def gui_toggle_menu_view(self, obj):
paned = self.gui.get_widget('gtk_vbox_container_menu_view')
button = self.gui.get_widget('gtk_toogletoolbutton_menu_view')
 
if isinstance(obj, Gtk.ToggleToolButton):
if button.get_active():
paned.show_all()
else:
paned.hide()
elif isinstance(obj, bool):
if obj == True:
# ~ paned.show_all()
button.set_active(True)
else:
# ~ paned.hide()
button.set_active(False)
 
 
def gui_toggle_fullscreen(self, button):
icon_container = self.gui.get_widget('gtk_box_container_icon_fullscreen')
icon_fullscreen = self.im.get_new_image_icon('basico-fullscreen', 24, 24)
icon_unfullscreen = self.im.get_new_image_icon('basico-unfullscreen', 24, 24)
active = button.get_active()
ui = self.gui.get_window()
window = ui.get_window()
if active:
self.gui.swap_widget(icon_container, icon_unfullscreen)
window.fullscreen()
else:
self.gui.swap_widget(icon_container, icon_fullscreen)
window.unfullscreen()
 
 
def action_annotation_edit(self, aid):
self.gui_annotation_widget_show(None, aid, 'edit')
 
 
def action_annotation_duplicate(self, *args):
self.debug("ACTION-DUPLICATE: %s" % args)
 
 
def action_annotation_delete(self, *args):
statusbar = self.gui.get_widget('widget_statusbar')
visor = self.gui.get_widget('visor')
widget_annotation = self.gui.get_widget('widget_annotation')
aid = widget_annotation.get_aid_from_widget()
self.annot.delete(aid)
self.gui_annotation_widget_clear()
self.uif.set_widget_visibility('gtk_vbox_container_annotations', False)
# ~ visor.refresh()
visor.populate_annotations()
statusbar.message("Annotation deleted")
 
 
def action_annotation_accept(self, button, sid):
statusbar = self.gui.get_widget('widget_statusbar')
widget_annotation = self.gui.get_widget('widget_annotation')
visor = self.gui.get_widget('visor')
 
aid = widget_annotation.get_aid_from_widget()
 
annotation = widget_annotation.get_metadata_from_widget(aid)
 
if self.annot.is_valid(aid):
self.annot.update(annotation)
statusbar.message('<b>Anotation edited</b>')
self.debug("Updated annotation: %s" % aid)
else:
self.annot.create(annotation)
statusbar.message('<b>New annotation added to SAP Note %d</b>' % int(sid))
self.debug("Created annotation: %s" % aid)
 
self.gui_annotation_widget_clear()
visor.populate_annotations()
 
 
def action_annotation_cancel(self, *args):
statusbar = self.gui.get_widget('widget_statusbar')
self.gui_annotation_widget_clear()
self.uif.set_widget_visibility('gtk_vbox_container_annotations', False)
statusbar.message('<b>Annotation canceled</b>')
 
 
def action_backup(self, *args):
TIMESTAMP = self.utils.timestamp()
SOURCE_DIRECTORY = LPATH['DB']
TARGET_DIRECTORY = LPATH['EXPORT']
TARGET_FILE = TARGET_DIRECTORY + 'basico-' + TIMESTAMP
self.utils.zip(TARGET_FILE, SOURCE_DIRECTORY)
 
 
def gui_annotation_widget_clear(self):
a_wdg_timestamp = self.gui.get_widget('gtk_label_human_timestamp')
a_wdg_title = self.gui.get_widget('gtk_entry_annotation_title')
a_wdg_type = self.gui.get_widget('gtk_combobox_annotation_type')
a_wdg_text = self.gui.get_widget('gtk_textview_annotation_text')
a_wdg_link = self.gui.get_widget('gtk_entry_annotation_link')
a_wdg_link_type = self.gui.get_widget('gtk_combobox_annotation_link_type')
 
a_wdg_timestamp.set_text('')
a_wdg_title.set_text('')
textbuffer = a_wdg_text.get_buffer()
textbuffer.set_text('')
a_wdg_link.set_text('')
self.uif.set_widget_visibility('gtk_vbox_container_annotations', False)
/branches/BR-0.3/basico/core/collections.py
0,0 → 1,111
#!/usr/bin/python
# -*- coding: utf-8 -*-
# File: collections.py
# Author: Tomás Vírseda
# License: GPL v3
# Description: Collections service
 
import os
import json
import uuid
 
from basico.core.env import FILE
from basico.core.service import Service
 
HEADER = ['id', 'title']
COLLECTIONS_FILE = FILE['COLLECTIONS']
 
 
class Collections(Service):
def initialize(self):
'''
Setup Collections Service
'''
self.get_services()
self.clts = {}
self.load()
 
 
def get_services(self):
self.gui = self.app.get_service("GUI")
self.uif = self.app.get_service("UIF")
self.cb = self.app.get_service('Callbacks')
self.im = self.app.get_service('IM')
 
 
def load(self):
try:
with open(COLLECTIONS_FILE, 'r') as ft:
self.clts = json.load(ft)
self.debug ("Loaded %d collections" % len(self.clts))
except Exception as error:
self.debug("Collections file not found. Creating a new one.")
self.save()
 
 
def save(self, collections={}):
if len(collections) == 0:
collections = self.clts
with open(COLLECTIONS_FILE, 'w') as ft:
json.dump(collections, ft)
self.debug ("Saved %d collections" % (len(collections)))
 
 
def get_all(self):
return self.clts
 
 
def get_collections_name(self):
names = []
for tid in self.clts:
names.append(self.clts[tid])
return names
 
 
def get_collections_id(self):
tids = []
for tid in self.clts:
tids.append(tid)
return tids
 
 
def create(self, entry):
tid = str(uuid.uuid4())
name = entry.get_text()
 
if len(name) == 0:
return
 
tid_exists = tid in self.get_collections_id()
name_exists = name in self.get_collections_name()
 
if tid_exists:
self.debug('Collision? :)')
self.create(entry)
else:
if name_exists:
return
else:
self.clts[tid] = name
self.debug("Created collection: %s" % name)
self.save()
 
 
def delete(self, tid):
try:
name = self.get_name(tid)
del(self.clts[tid])
self.debug("Deleted Collection: %s" % name)
self.save()
return True
except KeyError:
self.debug("You can't delete a non existent collection...")
except Exception as error:
self.debug("Error deleting collection: %s" % error)
self.debug(self.clts)
raise
 
 
def get_name(self, tid):
return self.clts[tid]
 
/branches/BR-0.3/basico/core/database.py
0,0 → 1,342
#!/usr/bin/python
# -*- coding: utf-8 -*-
# File: database.py
# Author: Tomás Vírseda
# License: GPL v3
# Description: Database service
 
import os
from os.path import basename
import json
import glob
from cgi import escape
 
from basico.core.env import LPATH
from basico.core.service import Service
 
SEP = os.path.sep
 
class Database(Service):
def initialize(self):
'''
Setup AppLogic Service
'''
self.sapnotes = {}
self.stats = {}
self.stats['maincomp'] = {}
self.stats['cats'] = {}
self.stats['component'] = set()
self.stats['category'] = set()
self.stats['priority'] = set()
self.stats['type'] = set()
self.stats['version'] = set()
#FIXME self.stats['releaseon'] = set()
 
 
def store(self, sapnote, html):
CACHE_DIR = self.get_var('CACHE', 'local')
FSAPNOTE = CACHE_DIR + sapnote + '.xml'
 
try:
f = open(FSAPNOTE, 'w')
f.write(html)
f.close()
self.debug("\tSAP Note %s stored in %s" % (sapnote, FSAPNOTE))
except Exception as error:
self.log.error(error)
 
 
def get_sapnote_content(self, sid):
CACHE_DIR = self.get_var('CACHE', 'local')
FSAPNOTE = CACHE_DIR + sid + '.xml'
content = open(FSAPNOTE, 'r').read()
return content
 
 
def is_saved(self, sid):
metadata = self.get_sapnote_metadata(sid)
if metadata is None:
return False
else:
return True
 
 
def is_valid(self, sid):
valid = True
for ch in sid:
if not ch.isdigit():
return False
return valid
 
 
def is_stored(self, sid):
CACHE_DIR = self.get_var('CACHE', 'local')
fsapnote = CACHE_DIR + sid + '.xml'
stored = os.path.exists(fsapnote)
 
return stored
 
 
def build_stats(self, bag=None):
if bag is None:
bag = self.sapnotes
self.dstats = {}
self.compstats = {}
allcollections = set()
 
for sid in bag:
try:
collections = self.sapnotes[sid]['collections']
for collection in collections:
allcollections.add(collection)
except: pass
 
#COMMENT Components
compkey = self.sapnotes[sid]['componentkey']
comptxt = self.sapnotes[sid]['componenttxt']
component = escape("%s (%s)" % (compkey, comptxt))
sep = compkey.find('-')
if sep > 0:
maincomp = compkey[0:sep]
else:
maincomp = compkey
 
#COMMENT Categories
category = escape(self.sapnotes[sid]['category'])
try:
cont = self.stats['cats'][category]
self.stats['cats'][category] = cont + 1
except:
self.stats['cats'][category] = 1
 
#COMMENT Build all (sub)keys from given component key
#COMMENT useful for display stats with pygal
compkeys = compkey.split('-')
total = len(compkeys)
key = ''
i = 0
for subkey in compkeys:
key = key + '-' + subkey
if key[:1] == '-':
key = key[1:]
 
# update stats
try:
count = self.compstats[key]
self.compstats[key] = count + 1
except Exception as error:
self.compstats[key] = 1
 
try:
cont = self.stats['maincomp'][maincomp]
self.stats['maincomp'][maincomp] = cont + 1
except:
self.stats['maincomp'][maincomp] = 1
 
category = escape(self.sapnotes[sid]['category'])
priority = escape(self.sapnotes[sid]['priority'])
ntype = escape(self.sapnotes[sid]['type'])
version = escape(self.sapnotes[sid]['version'])
releaseon = escape(self.sapnotes[sid]['releaseon'])
self.stats['component'].add(component)
self.stats['category'].add(category)
self.stats['priority'].add(priority)
self.stats['type'].add(ntype)
self.stats['version'].add(version)
#FIXME self.stats['releaseon'].add(releaseon)
#FIXME self.stats[''].add(version)
 
 
def add(self, sapnote):
sid = sapnote['id']
self.sapnotes[sid] = sapnote
 
 
def add_list(self, sapnotes):
for sapnote in sapnotes:
self.add(sapnote)
 
 
def get_notes(self):
'''
Return all sapnotes
'''
return self.sapnotes
 
 
def get_notes_by_node(self, key, value):
'''
Return a set of sapnotes which matches with an specific key/value
'''
bag = set()
sapnotes = self.get_notes()
for sapnote in sapnotes:
if key.startswith('component') or \
key in ['category', 'priority', 'type']:
if sapnotes[sapnote][key].startswith(value):
bag.add(sapnote)
elif key == 'collection':
if len(value) == 0:
try:
collections = sapnotes[sapnote]['collections']
if len(collections) > 0:
bag.add(sapnote)
except:
pass
else:
try:
if value in sapnotes[sapnote]['collections']:
bag.add(sapnote)
except:
pass
elif key in ['date-year', 'date-month', 'date-day']:
if len(value) == 6:
value = value[0:4] + '-' + value[4:6]
elif len(value) == 8:
value = value[0:4] + '-' + value[4:6] + '-' + value[6:8]
 
if sapnotes[sapnote]['feedupdate'].startswith(value):
bag.add(sapnote)
else:
pass
 
return bag
 
 
def get_total(self):
'''
Return total sapnotes
'''
return len(self.sapnotes)
 
 
def load_notes(self):
'''
If notes.json exists, load notes
'''
try:
fnotes = self.get_file('SAP')
with open(fnotes, 'r') as fp:
self.sapnotes = json.load(fp)
self.debug ("Loaded %d notes" % len(self.sapnotes))
except Exception as error:
self.debug("SAP Notes database not found. Creating a new one")
self.save_notes()
 
 
def get_sapnote_metadata(self, sid):
sid = self.normalize_sid(sid)
 
try:
return self.sapnotes[sid]
except KeyError as error:
self.debug("SAP Note %s doesn't exist in the database" % sid)
return None
 
 
def get_title(self, sid):
title = ''
metadata = self.get_sapnote_metadata(sid)
if metadata is not None:
title = metadata['title']
 
return title
 
 
def save_notes(self, bag={}, export_path=None):
'''
Save SAP Notes to json database file
'''
if export_path is None:
export_path = self.get_file('SAP')
 
if len(bag) == 0:
bag = self.get_notes()
 
fdb = open(export_path, 'w')
json.dump(bag, fdb)
fdb.close()
self.debug ("Saved %d notes to %s" % (len(bag), export_path))
 
 
def set_bookmark(self, lsid):
for sid in lsid:
self.sapnotes[sid]['bookmark'] = True
self.debug("SAP Note %s bookmarked: True" % sid)
self.save_notes()
 
 
def set_no_bookmark(self, lsid):
for sid in lsid:
self.sapnotes[sid]['bookmark'] = False
self.debug("SAP Note %s bookmarked: False" % sid)
self.save_notes()
 
 
def is_bookmark(self, sapnote):
try:
return self.sapnotes[sapnote]['bookmark']
except:
return False
 
def get_collections(self, sid):
try:
return self.sapnotes[sid]['collections']
except:
return []
 
 
def search(self, term):
bag = []
 
for sid in self.get_notes():
values = []
sapnote = self.sapnotes[sid]
for key in sapnote:
# ~ self.debug(key)
values.append(str(sapnote[key]))
text = ' '.join(values)
if term.upper() in text.upper():
bag.append(sapnote['id'])
self.debug("Found term '%s' in %d SAP Notes" % (term, len(bag)))
return bag
 
 
def normalize_sid(self, sid):
if isinstance(sid, int):
sid = "0"*(10 - len(str(sid))) + str(sid)
elif isinstance(sid, str):
sid = "0"*(10 - len(sid)) + sid
 
return sid
 
 
def set_collections(self, sid, collections):
if sid != '0000000000':
sid = self.normalize_sid(sid)
self.sapnotes[sid]['collections'] = collections
self.debug("SAP Note %s in collections: %s" % (sid, collections))
self.save_notes()
 
 
def delete(self, sapnote):
deleted = False
try:
del (self.sapnotes[sapnote])
deleted = True
self.debug("SAP Note %s deleted" % sapnote)
self.save_notes()
except:
deleted = False
 
return deleted
 
 
def run(self):
self.load_notes()
self.build_stats()
 
 
def end(self):
self.save_notes()
 
/branches/BR-0.3/basico/core/driver.py
0,0 → 1,98
#!/usr/bin/python
# -*- coding: utf-8 -*-
# File: driver.py
# Author: Tomás Vírseda
# License: GPL v3
# Description: Selenium Driver service
 
import os
import selenium
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import WebDriverException
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.firefox.options import Options
 
from basico.core.service import Service
 
GECKODRIVER_URL = "https://github.com/mozilla/geckodriver/releases/download/v0.15.0/geckodriver-v0.15.0-linux64.tar.gz"
 
 
class SeleniumDriver(Service):
def open(self):
'''
In order to have selenium working with Firefox and be able to
get SAP Notes from launchpad.support.sap.com you must:
1. Use a browser certificate (SAP Passport) in order to avoid
renewed logons.
You can apply for it at:
https://support.sap.com/support-programs-services/about/getting-started/passport.html
2. Get certificate and import it into Firefox.
Open menu -> Preferences -> Advanced -> View Certificates
-> Your Certificates -> Import
3. Trust this certificate (auto select)
4. Check it. Visit some SAP Note url in Launchpad.
No credentials will be asked.
Launchpad must load target page successfully.
'''
 
utils = self.get_service('Utils')
options = Options()
# ~ Uncomment this line to set Firefox to headless
options.add_argument('--headless')
FIREFOX_PROFILE_DIR = utils.get_firefox_profile_dir()
FIREFOX_PROFILE = webdriver.FirefoxProfile(FIREFOX_PROFILE_DIR)
driver = webdriver.Firefox(firefox_profile=FIREFOX_PROFILE, firefox_options=options)
self.debug("Webdriver initialited")
return driver
 
 
def close(self, driver):
driver.quit()
self.debug("Webdriver closed")
driver = None
 
 
 
def load(self, driver, URL):
driver.get(URL)
return driver
 
 
def check(self):
"""
Check gecko webdriver
You must install geckodriver. It is mandatory
Yo can download it from:
https://github.com/mozilla/geckodriver/
Then, extract the binary and copy it to somewhere in your $PATH.
If OS is Linux: /usr/local/bin/geckodriver
If OS is Windows: C:\Windows\System32 or elsewhere.
 
Basico will try to do it for you.
"""
utils = self.get_service('Utils')
 
# First, add BASICO OPT Path to $PATH
GECKO_INSTALL_DIR = self.get_var('DRIVERS', 'local')
os.environ["PATH"] += os.pathsep + GECKO_INSTALL_DIR
# Then, look for Geckodriver
GECKODRIVER = utils.which('geckodriver')
 
if not GECKODRIVER:
self.debug("Attempting to download Gecko driver and install it.")
utils.install_geckodriver()
 
GECKODRIVER = utils.which('geckodriver')
if GECKODRIVER is None:
self.log.warning("Gecko driver not found.")
return False
else:
self.debug("Gecko Webdriver found in: %s" % GECKODRIVER)
return True
 
 
/branches/BR-0.3/basico/core/env.py
0,0 → 1,67
#!/usr/bin/python
# -*- coding: utf-8 -*-
# File: env.py
# Author: Tomás Vírseda
# License: GPL v3
# Description: Environment variables
 
import sys
import os
from os.path import abspath, sep as SEP
 
ROOT = abspath(sys.modules[__name__].__file__ + "/../../")
USER_DIR = os.path.expanduser('~')
 
# App Info
APP = {}
APP['short'] = "basico"
APP['name'] = "SAP Notes Manager for SAP Consultants"
APP['license'] = "The code is licensed under the terms of the GPL v3\nso you're free to grab, extend, improve and fork the code\nas you want"
APP['desc'] = "SAP Notes Manager for SAP Consultants\n\nThe code is licensed under the terms of the GPL v3 so you're free to grab, extend, improve and fork the code as you want"
APP['version'] = "0.3"
APP['authors'] = ["Tomás Vírseda <tomasvirseda@gmail.com>"]
APP['documenters'] = ["Tomás Vírseda <tomasvirseda@gmail.com>"]
APP['email'] = "t00m@t00mlabs.net"
 
 
# Local paths
LPATH = {}
LPATH['ROOT'] = USER_DIR + SEP + '.basico' + SEP
LPATH['ETC'] = LPATH['ROOT'] + 'etc' + SEP
LPATH['VAR'] = LPATH['ROOT'] + 'var' + SEP
LPATH['PLUGINS'] = LPATH['VAR'] + 'plugins' + SEP
LPATH['LOG'] = LPATH['VAR'] + 'logs' + SEP
LPATH['TMP'] = LPATH['VAR'] + 'tmp' + SEP
LPATH['DB'] = LPATH['VAR'] + 'db' + SEP
LPATH['CACHE'] = LPATH['DB'] + 'cache' + SEP
LPATH['ANNOTATIONS'] = LPATH['DB'] + 'annotations' + SEP
LPATH['RESOURCES'] = LPATH['DB'] + 'resources' + SEP
LPATH['WWW'] = LPATH['VAR'] + 'www' + SEP
LPATH['EXPORT'] = LPATH['VAR'] + 'export' + SEP
LPATH['BACKUP'] = LPATH['EXPORT'] + 'backup' + SEP
LPATH['PDF'] = LPATH['EXPORT'] + 'pdf' + SEP
LPATH['OPT'] = LPATH['ROOT'] + 'opt' + SEP
LPATH['DRIVERS'] = LPATH['OPT'] + 'webdrivers' + SEP
 
 
# Global paths
GPATH = {}
GPATH['ROOT'] = ROOT
GPATH['DATA'] = GPATH['ROOT'] + SEP + 'data' + SEP
GPATH['UI'] = GPATH['DATA'] + 'ui' + SEP
GPATH['ICONS'] = GPATH['DATA'] + 'icons' + SEP
GPATH['PLUGINS'] = GPATH['DATA'] + 'plugins' + SEP
GPATH['SHARE'] = GPATH['DATA'] + 'share' + SEP
GPATH['DOC'] = GPATH['SHARE'] + 'docs' + SEP
GPATH['RES'] = GPATH['DATA'] + 'res' + SEP
 
# Configuration, SAP Notes Database and Log files
FILE = {}
FILE['COLLECTIONS'] = LPATH['DB'] + 'collections.json'
FILE['CNF'] = LPATH['ETC'] + 'basico.ini'
FILE['SAP'] = LPATH['DB'] + 'sapnotes.json'
FILE['LOG'] = LPATH['LOG'] + 'basico.log'
FILE['CREDITS'] = GPATH['DOC'] + 'CREDITS'
 
# APP STATS
STATS = {}
Property changes:
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: branches/BR-0.3/basico/core/gui.py
===================================================================
--- branches/BR-0.3/basico/core/gui.py (nonexistent)
+++ branches/BR-0.3/basico/core/gui.py (revision 233)
@@ -0,0 +1,137 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# File: gui.py
+# Author: Tomás Vírseda
+# License: GPL v3
+# Description: GUI module
+
+import os
+import sys
+import subprocess
+
+from datetime import datetime
+
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+from gi.repository import Gdk
+from gi.repository.GdkPixbuf import Pixbuf
+from gi.repository import Pango
+from gi.repository import GLib
+from gi.repository import GObject
+from gi.repository import Gio
+
+from basico.core.service import Service
+from basico.core.uiapp import UIApp
+from basico.core.env import STATS
+
+
+class GUI(Service):
+ def initialize(self):
+ '''
+ Setup GUI Service
+ '''
+ # Invoke services
+ self.sap = self.app.get_service('SAP')
+
+ # Setup caches
+ self.widgets = {} # Widget name : Object
+ self.keys = {} # Key : Value; keys: doctype, property, values
+
+
+ def run(self):
+ '''
+ Let GUI service start
+ '''
+ self.debug("Setting up GUI")
+ GObject.threads_init()
+ self.sap.run()
+ self.ui = UIApp(self.app)
+ DEBUG_END_TIME = datetime.now()
+ STATS['BASICO_STARTUP_END'] = datetime.now()
+ self.debug("Startup duration: %s" % (STATS['BASICO_STARTUP_END'] - STATS['BASICO_STARTUP_START']))
+ self.ui.run()
+
+
+ def quit(self, *args):
+ '''
+ GUI destroyed
+ '''
+ self.app.stop()
+
+
+ def end(self):
+ '''
+ End GUI Service
+ '''
+ self.debug("GUI terminated")
+ self.ui.quit()
+
+
+ def swap_widget(self, parent, widget):
+ '''
+ Swap widget inside a container
+ '''
+ try:
+ child = parent.get_children()[0]
+ parent.remove(child)
+ # ~ del (child)
+ parent.add(widget)
+ except IndexError:
+ parent.add(widget)
+ except Exception as error:
+ self.print_traceback()
+ raise
+
+ widget.show_all()
+
+
+ def get_key(self, key):
+ '''
+ Return current value from var cache
+ '''
+ return self.keys[key]
+
+
+ def set_key(self, key, value):
+ '''
+ Set current value for var cache
+ '''
+ self.keys[key] = value
+
+
+ def add_widget(self, name, obj=None):
+ '''
+ Add widget to cache
+ '''
+ try:
+ if obj is not None:
+ self.widgets[name] = obj
+ return self.widgets[name]
+ else:
+ return None
+ except Exception as error:
+ self.debug(error)
+ return None
+
+
+ def get_widget(self, name):
+ '''
+ Return widget from cache
+ '''
+ try:
+ return self.widgets[name]
+ except KeyError:
+ return self.add_widget(name)
+
+
+ def get_widgets(self):
+ return self.widgets
+
+
+ def get_app(self):
+ return self.ui
+
+
+ def get_window(self):
+ return self.ui.get_window()
/branches/BR-0.3/basico/core/gui.py
Property changes:
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: branches/BR-0.3/basico/core/iconmanager.py
===================================================================
--- branches/BR-0.3/basico/core/iconmanager.py (nonexistent)
+++ branches/BR-0.3/basico/core/iconmanager.py (revision 233)
@@ -0,0 +1,89 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# File: menus.py
+# Author: Tomás Vírseda
+# License: GPL v3
+# Description: Icon manager service
+
+import os
+
+import pkg_resources
+
+from gi.repository import Gtk
+from gi.repository import Gio
+from gi.repository import Pango
+from gi.repository.GdkPixbuf import Pixbuf
+
+from basico.core.service import Service
+
+
+class IconManager(Service):
+ def initialize(self):
+ APP_DIR_ICONS = self.app.get_var('ICONS')
+ self.debug("Attached Icons DIR: %s" % APP_DIR_ICONS)
+ self.icondict = {}
+ self.imgdict = {}
+ self.theme = Gtk.IconTheme.get_default()
+ self.theme.prepend_search_path (APP_DIR_ICONS)
+
+
+ def get_themed_icon(self, icon_name):
+ APP_DIR_ICONS = self.app.get_var('ICONS')
+ ICON = APP_DIR_ICONS + icon_name + '.png'
+ icon = Gio.ThemedIcon.new(ICON)
+
+ return icon
+
+
+
+ def get_icon(self, name, width=24, height=24):
+ key = "%s-%d-%d" % (name, width, height)
+
+ # Get icon from cache if exists or add a new one
+ try:
+ icon = self.icondict[key]
+ except:
+ # ~ self.debug("Icon Key '%s' not found", key)
+ iconinfo = self.theme.lookup_icon(name, width, Gtk.IconLookupFlags.GENERIC_FALLBACK)
+ icon = iconinfo.load_icon()
+ self.icondict[key] = icon
+
+ return icon
+
+
+ def get_pixbuf_icon(self, name, width=36, height=36):
+ key = "%s-%d-%d" % (name, width, height)
+
+ # Get icon from cache if exists or add a new one
+ try:
+ icon = self.icondict[key]
+ except Exception as error:
+ icon = None
+ if name in self.theme.list_icons():
+ icon = self.theme.load_icon(name, width, Gtk.IconLookupFlags.GENERIC_FALLBACK)
+ self.icondict[key] = icon
+
+ return icon
+
+
+ def get_new_image_icon(self, name, width=36, height=36):
+ pixbuf = self.get_pixbuf_icon(name, width, height)
+ icon = Gtk.Image()
+ icon.set_from_pixbuf(pixbuf)
+
+ return icon
+
+
+ def get_image_icon(self, name, width=36, height=36):
+ key = "%s-%d-%d" % (name, width, height)
+ try:
+ icon = self.imgdict[key]
+ except Exception as error:
+ icon = None
+ if name in self.theme.list_icons():
+ pixbuf = self.theme.load_icon(name, width, Gtk.IconLookupFlags.GENERIC_FALLBACK)
+ icon = Gtk.Image()
+ icon.set_from_pixbuf(pixbuf)
+ self.imgdict[key] = icon
+
+ return icon
Index: branches/BR-0.3/basico/core/log.py
===================================================================
--- branches/BR-0.3/basico/core/log.py (nonexistent)
+++ branches/BR-0.3/basico/core/log.py (revision 233)
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# File: log.py
+# Author: Tomás Vírseda
+# License: GPL v3
+# Description: log service
+
+from os.path import sep as SEP
+import logging
+import inspect
+
+def get_logger(name, LOG_FILE):
+ """Returns a new logger with personalized.
+ @param name: logger name
+ """
+ log = logging.getLogger(name)
+ log.setLevel(logging.DEBUG)
+
+ ## Redirect log to stdout
+ formatter = logging.Formatter("%(levelname)7s | %(asctime)s | %(message)s")
+ # ~ formatter = logging.Formatter("%(levelname)7s | %(lineno)4d |%(name)15s | %(asctime)s | %(message)s")
+ ch = logging.StreamHandler() # Create console handler and set level to debug
+ ch.setLevel(logging.DEBUG) # Set logging devel
+ ch.setFormatter(formatter) # add formatter to console handler
+ log.addHandler(ch) # add console handler to logger
+
+ #FIXME Redirect log to file
+ fh = logging.FileHandler(LOG_FILE)
+ fh.setFormatter(formatter)
+ fh.setLevel(logging.DEBUG) # Set logging devel
+ log.addHandler(fh) # add file handler to logger
+
+ return log
/branches/BR-0.3/basico/core/log.py
Property changes:
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: branches/BR-0.3/basico/core/notify.py
===================================================================
--- branches/BR-0.3/basico/core/notify.py (nonexistent)
+++ branches/BR-0.3/basico/core/notify.py (revision 233)
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# File: notify.py
+# Author: Tomás Vírseda
+# License: GPL v3
+# Description: notifications service
+
+import sys
+import gi
+
+try:
+ gi.require_version('Notify', '0.7')
+ from gi.repository import Notify
+ NOTIFY_INSTALLED = True
+except:
+ NOTIFY_INSTALLED = False
+
+from basico.core.service import Service
+
+
+class Notification(Service):
+ def initialize(self):
+ if NOTIFY_INSTALLED:
+ Notify.init('Basico')
+
+ def show(self, module, message, icon_name):
+ if NOTIFY_INSTALLED:
+ icon = "dialog-%s" % icon_name # information | question | warning | error
+ notification = Notify.Notification.new (module, message, icon)
+ notification.show()
+ else:
+ return
+
Index: branches/BR-0.3/basico/core/sap.py
===================================================================
--- branches/BR-0.3/basico/core/sap.py (nonexistent)
+++ branches/BR-0.3/basico/core/sap.py (revision 233)
@@ -0,0 +1,262 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# File: sap.py
+# Author: Tomás Vírseda
+# License: GPL v3
+# Description: SAP service
+
+import time
+import traceback
+from shutil import which
+# ~ from datetime.datetime import now
+
+from basico.core.service import Service
+from basico.core.env import LPATH
+
+# Default settings for SAP module
+LOGIN_PAGE_URL = "https://accounts.sap.com"
+LOGOUT_PAGE_URL = "https://accounts.sap.com/ui/logout"
+ODATA_NOTE_URL = "https://launchpad.support.sap.com/services/odata/svt/snogwscorr/TrunkSet(SapNotesNumber='%s',Version='0',Language='E')"
+ODATA_NOTE_URL_LONGTEXT = "https://launchpad.support.sap.com/services/odata/svt/snogwscorr/TrunkSet(SapNotesNumber='%s',Version='0',Language='E')?$expand=LongText"
+SAP_NOTE_URL = "https://launchpad.support.sap.com/#/notes/%s"
+SAP_NOTE_URL_PDF = "https://launchpad.support.sap.com/services/pdf/notes/%s/E"
+TIMEOUT = 10
+
+DSTART = None
+DEND = None
+DYNTMOUT = 0
+AVERAGE = 0
+
+"""
+<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/RefBy"
+<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/RefTo"
+<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/CorrIns"
+<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/Patch"
+<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/Sp"
+<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/SoftCom"
+<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/Attach"
+<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/LongText"
+<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/Languages"
+<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/SideCau"
+<link href="TrunkSet(SapNotesNumber='0002258035',Version='4',Language='E')/SideSol"
+
+"""
+
+
+class SAP(Service):
+ def initialize(self):
+ '''
+ Setup AppLogic Service
+ '''
+ self.settings = self.get_service('Settings')
+ self.__init_config_section()
+
+
+ def __init_config_section(self):
+ settings = self.settings.load()
+ settings[self.section]
+ try:
+ settings[self.section]['LOGIN_PAGE_URL']
+ except:
+ settings[self.section]['LOGIN_PAGE_URL'] = LOGIN_PAGE_URL
+
+ try:
+ settings[self.section]['LOGOUT_PAGE_URL']
+ except:
+ settings[self.section]['LOGOUT_PAGE_URL'] = LOGOUT_PAGE_URL
+
+ try:
+ settings[self.section]['ODATA_NOTE_URL']
+ except:
+ settings[self.section]['ODATA_NOTE_URL'] = ODATA_NOTE_URL
+
+ try:
+ settings[self.section]['ODATA_NOTE_URL_LONGTEXT']
+ except:
+ settings[self.section]['ODATA_NOTE_URL_LONGTEXT'] = ODATA_NOTE_URL_LONGTEXT
+
+ try:
+ settings[self.section]['SAP_NOTE_URL']
+ except:
+ settings[self.section]['SAP_NOTE_URL'] = SAP_NOTE_URL
+
+ try:
+ settings[self.section]['SAP_NOTE_URL_PDF']
+ except:
+ settings[self.section]['SAP_NOTE_URL_PDF'] = SAP_NOTE_URL_PDF
+
+ try:
+ settings[self.section]['TIMEOUT']
+ except:
+ settings[self.section]['TIMEOUT'] = TIMEOUT
+
+ self.settings.save(settings)
+
+
+ def analyze_sapnote_metadata(self, sid, content):
+ '''
+ Get metadata details from SAP Note
+ '''
+ try:
+ utils = self.get_service('Utils')
+ f = utils.feedparser_parse(content)
+ sid = f.entries[0].d_sapnotesnumber
+ sapnote = {}
+ sapnote['id'] = sid
+ sapnote['componentkey'] = f.entries[0].d_componentkey
+ comptxt = f.entries[0].d_componenttext
+ if comptxt == "Please use note 1433157 for finding the right component":
+ comptxt = ""
+ sapnote['componenttxt'] = comptxt
+ sapnote['category'] = f.entries[0].d_category_detail['value']
+ sapnote['language'] = f.entries[0].d_languagetext_detail['value']
+ sapnote['title'] = f.entries[0].d_title_detail['value']
+ sapnote['priority'] = f.entries[0].d_priority_detail['value']
+ sapnote['releaseon'] = f.entries[0].d_releasedon
+ sapnote['type'] = f.entries[0].d_type_detail['value']
+ sapnote['version'] = f.entries[0].d_version_detail['value']
+ sapnote['feedupdate'] = f.entries[0].updated
+ sapnote['bookmark'] = False
+ self.debug ("\tSAP Note %s analyzed successfully" % sid)
+ except Exception as error:
+ sapnote = {}
+ self.log.error("\tError while analyzing data for SAP Note %s" % sid)
+
+ return sapnote
+
+
+ def analyze_sapnote_metadata_details(self, sid, content):
+ '''
+ Get metadata details from SAP Note
+ '''
+ try:
+ utils = self.get_service('Utils')
+ f = utils.feedparser_parse(content)
+ sid = f.entries[0].d_sapnotesnumber
+ sapnote = {}
+ sapnote['id'] = sid
+ sapnote['componentkey'] = f.entries[0].d_componentkey
+ comptxt = f.entries[0].d_componenttext
+ if comptxt == "Please use note 1433157 for finding the right component":
+ comptxt = ""
+ sapnote['componenttxt'] = comptxt
+ sapnote['category'] = f.entries[0].d_category_detail['value']
+ sapnote['language'] = f.entries[0].d_languagetext_detail['value']
+ sapnote['title'] = f.entries[0].d_title_detail['value']
+ sapnote['priority'] = f.entries[0].d_priority_detail['value']
+ sapnote['releaseon'] = f.entries[0].d_releasedon
+ sapnote['type'] = f.entries[0].d_type_detail['value']
+ sapnote['version'] = f.entries[0].d_version_detail['value']
+ sapnote['feedupdate'] = f.entries[0].updated
+ sapnote['bookmark'] = False
+ self.debug ("\tSAP Note %s analyzed successfully" % sid)
+ except Exception as error:
+ sapnote = {}
+ self.log.error("\tError while analyzing data for SAP Note %s" % sid)
+
+ return sapnote
+
+ def fetch(self, driver, sid):
+ db = self.get_service('DB')
+ valid = False
+
+ if not db.is_stored(sid):
+ self.debug("%3d/%3d - SAP Note %s must be downloaded" % (self.notes_fetched+1, self.notes_total, sid))
+ # ~ DSTART = datetime.now()
+ content = self.download(driver, sid)
+ if len(content) > 0:
+ self.debug("%3d/%3d - SAP Note %s fetched" % (self.notes_fetched+1, self.notes_total, sid))
+ else:
+ self.debug("%3d/%3d - SAP Note %s not feched" % (self.notes_fetched+1, self.notes_total, sid))
+ else:
+ self.debug("%3d/%3d - SAP Note %s will be analyzed again" % (self.notes_fetched+1, self.notes_total, sid))
+ content = db.get_sapnote_content(sid)
+
+ self.fetched()
+
+ sapnote = self.analyze_sapnote_metadata_details(sid, content)
+ if len(sapnote) > 0:
+ db = self.get_service('DB')
+ db.add(sapnote)
+ db.store(sid, content)
+ valid = True
+ # ~ DEND = datetime.now()
+ # ~ DYNTMOUT = DEND - DSTART
+ # ~ self.debug(DYNTMOUT.seconds)
+ return valid, sid
+
+
+ def start_fetching(self, total):
+ self.notes_fetched = 0
+ self.notes_total = total
+
+
+ def fetched(self):
+ self.notes_fetched += 1
+
+
+ def stop_fetching(self):
+ self.notes_fetched = 0
+ self.notes_total = 0
+
+ def browse(self, sid):
+ webdriver = self.get_service('Driver')
+ driver = webdriver.open()
+ url = SAP_NOTE_URL % sid
+ webdriver.load(driver, url)
+
+
+ def download(self, driver, sapnote=None):
+ try:
+ webdriver = self.get_service('Driver')
+ ODATA_NOTE_URL = self.settings.get('SAP', 'ODATA_NOTE_URL')
+ timeout = self.settings.get('SAP', 'TIMEOUT')
+ self.debug("Downloading SAP Note %s" % sapnote)
+ browser = webdriver.load(driver, ODATA_NOTE_URL % sapnote)
+ time.sleep(timeout)
+ content = browser.page_source
+ fsn = LPATH['CACHE'] + sapnote + '.xml'
+ with open(fsn, 'w') as fxml:
+ fxml.write(content)
+ except Exception as error:
+ self.log.error(error)
+ content = ''
+
+ return content
+
+
+ def set_bookmark(self, bag):
+ db = self.get_service('DB')
+ sapnotes = db.get_notes()
+ mylist = []
+ for tid in bag:
+ sid = "0"*(10 - len(tid)) + tid
+ sapnotes[sid]['bookmark'] = True
+ mylist.append(sapnotes[sid])
+ self.debug("SAP Note %s bookmarked" % sid)
+ db.add_list(mylist)
+
+
+ def set_no_bookmark(self, bag):
+ db = self.get_service('DB')
+ sapnotes = db.get_notes()
+ mylist = []
+ for tid in bag:
+ sid = "0"*(10 - len(tid)) + tid
+ sapnotes[sid]['bookmark'] = False
+ mylist.append(sapnotes[sid])
+ self.debug("SAP Note %s unbookmarked" % sid)
+ db.add_list(mylist)
+
+
+ def is_bookmark(self, sapnote):
+ try:
+ return self.sapnotes[sapnote]['bookmark']
+ except:
+ return False
+
+
+ def run(self):
+ db = self.get_service('DB')
+ db.load_notes()
+ db.build_stats()
Index: branches/BR-0.3/basico/core/service.py
===================================================================
--- branches/BR-0.3/basico/core/service.py (nonexistent)
+++ branches/BR-0.3/basico/core/service.py (revision 233)
@@ -0,0 +1,118 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# File: service.py
+# Author: Tomás Vírseda
+# License: GPL v3
+# Description: Service class
+
+import sys
+import traceback as tb
+
+from basico.core.env import FILE
+
+class Service(object):
+ """
+ Service class is the base class for the rest of main classes used in
+ the application.
+ Different modules (GUI, Database, Ask, etc...) share same methods
+ which is useful to start/stop them, simplify logging and, comunicate
+ each other easily.
+ """
+
+ def __init__(self, app=None):
+ """Initialize Service instance
+ @type app: Basico instance
+ @param app: current Basico instance reference
+ """
+ self.started = False
+
+
+ def is_started(self):
+ """Return True or False if service is running / not running
+ """
+ return self.started
+
+
+ def print_traceback(self):
+ self.debug(tb.format_exc())
+
+
+ def start(self, app, logname=None):
+ """Start service.
+ Use initialize for writting a custom init method
+ @type app: basico
+ @param app: basico Class pointer.
+ @type logname: string
+ @param logname: name of associated logger. It is used aswell to
+ identify configuration section name
+ """
+ self.started = True
+ self.app = app
+ self.log = self.app.log
+ self.section = logname
+ self.init_section(self.section)
+
+ try:
+ self.initialize()
+ self.debug("Service %s loaded" % logname)
+ except Exception as error:
+ self.debug (self.get_traceback())
+
+
+ def get_var(self, name, scope='global'):
+ return self.app.get_var(name, scope)
+
+
+ def get_app_info(self, name):
+ return self.app.get_app_info(name)
+
+
+ def get_file(self, name):
+ return self.app.get_file(name)
+
+
+ def end(self):
+ """End service
+ Use finalize for writting a custom end method
+ """
+ self.started = False
+ try:
+ self.finalize()
+ except Exception as error:
+ self.debug (self.get_traceback())
+
+
+ def initialize(self):
+ """Initialize service.
+ All clases derived from Service class must implement this method
+ """
+ pass
+
+
+ def finalize(self):
+ """Finalize service.
+ All clases derived from Service class must implement this method
+ """
+ pass
+
+
+ def init_section(self, section):
+ """Check if section exists in config. If not, create it"""
+ self.settings = self.app.get_service('Settings')
+ config = self.settings.load()
+ try:
+ config[section]
+ except:
+ config[section] = {}
+ self.settings.save(config)
+ self.debug("Section '%s' initialized in config file" % section)
+
+ def debug(self, message):
+ self.app.log.debug("%18s | %s" % (self.__class__.__name__, message))
+
+ def get_traceback(self):
+ return self.app.get_traceback()
+
+
+ def get_service(self, name):
+ return self.app.get_service(name)
/branches/BR-0.3/basico/core/service.py
Property changes:
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: branches/BR-0.3/basico/core/settings.py
===================================================================
--- branches/BR-0.3/basico/core/settings.py (nonexistent)
+++ branches/BR-0.3/basico/core/settings.py (revision 233)
@@ -0,0 +1,72 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# File: settings.py
+# Author: Tomás Vírseda
+# License: GPL v3
+# Description: Settings service
+
+
+from gi.repository import Gtk
+from gi.repository import Gio
+from gi.repository import Pango
+from gi.repository.GdkPixbuf import Pixbuf
+
+import json
+
+from basico.core.service import Service
+from basico.core.env import FILE
+
+#COMMENT Default settings for SAP module
+#COMMENT LOGIN_PAGE_URL = "https://accounts.sap.com"
+#COMMENT LOGOUT_PAGE_URL = "https://accounts.sap.com/ui/logout"
+#COMMENT ODATA_NOTE_URL = "https://launchpad.support.sap.com/services/odata/svt/snogwscorr/TrunkSet(SapNotesNumber='%s',Version='0',Language='E')" #$expand=LongText" #?$expand=LongText,RefTo,RefBy"
+#COMMENT SAP_NOTE_URL = "https://launchpad.support.sap.com/#/notes/%s"
+#COMMENT SAP_NOTE_URL_PDF = "https://launchpad.support.sap.com/services/pdf/notes/%s/E"
+#COMMENT TIMEOUT = 5
+
+
+class Settings(Service):
+ def initialize(self):
+ self.debug("Basico config file: %s" % FILE['CNF'])
+ config = self.load()
+
+
+ def get(self, section, key):
+ config = self.load()
+ try:
+ value = config[section][key]
+ self.debug("[%s][%s] = %s" % (section, key, value))
+ return value
+ except:
+ self.debug("[%s][%s] = None" % (section, key))
+ return None
+
+ def set(self, section, key, value):
+ config = self.load()
+ try:
+ config[section][key] = value
+ self.save(config)
+ except:
+ self.log.error("Setting not saved")
+ self.log.error(self.get_traceback())
+
+
+ def load(self):
+ try:
+ with open(FILE['CNF'], 'r') as fp:
+ config = json.load(fp)
+ except Exception as error:
+ self.debug("Config file not found. Creating a new one")
+ config = {}
+ self.save(config)
+
+ return config
+
+
+ def save(self, config=None):
+ if config is None:
+ self.log.error("A dictionary with all settings must be provided")
+ return
+ with open(FILE['CNF'], 'w') as fp:
+ json.dump(config, fp)
+ self.debug("Settings saved successfully")
Index: branches/BR-0.3/basico/core/uiapp.py
===================================================================
--- branches/BR-0.3/basico/core/uiapp.py (nonexistent)
+++ branches/BR-0.3/basico/core/uiapp.py (revision 233)
@@ -0,0 +1,79 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# File: uiapp.py
+# Author: Tomás Vírseda
+# License: GPL v3
+# Description: Gtk.Application instance
+
+import os
+import sys
+import subprocess
+from datetime import datetime
+
+from gi.repository import Gtk
+from gi.repository import Gdk
+from gi.repository.GdkPixbuf import Pixbuf
+from gi.repository import Pango
+from gi.repository import GLib
+from gi.repository import GObject
+from gi.repository import Gio
+
+from basico.core.service import Service
+from basico.core.window import GtkAppWindow
+from basico.core.env import FILE, APP
+
+class UIApp(Gtk.Application):
+ """
+ """
+ def __init__(self, controller):
+ Gtk.Application.__init__(self,
+ application_id="net.t00mlabs.basico",
+ flags=Gio.ApplicationFlags.FLAGS_NONE)
+ GLib.set_application_name("Basico")
+ GLib.set_prgname('basico')
+ self.controller = controller
+ self.get_services()
+
+
+ def get_services(self):
+ self.gui = self.controller.get_service('GUI')
+ self.im = self.controller.get_service('IM')
+ self.cb = self.controller.get_service('Callbacks')
+
+
+ def do_activate(self):
+ self.window = self.gui.add_widget('gtk_app_window_main', GtkAppWindow(self))
+ self.window.connect("delete-event", self.gui.quit)
+ self.window.show()
+
+
+ def do_startup(self):
+ Gtk.Application.do_startup(self)
+
+ # show icons on the buttons
+ #~ settings = Gtk.Settings.get_default()
+ #~ DEPRECATED: settings.props.gtk_button_images = True
+
+ # actions that control the application: create, connect their
+ # signal to a callback method (see below), add the action to the
+ # application
+
+
+ def get_window(self):
+ return self.window
+
+
+ def get_controller(self):
+ return self.controller
+
+
+ def cb_hide_about(self, aboutdialog, user_data):
+ aboutdialog.destroy()
+
+
+ def cb_toggle_fullscreen(self, tgbutton, user_data=None):
+ if tgbutton.get_active():
+ self.window.fullscreen()
+ else:
+ self.window.unfullscreen()
+
Index: branches/BR-0.3/basico/core/uifuncs.py
===================================================================
--- branches/BR-0.3/basico/core/uifuncs.py (nonexistent)
+++ branches/BR-0.3/basico/core/uifuncs.py (revision 233)
@@ -0,0 +1,160 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# File: uifuncs.py
+# Author: Tomás Vírseda
+# License: GPL v3
+# Description: Generic UI functions service
+
+
+from gi.repository import Gtk
+from gi.repository import Gio
+from gi.repository import Pango
+from gi.repository.GdkPixbuf import Pixbuf
+
+from basico.core.service import Service
+
+class UIFuncs(Service):
+ def initialize(self):
+ self.get_services()
+
+
+ def get_services(self):
+ self.gui = self.app.get_service('GUI')
+ self.cb = self.app.get_service('Callbacks')
+ self.im = self.app.get_service('IM')
+ self.db = self.app.get_service('DB')
+
+
+ def get_gtk_version(self):
+ return Gtk.get_major_version(), Gtk.get_minor_version(), Gtk.get_micro_version()
+
+
+ def check_gtk_version(self):
+ vmajor, vminor, vmicro = self.get_gtk_version()
+ self.debug("GTK+ Version: %d.%d.%d" % (vmajor, vminor, vmicro))
+
+ if vmajor == 3 and vminor >= 18:
+ self.debug("GTK+ version supported")
+ return True
+ else:
+ self.log.error("Please, install a modern version of GTK+ (>= 3.18)")
+ return False
+
+
+ def message_dialog(self, head, body):
+ dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, "%s" % head)
+ dialog.format_secondary_text("%s" % body)
+ dialog.run()
+ dialog.destroy()
+
+
+ def statusbar_msg(self, message):
+ statusbar = self.gui.get_widget('widget_statusbar')
+ context = statusbar.get_context_id("basico")
+ statusbar.push(context, message)
+
+
+ def create_menuview_button(self, view):
+ hbox = Gtk.Box(spacing = 0, orientation="horizontal")
+ icon = self.im.get_pixbuf_icon('basico-%s' % view, 24, 24)
+ image = Gtk.Image()
+ image.set_from_pixbuf(icon)
+ label = Gtk.Label("View by %s" % view)
+ hbox.pack_start(image, False, False, 3)
+ hbox.pack_start(label, False, False, 3)
+ button = Gtk.Button()
+ button.add(hbox)
+ button.set_relief(Gtk.ReliefStyle.NONE)
+ button.connect('clicked', self.cb.gui_refresh_view, '%s' % view)
+
+ return button
+
+
+ def create_button(self, icon_name=None, icon_width=32, icon_heigth=32, title=''):
+ button = Gtk.Button()
+ button.set_relief(Gtk.ReliefStyle.NONE)
+ hbox = Gtk.HBox()
+ icon = self.im.get_new_image_icon(icon_name, icon_width, icon_heigth)
+ label = Gtk.Label()
+ label.set_markup(title)
+ label.set_xalign(0.0)
+ hbox.pack_start(icon, False, False, 6)
+ hbox.pack_start(label, True, True, 0)
+ button.add(hbox)
+
+ return button
+
+
+ def warning_message(self, widget, question, explanation, bag):
+ self.debug("DBAG: %s(%d)" % (bag, len(bag)))
+ window = self.gui.get_widget('gtk_app_window_main')
+ icon = self.im.get_new_image_icon('basico-delete', 96, 96)
+ icon.show_all()
+ dialog = Gtk.MessageDialog(window, 0, Gtk.MessageType.QUESTION,
+ Gtk.ButtonsType.YES_NO, question)
+ dialog.set_size_request(800, 600);
+ content_area = dialog.get_content_area()
+ vbox = Gtk.VBox()
+ label = Gtk.Label()
+ label.set_markup('<b>This is the list of SAP Notes to be deleted</b>')
+ vbox.pack_start(label, False, False, 0)
+ scr = Gtk.ScrolledWindow()
+ scr.set_shadow_type(Gtk.ShadowType.NONE)
+ scr.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
+ label = Gtk.Label()
+ label.set_property('ellipsize', Pango.EllipsizeMode.MIDDLE)
+ label.set_property('selectable', True)
+ label.grab_focus()
+ msg = '\n'
+ if len(bag) == 1:
+ label.set_text(bag[0])
+ else:
+ for sid in bag:
+ msg += ' %s - %s \n' % (sid, self.db.get_title(sid))
+ msg += '\n'
+ label.set_markup(msg)
+ scr.add(label)
+ vbox.pack_start(scr, True, True, 0)
+ content_area.pack_start(vbox, True, True, 0)
+ content_area.show_all()
+ dialog.set_image(icon)
+ dialog.format_secondary_text(explanation)
+ response = dialog.run()
+ if response == Gtk.ResponseType.YES:
+ answer = True
+ elif response == Gtk.ResponseType.NO:
+ answer = False
+ dialog.destroy()
+
+ return answer
+
+
+ def set_widget_visibility(self, widget_name, visibility):
+ widget = self.gui.get_widget(widget_name)
+ if visibility:
+ widget.set_no_show_all(False)
+ widget.show_all()
+ else:
+ widget.set_no_show_all(True)
+ widget.hide()
+
+
+ def get_combobox_text(self, combobox, col):
+ treeiter = combobox.get_active_iter()
+ model = combobox.get_model()
+ return model[treeiter][col]
+
+
+ def set_combobox_active(self, combobox, value):
+ model = combobox.get_model()
+ for treeiter in model:
+ self.debug (model[treeiter][0])
+
+ def get_textview_text(self, textview):
+ textbuffer = textview.get_buffer()
+ istart, iend = textbuffer.get_bounds()
+ return textbuffer.get_text(istart, iend, False)
+
+ def set_textview_text(self, textview, text):
+ textbuffer = textview.get_buffer()
+ textbuffer.set_text(text)
/branches/BR-0.3/basico/core/uifuncs.py
Property changes:
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: branches/BR-0.3/basico/core/utils.py
===================================================================
--- branches/BR-0.3/basico/core/utils.py (nonexistent)
+++ branches/BR-0.3/basico/core/utils.py (revision 233)
@@ -0,0 +1,203 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# File: utils.py
+# Author: Tomás Vírseda
+# License: GPL v3
+# Description: Generic functions service
+
+import os
+import sys
+import subprocess
+import tarfile
+import zipfile
+import shutil
+import urllib.request
+import requests
+import webbrowser
+import feedparser
+from datetime import datetime
+
+from basico.core.service import Service
+
+class Utils(Service):
+ def initialize(self):
+ self.uas = []
+
+ def timestamp(self):
+ now = datetime.now()
+ timestamp = "%4d%02d%02d_%02d%02d%02d" % (now.year, now.month, now.day, now.hour, now.minute, now.second)
+
+ return timestamp
+
+
+ def get_datetime(self, timestamp):
+ adate = datetime.strptime(timestamp, "%Y%m%d_%H%M%S")
+ return adate
+
+
+ def get_human_date_from_timestamp(self, timestamp):
+ adate = self.get_datetime(timestamp)
+ return "%s" % adate.strftime("%A, %B %e, %Y at %H:%M")
+
+
+ def fuzzy_date_from_timestamp(self, timestamp):
+ # ~ date = self.get_datetime(timestamp)
+ d1 = self.get_datetime(timestamp)
+ d2 = datetime.now()
+ rdate = d2 - d1 # DateTimeDelta
+ if int(rdate.days) > 0:
+ if (rdate.days >= 365):
+ return "%d years ago" % int((rdate.days/365))
+ else:
+ return "%d days ago" % int(rdate.days)
+
+ hours = rdate.seconds / 3600
+ if int(hours) > 0:
+ return "%d hours ago" % int(hours)
+
+ minutes = rdate.seconds / 60
+ if int(minutes) > 0:
+ return "%d minutes ago" % int(minutes)
+
+ if int(rdate.seconds) > 0:
+ return "%d seconds ago" % int(rdate.seconds)
+
+
+ def browse(self, url):
+ if sys.platform in ['linux', 'linux2']:
+ browser = webbrowser.get('firefox')
+ elif sys.platform == 'win32':
+ browser = webbrowser.get('windows-default')
+
+ browser.open_new_tab(url)
+
+
+ def which(self, program):
+ if sys.platform == 'win32':
+ program = program + '.exe'
+
+ def is_exe(fpath):
+ return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+
+ fpath, fname = os.path.split(program)
+ if fpath:
+ if is_exe(program):
+ return program
+ else:
+ for path in os.environ["PATH"].split(os.pathsep):
+ path = path.strip('"')
+ exe_file = os.path.join(path, program)
+ if is_exe(exe_file):
+ return exe_file
+
+ return None
+
+
+ def install_geckodriver(self):
+ """Get last version of Gecko webdriver from github"""
+ self.debug("OS Platform: %s" % sys.platform)
+ if sys.platform in ['linux', 'linux2']:
+ GECKO_SOURCE = "https://github.com/mozilla/geckodriver/releases/download/v0.23.0/geckodriver-v0.23.0-linux64.tar.gz"
+ GECKO_TARGET = self.get_var('TMP', scope='local') + 'gecko.tar.gz'
+ elif sys.platform == 'win32':
+ GECKO_SOURCE = "https://github.com/mozilla/geckodriver/releases/download/v0.23.0/geckodriver-v0.23.0-win64.zip"
+ GECKO_TARGET = self.get_var('TMP', scope='local') + 'gecko.zip'
+
+ GECKO_INSTALL_DIR = self.get_var('DRIVERS', 'local')
+
+
+ if os.path.exists(GECKO_TARGET):
+ self.debug("Gecko webdriver already downloaded")
+ downloaded = True
+ else:
+ downloaded = self.download('Gecko', GECKO_SOURCE, GECKO_TARGET)
+
+ if downloaded:
+ if sys.platform in ['linux', 'linux2']:
+ extracted = self.extract(GECKO_TARGET, GECKO_INSTALL_DIR, 'tar.gz')
+ elif sys.platform == 'win32':
+ extracted = self.extract(GECKO_TARGET, GECKO_INSTALL_DIR, 'zip')
+ if extracted:
+ self.debug("Gecko webdriver deployed successfully")
+ else:
+ self.log.error("Gecko could not be deployed")
+ self.log.error("Tip: maybe %s is corrupt. Delete it" % GECKO_TARGET)
+ #FIXME: stop application gracefully
+ exit(-1)
+
+
+ def download(self, prgname, source, target):
+ try:
+ self.debug ("Downloading %s from: %s" % (prgname, source))
+ response = requests.get(source, stream=True)
+ with open(target, 'wb') as out_file:
+ shutil.copyfileobj(response.raw, out_file)
+ del response
+ self.debug ("%s downloaded to %s" % (prgname, target))
+ return True
+ except Exception as error:
+ self.log.error(error)
+ return False
+
+
+ def extract(self, filename, target_path, protocol):
+ self.debug("Extracting %s to %s using protocol %s" % (filename, target_path, protocol))
+ if protocol in ['tar.gz', 'bz2']:
+ try:
+ tar = tarfile.open(filename, "r:*")
+ tar.extractall(target_path)
+ tar.close()
+ self.debug("Extracted successfully")
+ return True
+ except Exception as error:
+ self.log.error(error)
+ return False
+ elif protocol == 'zip':
+ try:
+ self.unzip(filename, target_path)
+ self.debug("Extracted successfully")
+ return True
+ except Exception as error:
+ self.log.error(error)
+ return False
+
+ def zip(self, filename, directory):
+ # http://stackoverflow.com/a/25650295
+ #~ make_archive(archive_name, 'gztar', root_dir)
+ res = shutil.make_archive(filename, 'gztar', directory)
+ self.debug("%s - %s" % (filename, directory))
+ self.debug("zip res: %s" % res)
+
+
+ def unzip(self, target, install_dir):
+ zip_archive = zipfile.ZipFile(target, "r")
+ zip_archive.extractall(path=install_dir)
+ zip_archive.close()
+
+
+ def get_firefox_profile_dir(self):
+ if sys.platform in ['linux', 'linux2']:
+ cmd = "ls -d /home/$USER/.mozilla/firefox/*.default/"
+ p = subprocess.Popen([cmd], shell=True, stdout=subprocess.PIPE)
+ FF_PRF_DIR = p.communicate()[0][0:-2]
+ FF_PRF_DIR_DEFAULT = str(FF_PRF_DIR,'utf-8')
+ elif sys.platform == 'win32':
+ import glob
+ APPDATA = os.getenv('APPDATA')
+ FF_PRF_DIR = "%s\\Mozilla\\Firefox\\Profiles\\" % APPDATA
+ PATTERN = FF_PRF_DIR + "*default*"
+ FF_PRF_DIR_DEFAULT = glob.glob(PATTERN)[0]
+
+ return FF_PRF_DIR_DEFAULT
+
+
+ def feedparser_parse(self, thing):
+ try:
+ return feedparser.parse(thing)
+ except TypeError:
+ if 'drv_libxml2' in feedparser.PREFERRED_XML_PARSERS:
+ feedparser.PREFERRED_XML_PARSERS.remove('drv_libxml2')
+ return feedparser.parse(thing)
+ else:
+ self.log.error(self.get_traceback())
+ return None
Index: branches/BR-0.3/basico/core/window.py
===================================================================
--- branches/BR-0.3/basico/core/window.py (nonexistent)
+++ branches/BR-0.3/basico/core/window.py (revision 233)
@@ -0,0 +1,400 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# File: window.py
+# Author: Tomás Vírseda
+# License: GPL v3
+# Description: Gtk.ApplicationWindow implementation
+
+import os
+import stat
+import time
+import platform
+
+import gi
+gi.require_version('Gtk', '3.0')
+
+from gi.repository import GLib
+from gi.repository import GObject
+from gi.repository import Gtk
+from gi.repository import Gdk
+from gi.repository import Gio
+from gi.repository import Pango
+from gi.repository.GdkPixbuf import Pixbuf
+
+from basico.widgets.sapnoteviewmenu import SAPNoteViewMenu
+from basico.widgets.sapnoteviewvisor import SAPNoteViewVisor, SAPNoteViewVisorToolbar
+from basico.widgets.about import About
+from basico.widgets.settings import SettingsView
+from basico.widgets.logviewer import LogViewer
+from basico.widgets.annotation import AnnotationWidget
+from basico.widgets.statusbar import Statusbar
+from basico.core.service import Service
+
+class GtkAppWindow(Gtk.ApplicationWindow, Service):
+ def __init__(self, uiapp):
+ self.setup_controller(uiapp)
+ self.get_services()
+ self.log = self.controller.log
+ self.gui.add_widget('uiapp', uiapp)
+ self.setup_window()
+ self.controller.debug("Window setup correctly")
+ self.setup_widgets()
+ self.controller.debug("Widgets setup correctly")
+ self.run()
+
+
+ def get_services(self):
+ self.gui = self.controller.get_service("GUI")
+ self.app = self.gui.get_app()
+ self.db = self.controller.get_service("DB")
+ self.uif = self.controller.get_service("UIF")
+ self.prefs = self.controller.get_service("Settings")
+ self.im = self.controller.get_service('IM')
+ self.cb = self.controller.get_service('Callbacks')
+
+
+ def setup_controller(self, uiapp):
+ self.uiapp = uiapp
+ self.controller = uiapp.get_controller()
+
+
+ def setup_window(self):
+ app_title = self.controller.get_app_info('name')
+ Gtk.Window.__init__(self, title=app_title, application=self.uiapp)
+ icon = self.im.get_icon('basico-component', 48, 48)
+ self.set_icon(icon)
+ # FIXME
+ # From docs: Don’t use this function. It sets the X xlib.Window
+ # System “class” and “name” hints for a window.
+ # But I have to do it or it doesn't shows the right title. ???
+ self.set_wmclass (app_title, app_title)
+ self.set_role(app_title)
+ self.set_default_size(1024, 728)
+ self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
+ self.maximize ()
+ self.setup_headerbar()
+ self.show_all()
+
+
+ def setup_headerbar(self):
+ hb = self.gui.add_widget('gtk_headerbar_container', Gtk.HeaderBar())
+ hb.set_show_close_button(True)
+ hb.props.title = "Basico"
+ hb.props.subtitle = "SAP Notes Manager for SAP Consultants"
+ lhbox = self.setup_headerbar_left(hb)
+ hb.pack_start(lhbox)
+ rhbox = self.setup_headerbar_right(hb)
+
+ hb.pack_end(rhbox)
+ self.set_titlebar(hb)
+ hb.show_all()
+
+
+ def setup_headerbar_left(self, hb):
+ # ~ '''Left headerbar side not used by now'''
+ lhbox = Gtk.HBox()
+
+ ### Dashboard / Visor
+ hbox = Gtk.HBox()
+ icon = self.im.get_pixbuf_icon('basico-dashboard', 24, 24)
+ image = Gtk.Image()
+ image.set_from_pixbuf(icon)
+ label = Gtk.Label()
+ hbox.pack_start(image, False, False, 3)
+ hbox.pack_start(label, False, False, 3)
+ button = self.gui.add_widget('gtk_button_dashboard', Gtk.Button())
+ button.add(hbox)
+ button.set_relief(Gtk.ReliefStyle.NONE)
+ lhbox.pack_start(button, False, False, 0)
+ button.connect('clicked', self.cb.gui_show_dashboard)
+
+ return lhbox
+
+
+ def setup_headerbar_right(self, hb):
+ rhbox = Gtk.HBox()
+
+ ## System Menu
+ button = Gtk.Button()
+ icon = self.im.get_pixbuf_icon('basico-menu-system', 24, 24)
+ image = Gtk.Image()
+ image.set_from_pixbuf(icon)
+ button.set_image(image)
+ button.set_relief(Gtk.ReliefStyle.NONE)
+ popover = Gtk.Popover.new(button)
+ self.gui.add_widget('gtk_popover_button_menu_system', popover)
+ button.connect('clicked', self.cb.gui_show_popover, popover)
+ rhbox.pack_end(button, False, False, 0)
+
+ # Popover body
+ box = Gtk.Box(spacing = 0, orientation="vertical")
+
+ ### About
+ hbox = Gtk.Box(spacing = 0, orientation="horizontal")
+ icon = self.im.get_pixbuf_icon('basico-about', 24, 24)
+ image = Gtk.Image()
+ image.set_from_pixbuf(icon)
+ label = Gtk.Label("About")
+ hbox.pack_start(image, False, False, 3)
+ hbox.pack_start(label, False, False, 3)
+ button = Gtk.Button()
+ button.add(hbox)
+ button.set_relief(Gtk.ReliefStyle.NONE)
+ button.connect('clicked', self.cb.gui_show_about)
+ box.pack_end(button, False, False, 0)
+
+ # ~ ### Help
+ # ~ hbox = Gtk.Box(spacing = 0, orientation="horizontal")
+ # ~ icon = self.im.get_pixbuf_icon('basico-help', 24, 24)
+ # ~ image = Gtk.Image()
+ # ~ image.set_from_pixbuf(icon)
+ # ~ label = Gtk.Label("Help")
+ # ~ hbox.pack_start(image, False, False, 3)
+ # ~ hbox.pack_start(label, False, False, 3)
+ # ~ button = Gtk.Button()
+ # ~ button.add(hbox)
+ # ~ button.set_relief(Gtk.ReliefStyle.NONE)
+ # ~ button.connect('clicked', self.cb.gui_show_help)
+ # ~ box.pack_end(button, False, False, 0)
+
+ ### Log viewer
+ hbox = Gtk.Box(spacing = 0, orientation="horizontal")
+ icon = self.im.get_pixbuf_icon('basico-logviewer', 24, 24)
+ image = Gtk.Image()
+ image.set_from_pixbuf(icon)
+ label = Gtk.Label("Log viewer")
+ hbox.pack_start(image, False, False, 3)
+ hbox.pack_start(label, False, False, 3)
+ button = Gtk.Button()
+ button.add(hbox)
+ button.set_relief(Gtk.ReliefStyle.NONE)
+ button.connect('clicked', self.cb.gui_show_log)
+ box.pack_end(button, False, False, 0)
+
+ ### Settings
+ hbox = Gtk.Box(spacing = 0, orientation="horizontal")
+ icon = self.im.get_pixbuf_icon('basico-settings', 24, 24)
+ image = Gtk.Image()
+ image.set_from_pixbuf(icon)
+ label = Gtk.Label("Settings")
+ hbox.pack_start(image, False, False, 3)
+ hbox.pack_start(label, False, False, 3)
+ button = Gtk.Button()
+ button.add(hbox)
+ button.set_relief(Gtk.ReliefStyle.NONE)
+ button.connect('clicked', self.cb.gui_show_settings)
+ box.pack_start(button, False, False, 0)
+
+ ### Backup
+ hbox = Gtk.Box(spacing = 0, orientation="horizontal")
+ icon = self.im.get_pixbuf_icon('basico-backup', 24, 24)
+ image = Gtk.Image()
+ image.set_from_pixbuf(icon)
+ label = Gtk.Label("Backup")
+ hbox.pack_start(image, False, False, 3)
+ hbox.pack_start(label, False, False, 3)
+ button = Gtk.Button()
+ button.add(hbox)
+ button.set_relief(Gtk.ReliefStyle.NONE)
+ button.connect('clicked', self.cb.action_backup)
+ box.pack_start(button, False, False, 0)
+
+
+ popover.add(box)
+ return rhbox
+
+
+ def setup_widgets(self):
+ # Mainbox
+ mainbox = self.gui.add_widget('gtk_vbox_container_main', Gtk.VBox())
+ mainbox.set_hexpand(True)
+ paned = self.gui.add_widget('gtk_hpaned', Gtk.HPaned())
+ paned.set_property('margin-bottom', 6)
+ paned.set_wide_handle(False)
+ paned.set_position(300)
+
+ # Paned
+ ## Left view
+ box = self.gui.add_widget('gtk_vbox_container_menu_view', Gtk.VBox())
+ box.set_property('margin-left', 6)
+ box.set_property('margin-right', 6)
+ box.set_property('margin-bottom', 0)
+
+ ### Toolbar
+ toolbar = Gtk.Toolbar()
+ toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
+
+ #### Filter entry tool
+ tool = Gtk.ToolItem.new()
+
+ hbox = Gtk.HBox()
+ viewfilter = self.gui.add_widget('gtk_entry_filter_view', Gtk.Entry())
+ viewfilter.connect('activate', self.cb.gui_viewmenu_filter)
+
+ icon = self.im.get_pixbuf_icon('basico-refresh')
+ viewfilter.set_icon_from_pixbuf(Gtk.EntryIconPosition.PRIMARY, icon)
+ viewfilter.set_icon_sensitive(Gtk.EntryIconPosition.PRIMARY, True)
+ viewfilter.set_icon_tooltip_markup (Gtk.EntryIconPosition.PRIMARY, "Refresh and collapse")
+
+ icon = self.im.get_pixbuf_icon('basico-filter')
+ viewfilter.set_icon_from_pixbuf(Gtk.EntryIconPosition.SECONDARY, icon)
+ viewfilter.set_icon_sensitive(Gtk.EntryIconPosition.SECONDARY, True)
+ viewfilter.set_icon_tooltip_markup (Gtk.EntryIconPosition.SECONDARY, "Click here to expand the tree")
+ viewfilter.set_placeholder_text("Filter this view...")
+
+ def on_icon_pressed(entry, icon_pos, event):
+ if icon_pos == Gtk.EntryIconPosition.PRIMARY:
+ viewmenu = self.gui.get_widget('viewmenu')
+ viewmenu.refresh()
+ elif icon_pos == Gtk.EntryIconPosition.SECONDARY:
+ self.cb.expand_menuview()
+
+ viewfilter.connect("icon-press", on_icon_pressed)
+
+ hbox.pack_start(viewfilter, True, True, 0)
+ tool.add(hbox)
+ tool.set_expand(True)
+ toolbar.insert(tool, -1)
+
+ box.pack_start(toolbar, False, False, 0)
+
+ ### View treeview
+ scr = Gtk.ScrolledWindow()
+ scr.set_hexpand(True)
+ scr.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
+ scr.set_shadow_type(Gtk.ShadowType.IN)
+ vwp = Gtk.Viewport()
+ vwp.set_hexpand(True)
+ viewsbox = self.gui.add_widget('gtk_box_container_views', Gtk.Box())
+ viewsbox.set_hexpand(True)
+ vwp.add(viewsbox)
+ scr.add(vwp)
+ box.pack_start(scr, True, True, 0)
+ paned.add1(box)
+
+ ## Right view
+ box = Gtk.VBox()
+ box.set_hexpand(True)
+ stack_main = self.gui.add_widget('gtk_stack_main', Gtk.Stack())
+ stack_main.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
+ stack_main.set_transition_duration(2500)
+ box.pack_start(stack_main, True, True, 0)
+
+ ### Visor stack
+ stack_child = self.setup_stack_visor()
+ stack_main.add_titled(stack_child, "visor", "SAP Notes Visor")
+
+ ### About stack
+ stack_child = self.setup_stack_about()
+ stack_main.add_titled(stack_child, "about", "About Basico")
+
+ ### Log stack
+ stack_child = self.setup_stack_log()
+ stack_main.add_titled(stack_child, "log", "Log Viewer")
+
+ ### Settings stack
+ stack_child = self.setup_stack_settings()
+ stack_main.add_titled(stack_child, "settings", "Basico Settings")
+
+ ## Annotations
+ boxannotations = self.gui.add_widget('gtk_vbox_container_annotations', Gtk.VBox())
+
+ stack_annot = self.gui.add_widget('gtk_stack_annotation', Gtk.Stack())
+ stack_annot.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
+ stack_annot.set_transition_duration(2500)
+
+ stack_child = self.setup_stack_annotation()
+ stack_annot.add_titled(stack_child, "comment", "New comment")
+ stack_annot.child_set_property (stack_child, "icon-name", "basico-comments")
+
+ # ~ stack_child = self.setup_stack_annotation_link()
+ # ~ stack_annot.add_titled(stack_child, "link", "New link")
+ # ~ stack_annot.child_set_property (stack_child, "icon-name", "basico-browse")
+
+ self.uif.set_widget_visibility('gtk_vbox_container_annotations', False)
+ boxannotations.add(stack_annot)
+
+
+ box.pack_start(boxannotations, True, True, 6)
+ paned.add2(box)
+ mainbox.pack_start(paned, True, True, 0)
+
+ # Statusbar
+ statusbar = self.gui.add_widget('widget_statusbar', Statusbar(self.controller))
+ mainbox.pack_start(statusbar, False, False, 0)
+
+ # Menu Views
+ vbox = Gtk.VBox()
+ viewsbox = self.gui.get_widget('gtk_box_container_views')
+ viewmenu = self.gui.add_widget('viewmenu', SAPNoteViewMenu(self.controller))
+ viewmenu.set_hexpand(True)
+ viewmenu.set_vexpand(True)
+ vbox.pack_start(viewmenu, True, True, 0)
+ self.gui.swap_widget(viewsbox, vbox)
+
+ self.add(mainbox)
+ self.show_all()
+
+
+ def setup_stack_visor(self):
+ box = Gtk.VBox()
+ box.set_hexpand(True)
+
+ ### Toolbar
+ boxtoolbar = self.gui.add_widget('gtk_hbox_container_toolbar', Gtk.HBox())
+ box.pack_start(boxtoolbar, False, False, 0)
+ visortoolbar = self.gui.add_widget('visortoolbar', SAPNoteViewVisorToolbar(self.controller))
+ self.gui.swap_widget(boxtoolbar, visortoolbar)
+
+ ### Visor
+ scr = Gtk.ScrolledWindow()
+ scr.set_hexpand(True)
+ scr.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
+ vwp = Gtk.Viewport()
+ vwp.set_hexpand(True)
+ visor = self.gui.add_widget('visor', SAPNoteViewVisor(self.controller))
+ visor.set_hexpand(True)
+ visor.set_vexpand(True)
+ vwp.add(visor)
+ scr.add(vwp)
+ box.pack_start(scr, True, True, 0)
+ visor.show_all()
+ box.show_all()
+ return box
+
+
+ def setup_stack_about(self):
+ box = Gtk.VBox()
+ box.set_hexpand(True)
+ about = self.gui.add_widget('widget_about', About())
+ box.pack_start(about, True, True, 0)
+ box.show_all()
+ return box
+
+
+ def setup_stack_settings(self):
+ box = Gtk.VBox()
+ box.set_hexpand(True)
+ settings = self.gui.add_widget('widget_settings', SettingsView(self.controller))
+ box.pack_start(settings, True, True, 0)
+ box.show_all()
+ return box
+
+ def setup_stack_log(self):
+ box = Gtk.VBox()
+ box.set_hexpand(True)
+ logviewer = self.gui.add_widget('widget_logviewer', LogViewer(self.controller))
+ box.pack_start(logviewer, True, True, 0)
+ box.show_all()
+ return box
+
+
+ def setup_stack_annotation(self):
+ return self.gui.add_widget('widget_annotation', AnnotationWidget(self.controller))
+
+
+ def run(self):
+ viewmenu = self.gui.get_widget('viewmenu')
+ viewmenu.set_view('annotation')
+ self.cb.gui_show_dashboard()
+ # ~ self.cb.gui_show_about()
Index: branches/BR-0.3/basico/widgets/about.py
===================================================================
--- branches/BR-0.3/basico/widgets/about.py (revision 232)
+++ branches/BR-0.3/basico/widgets/about.py (revision 233)
@@ -14,8 +14,8 @@
from gi.repository import Gdk
from gi.repository import Pango
-from basico.env import ROOT, USER_DIR, APP, LPATH, GPATH, FILE
-from basico.service import Service
+from basico.core.env import ROOT, USER_DIR, APP, LPATH, GPATH, FILE
+from basico.core.service import Service
class About(Gtk.ScrolledWindow):
def __init__(self):
/branches/BR-0.3/basico/widgets/annotation.py
5,8 → 5,13
# License: GPL v3
# Description: Annotation Widget
 
import os
import json
import uuid
import glob
from os.path import sep as SEP
from cgi import escape
 
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
14,8 → 19,8
from gi.repository import Gdk
from gi.repository import Pango
 
from basico.env import ROOT, USER_DIR, APP, LPATH, GPATH, FILE
from basico.service import Service
from basico.core.env import ROOT, USER_DIR, APP, LPATH, GPATH, FILE
from basico.core.service import Service
 
class AnnotationWidget(Gtk.VBox, Service):
def __init__(self, app, sid='0000000000'):
24,13 → 29,9
self.log = app.log
self.sid = sid
self.get_services()
self.setup()
self.setup_header()
self.setup_body()
self.setup_footer()
self.__setup()
 
 
 
def get_services(self):
"""Load services to be used in this class
"""
43,7 → 44,7
self.utils = self.app.get_service('Utils')
 
 
def setup(self):
def __setup(self):
# Setup Widget properties
self.set_property('margin-left', 3)
self.set_property('margin-right', 3)
55,13 → 56,16
self.container_body.set_border_width(3)
self.pack_start(self.container_body, True, True, 3)
 
self.__setup_header()
self.__setup_body()
self.__setup_footer()
 
def setup_header(self):
 
def __setup_header(self):
# Annotation Header
container = self.gui.get_widget('gtk_vbox_annotation_container')
header = Gtk.VBox()
hbox = Gtk.HBox()
icon = self.im.get_new_image_icon('basico-annotation')
title = self.gui.add_widget('gtk_label_annotation_sid', Gtk.Label())
if self.sid == '0000000000':
title.set_markup('<big><b>Annotation</b></big>')
68,61 → 72,60
else:
title.set_markup('<big><b>Annotation for SAP Note %s</b>' % str(int(self.sid)))
title.set_xalign(0.0)
hbox.pack_start(icon, False, False, 6)
a_lbl_aid = Gtk.Label()
a_lbl_aid.set_property('xalign', 1.0)
a_lbl_aid.modify_font(Pango.FontDescription('Monospace 10'))
a_lbl_aid.set_markup('<b>Annotation Id:</b>')
a_aid = self.gui.add_widget('gtk_label_aid', Gtk.Label())
a_aid.set_property('xalign', 1.0)
a_aid.set_selectable(True)
a_aid.modify_font(Pango.FontDescription('Monospace 10'))
separator = Gtk.Separator()
hbox.pack_start(title, True, True, 0)
separator = Gtk.Separator()
hbox.pack_start(a_lbl_aid, False, False, 0)
hbox.pack_start(a_aid, False, False, 0)
header.pack_start(hbox, False, False, 0)
 
# Timestamp
a_wdg_human_timestamp = self.gui.add_widget('gtk_label_human_timestamp', Gtk.Label())
a_wdg_human_timestamp.modify_font(Pango.FontDescription('Monospace 10'))
a_wdg_human_timestamp.set_xalign(0.0)
header.pack_start(a_wdg_human_timestamp, True, True, 0)
 
header.pack_start(separator, False, False, 3)
header.show_all()
container.pack_start(header, False, False, 3)
container.pack_start(header, False, False, 0)
 
 
def setup_body(self):
def __setup_body(self):
vbox = Gtk.VBox()
 
# Hidden metadata
hbox = Gtk.HBox()
a_aid = self.gui.add_widget('gtk_label_aid', Gtk.Label())
a_aid.set_property('xalign', 0.0)
a_aid.set_sensitive(False)
a_aid.set_selectable(True)
a_aid.modify_font(Pango.FontDescription('Monospace 10'))
# ~ a_aid.set_visible(False)
# ~ a_aid.set_no_show_all(True)
hbox.pack_start(a_aid, True, True, 3)
hhbox = Gtk.HBox()
 
a_timestamp = self.gui.add_widget('gtk_label_timestamp', Gtk.Label(self.utils.timestamp()))
a_timestamp.set_sensitive(False)
a_timestamp.set_visible(False)
a_timestamp.set_no_show_all(True)
hbox.pack_start(a_timestamp, True, True, 0)
vbox.pack_start(hbox, False, False, 3)
self.uif.set_widget_visibility('gtk_label_timestamp', False)
hhbox.pack_start(a_timestamp, False, False, 0)
vbox.pack_start(hhbox, False, False, 0)
 
hbox = Gtk.HBox()
 
# Main hbox
mhbox = Gtk.HBox()
 
vboxl = Gtk.VBox()
vboxl.set_vexpand(True)
vboxr = Gtk.VBox()
vboxr.set_vexpand(True)
 
# Title
a_title = self.gui.add_widget('gtk_entry_annotation_title', Gtk.Entry())
a_title.set_placeholder_text("Type a title here...")
a_title.modify_font(Pango.FontDescription('Monospace 10'))
hbox.pack_start(a_title, True, True, 0)
vboxl.pack_start(a_title, False, False, 0)
 
# Type
a_type_model = Gtk.ListStore(str)
self.gui.add_widget('annotation_type_FIXME', a_type_model.append(['FIXME']))
active = self.gui.add_widget('annotation_type_Note', a_type_model.append(['Note']))
self.gui.add_widget('annotation_type_Procedure', a_type_model.append(['Procedure']))
self.gui.add_widget('annotation_type_Snippet', a_type_model.append(['Snippet']))
self.gui.add_widget('annotation_type_Template', a_type_model.append(['Template']))
self.gui.add_widget('annotation_type_TODO', a_type_model.append(['TODO']))
a_type = Gtk.ComboBox.new_with_model(a_type_model)
a_type.set_active_iter(active)
self.gui.add_widget('gtk_combobox_annotation_type', a_type)
renderer_type = Gtk.CellRendererText()
a_type.pack_start(renderer_type, True)
a_type.add_attribute(renderer_type, "text", 0)
hbox.pack_start(a_type, False, False, 3)
vbox.pack_start(hbox, False, False, 0)
 
# Text
hbox = Gtk.HBox()
# ~ box = Gtk.HBox()
scroller = Gtk.ScrolledWindow()
scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
scroller.set_shadow_type(Gtk.ShadowType.IN)
129,6 → 132,7
scroller.set_hexpand(True)
scroller.set_vexpand(True)
a_text = self.gui.add_widget('gtk_textview_annotation_text', Gtk.TextView())
a_text.set_wrap_mode(Gtk.WrapMode.WORD)
a_text.modify_font(Pango.FontDescription('Monospace 10'))
a_text.set_vexpand(True)
a_text.set_left_margin (6)
139,13 → 143,42
a_textbuffer.set_text('')
a_text.set_buffer(a_textbuffer)
scroller.add(a_text)
hbox.pack_start(scroller, True, True, 3)
vboxl.pack_start(scroller, True, True, 3)
 
# Calendar
calendar = self.gui.add_widget('gtk_calendar_annotation_timestamp', Gtk.Calendar())
hbox.pack_start(calendar, False, False, 0)
vbox.pack_start(hbox, True, True, 3)
 
# Type
hbox_type = Gtk.HBox()
a_type_lbl = Gtk.Label()
a_type_lbl.set_markup('%20s' % '<b>Type </b>')
a_type_lbl.set_xalign(1.0)
# ~ vboxr.pack_start(a_type_lbl, False, False, 0)
a_type_model = Gtk.ListStore(str)
self.gui.add_widget('annotation_type_FIXME', a_type_model.append(['FIXME']))
active = self.gui.add_widget('annotation_type_Note', a_type_model.append(['Note']))
self.gui.add_widget('annotation_type_Procedure', a_type_model.append(['Procedure']))
self.gui.add_widget('annotation_type_Snippet', a_type_model.append(['Snippet']))
self.gui.add_widget('annotation_type_Template', a_type_model.append(['Template']))
self.gui.add_widget('annotation_type_TODO', a_type_model.append(['TODO']))
a_type = Gtk.ComboBox.new_with_model(a_type_model)
a_type.set_active_iter(active)
self.gui.add_widget('gtk_combobox_annotation_type', a_type)
renderer_type = Gtk.CellRendererText()
a_type.pack_start(renderer_type, True)
a_type.add_attribute(renderer_type, "text", 0)
hbox_type.pack_start(a_type_lbl, False, False, 3)
hbox_type.pack_start(a_type, True, True, 0)
hbox_type.set_homogeneous(True)
vboxr.pack_start(hbox_type, False, False, 0)
 
# ~ vbox.pack_start(hbox, False, False, 0)
 
 
 
# ~ calendar = self.gui.add_widget('gtk_calendar_annotation_timestamp', Gtk.Calendar())
# ~ calendar.modify_font(Pango.FontDescription('Monospace 8'))
# ~ hbox.pack_start(calendar, False, False, 0)
 
 
# Url
hbox = Gtk.HBox()
a_link = self.gui.add_widget('gtk_entry_annotation_link', Gtk.Entry())
167,12 → 200,15
a_link_type.pack_start(renderer_text, True)
a_link_type.add_attribute(renderer_text, "text", 0)
hbox.pack_start(a_link_type, False, False, 0)
vbox.pack_start(hbox, False, False, 3)
vboxl.pack_start(hbox, False, False, 3)
 
mhbox.pack_start(vboxl, True, True, 6)
mhbox.pack_start(vboxr, False, False, 0)
vbox.pack_start(mhbox, True, True, 3)
self.container_body.add(vbox)
 
 
def setup_footer(self):
def __setup_footer(self):
# Buttons Accept/Cancel
hbox = Gtk.HBox()
accept = self.gui.add_widget('gtk_button_accept_annotation', Gtk.Button('Accept'))
190,18 → 226,18
self.pack_start(hbox, False, False, 3)
 
 
def set_aid(self, aid):
def set_aid_to_widget(self, aid):
a_aid = self.gui.get_widget('gtk_label_aid')
a_aid.set_text(aid)
 
 
def get_aid(self):
def get_aid_from_widget(self):
a_aid = self.gui.get_widget('gtk_label_aid')
return a_aid.get_text()
 
 
def set_metadata(self, aid, action):
self.set_aid(aid)
def set_metadata_to_widget(self, aid, action):
self.set_aid_to_widget(aid)
sid = self.annot.get_sid(aid)
title = self.gui.get_widget('gtk_label_annotation_sid')
if sid == '0000000000':
212,7 → 248,7
if action == 'create':
pass
elif action == 'edit':
annotation = self.annot.get_metadata(aid)
annotation = self.annot.get_metadata_from_file(aid)
if annotation is not None:
ANNOTATION_FILE_CONTENT = LPATH['ANNOTATIONS'] + aid + '.adoc'
timestamp = self.utils.get_datetime(annotation['timestamp'])
220,23 → 256,27
a_wdg_timestamp = self.gui.get_widget('gtk_label_timestamp')
a_wdg_title = self.gui.get_widget('gtk_entry_annotation_title')
a_wdg_type = self.gui.get_widget('gtk_combobox_annotation_type')
a_wdg_human_timestamp = self.gui.get_widget('gtk_label_human_timestamp')
a_wdg_text = self.gui.get_widget('gtk_textview_annotation_text')
a_wdg_link = self.gui.get_widget('gtk_entry_annotation_link')
a_wdg_link_type = self.gui.get_widget('gtk_combobox_annotation_link_type')
a_wdg_cal = self.gui.get_widget('gtk_calendar_annotation_timestamp')
# ~ a_wdg_cal = self.gui.get_widget('gtk_calendar_annotation_timestamp')
 
a_wdg_aid.set_text(annotation['aid'])
a_wdg_timestamp.set_text(self.utils.timestamp())
# ~ a_wdg_timestamp.set_text(self.utils.timestamp())
a_wdg_timestamp.set_text(annotation['timestamp'])
a_wdg_title.set_text(annotation['title'])
a_wdg_type.set_active_iter(self.gui.get_widget('annotation_type_%s' % annotation['type']))
self.uif.set_textview_text(a_wdg_text, open(ANNOTATION_FILE_CONTENT).read())
a_wdg_link.set_text(annotation['link'])
a_wdg_link_type.set_active_iter(self.gui.get_widget('annotation_link_type_%s' % annotation['link_type']))
a_wdg_cal.select_month(timestamp.month-1, timestamp.year)
a_wdg_cal.select_day(timestamp.day)
human_timestamp = self.utils.get_human_date_from_timestamp(annotation['timestamp'])
a_wdg_human_timestamp.set_text(human_timestamp)
# ~ a_wdg_cal.select_month(timestamp.month-1, timestamp.year)
# ~ a_wdg_cal.select_day(timestamp.day)
 
 
def get_metadata(self, aid):
def get_metadata_from_widget(self, aid):
annotation = {}
a_wdg_aid = self.gui.get_widget('gtk_label_aid')
a_wdg_timestamp = self.gui.get_widget('gtk_label_timestamp')
/branches/BR-0.3/basico/widgets/collections.py
14,8 → 14,8
from gi.repository import Gdk
from gi.repository import Pango
 
from basico.env import ROOT, USER_DIR, APP, LPATH, GPATH, FILE
from basico.service import Service
from basico.core.env import ROOT, USER_DIR, APP, LPATH, GPATH, FILE
from basico.core.service import Service
 
 
class CollectionsMgtView(Gtk.VBox, Service):
/branches/BR-0.3/basico/widgets/importwdg.py
14,8 → 14,8
from gi.repository import Gdk
from gi.repository import Pango
 
from basico.env import ROOT, USER_DIR, APP, LPATH, GPATH, FILE
from basico.service import Service
from basico.core.env import ROOT, USER_DIR, APP, LPATH, GPATH, FILE
from basico.core.service import Service
 
 
class ImportWidget(Gtk.VBox, Service):
/branches/BR-0.3/basico/widgets/listreview.py
14,8 → 14,8
from gi.repository import Gdk
from gi.repository import Pango
 
from .env import ROOT, USER_DIR, APP, LPATH, GPATH, FILE
from .service import Service
from basico.core.env import ROOT, USER_DIR, APP, LPATH, GPATH, FILE
from basico.core.service import Service
 
 
class ListReview(Gtk.ScrolledWindow, Service):
/branches/BR-0.3/basico/widgets/logviewer.py
14,8 → 14,8
from gi.repository import Gdk
from gi.repository import Pango
 
from basico.env import ROOT, USER_DIR, APP, LPATH, GPATH, FILE
from basico.service import Service
from basico.core.env import ROOT, USER_DIR, APP, LPATH, GPATH, FILE
from basico.core.service import Service
 
 
 
/branches/BR-0.3/basico/widgets/sapnoteviewmenu.py
0,0 → 1,412
#!/usr/bin/python
# -*- coding: utf-8 -*-
# File: sapnoteviewmenu.py
# Author: Tomás Vírseda
# License: GPL v3
# Description: SAPNoteViewMenu Widget
 
# ~ from enum import IntEnum
import traceback as tb
from cgi import escape
from collections import OrderedDict
 
import gi
gi.require_version('Gdk', '3.0')
gi.require_version('Gtk', '3.0')
from gi.repository import Gdk
from gi.repository import Gio
from gi.repository import Gtk
from gi.repository import Pango
from gi.repository.GdkPixbuf import Pixbuf
from gi.repository import Pango
from datetime import datetime
from dateutil import parser as dateparser
 
from basico.core.service import Service
from basico.widgets.sapnoteview import SAPNoteView
 
 
class SAPNoteViewMenu(SAPNoteView, Service):
view = 'component'
 
def __init__(self, app):
SAPNoteView.__init__(self, app)
self.app = app
self.row_type = None
 
 
def prepare(self):
self.column_rowtype.set_visible(False)
self.column_checkbox.set_visible(False)
self.column_icon.set_visible(False)
self.column_component.set_visible(True)
self.column_cat.set_visible(False)
self.column_sntype.set_visible(False)
self.column_sid.set_visible(False)
self.column_priority.set_visible(True)
self.column_lang.set_visible(False)
self.column_rel.set_visible(False)
 
# TreeView common
self.set_can_focus(True)
self.set_headers_visible(False)
self.set_enable_search(True)
self.set_hover_selection(False)
self.set_grid_lines(Gtk.TreeViewGridLines.NONE)
self.set_level_indentation(0)
self.sig_selection_changed = self.selection.connect('changed', self.row_changed)
 
 
def refresh(self):
visor = self.gui.get_widget('visor')
try:
self.set_view(self.view)
if self.row_type is not None:
matches = self.db.get_notes_by_node(self.row_type, self.cid)
visor.populate(matches)
except Exception as error:
self.debug(error)
self.print_traceback()
 
 
def row_changed(self, selection):
if self.current_status is None:
visor = self.gui.get_widget('visor')
try:
model, treeiters = selection.get_selected()
row = model[treeiters][0]
self.row_type, self.cid = row.split('@')
matches = self.db.get_notes_by_node(self.row_type, self.cid)
visor.populate(matches, self.cid)
except AttributeError as error:
self.debug(error)
except Exception as error:
self.debug(error)
self.print_traceback()
 
 
def set_view(self, view=None):
statusbar = self.gui.get_widget('widget_statusbar')
# FIXME: Get last view visited from config
if view is None:
view ='chronologic'
 
iconview = self.gui.get_widget('gtk_image_current_view')
icon = self.im.get_pixbuf_icon('basico-%s' % view, 24, 24)
iconview.set_from_pixbuf(icon)
iconview.show_all()
self.view = view
self.debug("Set current view to: %s" % view)
 
# Change label
viewlabel = self.gui.get_widget('gtk_label_current_view')
name = "<b>%s</b>" % view.capitalize()
viewlabel.set_markup(name)
viewlabel.set_xalign(0.0)
viewlabel.set_justify(Gtk.Justification.LEFT)
viewlabel.show_all()
self.populate([])
 
statusbar.message('<b>Displaying view: %s</b>' % view.capitalize())
 
 
def get_view(self):
return self.view
 
 
def populate(self, sapnotes):
self.current_status = "working"
self.column_component.set_title('Categories')
self.set_headers_visible(False) # Set
self.column_sid.set_visible(True)
self.column_icon.set_visible(False)
self.column_component.set_visible(True)
self.column_component.set_expand(True)
self.column_title.set_visible(False)
self.column_title.set_expand(True)
self.column_priority.set_expand(False)
self.column_cat.set_visible(False)
self.column_sid.set_expand(False)
tgbshowmenu = self.gui.get_widget('gtk_toogletoolbutton_menu_view')
tgbshowmenu.set_active(True)
 
if len(sapnotes) == 0:
sapnotes = self.db.get_notes()
 
if self.view == 'component':
self.populate_by_components(sapnotes)
elif self.view == 'description':
self.populate_by_component_descriptions(sapnotes)
elif self.view == 'bookmarks':
self.populate_by_bookmarks()
elif self.view == 'projects':
self.populate_by_projects(sapnotes)
elif self.view == 'collections':
self.populate_by_collections(sapnotes)
elif self.view == 'tags':
self.populate_by_tags(sapnotes)
elif self.view == 'category':
self.populate_by_category(sapnotes)
elif self.view == 'chronologic':
self.populate_by_chronologic(sapnotes)
elif self.view == 'priority':
self.populate_by_priority(sapnotes)
elif self.view == 'type':
self.populate_by_type(sapnotes)
elif self.view == 'collection':
self.populate_by_collection(sapnotes)
elif self.view == 'annotation':
self.populate_annotations()
else:
self.populate_by_components(sapnotes)
 
self.debug("View '%s' populated" % (self.view))
self.cb.gui_show_dashboard()
self.current_status = None
 
 
def populate_by_bookmarks(self):
matches = []
sapnotes = self.db.get_notes()
for sid in sapnotes:
if sapnotes[sid]['bookmark']:
matches.append(sid)
matches.sort()
 
visor = self.gui.get_widget('visor')
self.cb.gui_toggle_menu_view(False)
visor.populate(matches)
statusbar = self.gui.get_widget('widget_statusbar')
statusbar.message("<b>View %s populated with %d SAP Notes</b>" % (self.view.capitalize(), len(matches)))
 
 
def populate_by_components(self, sapnotes, only_bookmarks=False):
self.model.clear()
self.treepids = {}
 
if len(sapnotes) == 0:
return
 
scomp = set()
dcomp = {}
 
for sid in sapnotes:
compkey = escape(sapnotes[sid]['componentkey'])
comptxt = escape(sapnotes[sid]['componenttxt'])
scomp.add(compkey)
dcomp[compkey] = comptxt
lcomp = list(scomp)
lcomp.sort()
 
for compkey in lcomp:
subkeys = compkey.split('-')
ppid = None
for i in range(1, len(subkeys)+1):
key = ('-').join(subkeys[0:i])
try:
ppid = self.treepids[key]
except:
if i == len(subkeys):
title = dcomp[compkey]
else:
title = ""
node = self.get_node_component(key, title)
ppid = self.model.append(ppid, node)
self.treepids[key] = ppid
 
 
def populate_by_priority(self, sapnotes):
self.model.clear()
treepids = {}
 
if len(sapnotes) == 0:
return
 
scomp = set()
dcomp = {}
pset = set()
 
for sid in sapnotes:
try:
priority = sapnotes[sid]['priority']
pset.add(priority)
except: pass
 
plist = []
plist.extend(pset)
plist.sort()
 
for priority in plist:
node = self.get_node_priority(priority)
pid = self.model.append(None, node)
treepids[priority] = pid
 
 
def populate_by_type(self, sapnotes):
self.model.clear()
treepids = {}
 
if len(sapnotes) == 0:
return
 
scomp = set()
dcomp = {}
pset = set()
 
for sid in sapnotes:
try:
sntype = sapnotes[sid]['type']
pset.add(sntype)
except: pass
 
plist = []
plist.extend(pset)
plist.sort()
 
for sntype in plist:
node = self.get_node_type(sntype)
pid = self.model.append(None, node)
treepids[sntype] = pid
 
 
def populate_by_component_descriptions(self, sapnotes, only_bookmarks=False):
self.model.clear()
self.treepids = {}
 
if len(sapnotes) == 0:
return
 
scomp = set()
dcomp = {}
 
for sid in sapnotes:
compkey = escape(sapnotes[sid]['componentkey'])
comptxt = escape(sapnotes[sid]['componenttxt'])
scomp.add(compkey)
dcomp[compkey] = comptxt
lcomp = list(scomp)
lcomp.sort()
 
for compkey in lcomp:
subkeys = compkey.split('-')
ppid = None
for i in range(1, len(subkeys)+1):
key = ('-').join(subkeys[0:i])
try:
ppid = self.treepids[key]
except:
if i == len(subkeys):
title = dcomp[compkey]
else:
title = ""
node = self.get_node_component_desc(key, title)
ppid = self.model.append(ppid, node)
self.treepids[key] = ppid
 
 
def populate_by_category(self, sapnotes):
self.model.clear()
treepids = {}
 
if len(sapnotes) == 0:
return
 
scomp = set()
dcomp = {}
catset = set()
 
for sid in sapnotes:
try:
cat = sapnotes[sid]['category']
catset.add(cat)
except: pass
 
catlist = []
catlist.extend(catset)
catlist.sort()
 
for cat in catlist:
node = self.get_node_category(cat)
pid = self.model.append(None, node)
treepids[cat] = pid
 
 
def populate_by_collection(self, sapnotes):
#FIXME: manage collections
self.model.clear()
self.clts = self.app.get_service('Collections')
collections = self.clts.get_all()
od = OrderedDict(sorted(collections.items(), key=lambda t: t[1]))
for tid in od:
node = self.get_node_collection(tid, od[tid])
pid = self.model.append(None, node)
 
 
def populate_annotations(self, annotations=None):
visor = self.gui.get_widget('visor')
self.cb.gui_toggle_menu_view(False)
visor.populate_annotations(annotations)
 
 
def populate_by_chronologic(self, sapnotes):
self.model.clear()
treepids = {}
 
if len(sapnotes) == 0:
return
 
years = set()
months = set()
days = set()
for sid in sapnotes:
try:
downloaded = dateparser.parse(sapnotes[sid]['feedupdate'])
year = "%d" % downloaded.year
month = "%02d" % downloaded.month
day = "%02d" % downloaded.day
key_year = year
key_month = year + month
key_day = year + month + day
years.add(key_year)
months.add(key_month)
days.add(key_day)
except:
pass
years = list(years)
years.sort(reverse=True)
months = list(months)
months.sort(reverse=True)
days = list(days)
days.sort(reverse=True)
 
for key_year in years:
try:
treepids[key_year]
except:
adate = key_year + '0101'
downloaded = dateparser.parse(adate)
node = self.get_node_date_year(downloaded, key_year)
treepids[key_year] = self.model.append(None, node)
 
for key_month in months:
try:
treepids[key_month]
except:
adate = key_month + '01'
downloaded = dateparser.parse(adate)
node = self.get_node_date_month(downloaded, key_month)
key_year = key_month[0:4]
treepids[key_month] = self.model.append(treepids[key_year], node)
 
for key_day in days:
try:
treepids[key_day]
except:
downloaded = dateparser.parse(key_day)
key_month = key_day[0:6]
node = self.get_node_date_day(downloaded, key_day)
treepids[key_day] = self.model.append(treepids[key_month], node)
 
 
 
 
/branches/BR-0.3/basico/widgets/settings.py
14,8 → 14,8
from gi.repository import Gdk
from gi.repository import Pango
 
from basico.env import ROOT, USER_DIR, APP, LPATH, GPATH, FILE
from basico.service import Service
from basico.core.env import ROOT, USER_DIR, APP, LPATH, GPATH, FILE
from basico.core.service import Service
 
class SettingsView(Gtk.ScrolledWindow, Service):
def __init__(self, app):
/branches/BR-0.3/setup.py
117,7 → 117,7
long_description=long_description,
download_url = 'http://t00mlabs.net/downloads/basico-0.3.tar.gz',
license='GPLv3',
packages=['basico', 'basico.widgets'],
packages=['basico', 'basico.core', 'basico.widgets'],
# distutils does not support install_requires, but pip needs it to be
# able to automatically install dependencies
# ~ 'pygal==2.4.0',