diff --git a/.gitignore b/.gitignore index 19016ef..bf6279b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ dist/* build/* *.pyc src/*.egg-info/* - +*~ diff --git a/src/nailab/data/csvfolderdatasource.py b/src/nailab/data/csvfolderdatasource.py index 5361f71..ceab137 100644 --- a/src/nailab/data/csvfolderdatasource.py +++ b/src/nailab/data/csvfolderdatasource.py @@ -1,6 +1,8 @@ from nailab.data.datasource import DataSource +from naiback.data.feeds.genericcsvfeed import GenericCSVFeed + import glob import os @@ -14,12 +16,17 @@ class CsvFolderDataSource(DataSource): self._discover_feeds() def available_feeds(self): - return self.feeds + return [f[1] for f in self.feeds] def get_feed(self, feed_id): - pass + for path, f_id in self.feeds: + if f_id == feed_id: + with open(path, 'r') as f: + return GenericCSVFeed(f) + raise NailabError('Unable to obtain feed: {}'.format(feed_id)) + def _discover_feeds(self): files = glob.glob(self.path + '/*.csv') - self.feeds = [os.path.basename(f) for f in files] + self.feeds = [(f, os.path.basename(f)) for f in files] diff --git a/src/nailab/data/datasourcemanager.py b/src/nailab/data/datasourcemanager.py index 788e153..6094e76 100644 --- a/src/nailab/data/datasourcemanager.py +++ b/src/nailab/data/datasourcemanager.py @@ -15,6 +15,12 @@ class DataSourceManager: def add_source(self, source): self.sources.append(source) + def get_source(self, name): + for source in self.sources: + if source.name == name: + return source + return None + def all_sources(self): return self.sources[:] diff --git a/src/nailab/execution/__init__.py b/src/nailab/execution/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/nailab/execution/executor.py b/src/nailab/execution/executor.py new file mode 100644 index 0000000..41458d7 --- /dev/null +++ b/src/nailab/execution/executor.py @@ -0,0 +1,23 @@ + +from importlib.machinery import SourceFileLoader +import inspect + +from naiback.strategy import Strategy + +class Executor: + + def __init__(self): + pass + + def execute_from_file(self, path, feeds): + loader = SourceFileLoader('execution._current_strategy', path) + mod = loader.load_module() + for item in inspect.getmembers(mod, inspect.isclass): + klass = item[1] + if klass is not Strategy and issubclass(klass, Strategy): + strategy = klass() + for feed in feeds: + strategy.add_feed(feed) + strategy.run() + + diff --git a/src/nailab/ui/applicationwindow.py b/src/nailab/ui/applicationwindow.py index 6bbfbe8..5dfaf4d 100644 --- a/src/nailab/ui/applicationwindow.py +++ b/src/nailab/ui/applicationwindow.py @@ -1,20 +1,24 @@ from gi.repository import Gtk, GtkSource +from nailab.data.datasource import DataSource from nailab.data.datasourcemanager import DataSourceManager -from .sourceviewcontroller import SourceViewController +from nailab.execution.executor import Executor + +from .tabmanager import TabManager class ApplicationWindow: def __init__(self, builder): self.window = builder.get_object('ApplicationWindow') - self._init_sourceeditor(builder) + self.tab_manager = TabManager(builder.get_object('sourceNotebook')) self._init_tv_datasource(builder) handlers = { 'on_ApplicationWindow_delete_event' : Gtk.main_quit, - 'on_OpenFile' : self.open_file + 'on_OpenFile' : self.open_file, + 'on_StrategyExecute' : self.strategy_execute } builder.connect_signals(handlers) @@ -27,34 +31,37 @@ class ApplicationWindow: result = dlg.run() if result == Gtk.ResponseType.OK: - with open(dlg.get_filename(), 'r') as f: - self.sourceviewcontroller.set_source_text(f.read()) - + self.tab_manager.new_tab(dlg.get_filename()) dlg.destroy() + def strategy_execute(self, arg): + sel = self.tv_datasources.get_selection() + model, rows = sel.get_selected_rows() + + feeds = [] + for row in rows: + (feed_id, source_name) = self.datasources_store.get(self.datasources_store.get_iter(row), 0, 1) + source = self.datasourcemanager.get_source(source_name) + if source is not None: + feed = source.get_feed(feed_id) + feeds.append(feed) - def _init_sourceeditor(self, builder): - manager = GtkSource.LanguageManager() - buf = GtkSource.Buffer() - buf.set_language(manager.get_language('python')) - sv = builder.get_object('sourceview') - sv.set_buffer(buf) - sv.set_monospace(True) - - self.sourceviewcontroller = SourceViewController(sv) + e = Executor() + e.execute_from_file(self.tab_manager.get_current_source_path(), feeds) def _init_tv_datasource(self, builder): self.datasourcemanager = DataSourceManager() self.datasourcemanager.load_sources() tv_datasources = builder.get_object('tv_datasources') + self.tv_datasources = tv_datasources - self.datasources_store = Gtk.TreeStore(str) + self.datasources_store = Gtk.TreeStore(str, str) for source in self.datasourcemanager.all_sources(): - treeiter = self.datasources_store.append(None, (source.name,)) + treeiter = self.datasources_store.append(None, (source.name, source.name)) for feed in source.available_feeds(): - self.datasources_store.append(treeiter, (feed,)) + self.datasources_store.append(treeiter, (feed, source.name)) rendererText = Gtk.CellRendererText() @@ -63,3 +70,8 @@ class ApplicationWindow: tv_datasources.set_model(self.datasources_store) + sel = tv_datasources.get_selection() + sel.set_mode(Gtk.SelectionMode.MULTIPLE) + + + diff --git a/src/nailab/ui/tabmanager.py b/src/nailab/ui/tabmanager.py new file mode 100644 index 0000000..4c2b9d4 --- /dev/null +++ b/src/nailab/ui/tabmanager.py @@ -0,0 +1,74 @@ + +from gi.repository import GObject, Gtk, GtkSource, Pango + +from .sourceviewcontroller import SourceViewController + +class TabManager(GObject.Object): + + def __init__(self, notebook): + super().__init__() + self.notebook = notebook + self.widgets = {} + + def new_tab(self, source_file): + with open(source_file, 'r') as f: + (sv, sv_controller) = self._init_sourceeditor() + sv_controller.set_source_text(f.read()) + self.widgets[source_file] = sv + sv.show_all() + header = Gtk.HBox() + title_label = Gtk.Label(source_file) + image = Gtk.Image() + image.set_from_stock(Gtk.STOCK_CLOSE, Gtk.IconSize.MENU) + close_button = Gtk.Button() + close_button.set_image(image) + close_button.set_relief(Gtk.ReliefStyle.NONE) + close_button.connect('clicked', self.close_cb, source_file) + + header.pack_start(title_label, + expand=True, fill=True, padding=0) + header.pack_end(close_button, + expand=False, fill=False, padding=0) + header.show_all() + index = self.notebook.append_page(sv, header) + self.notebook.set_current_page(index) + + def close_cb(self, arg, source_file): + index = self._widget_num_by_source_file(source_file) + self.notebook.remove_page(index) + del self.widgets[source_file] + + def get_current_source_path(self): + index = self.notebook.get_current_page() + w = self.notebook.get_nth_page(index) + for k, v in self.widgets.items(): + if v == w: + return k + + raise KeyError('Invalid widget') + + def _init_sourceeditor(self): + scroll = Gtk.ScrolledWindow() + manager = GtkSource.LanguageManager() + buf = GtkSource.Buffer() + buf.set_language(manager.get_language('python')) + sv = GtkSource.View() + sv.set_buffer(buf) + sv.set_monospace(True) + + style_ctx = sv.get_style_context() + self.provider = Gtk.CssProvider() + self.provider.load_from_data(b'GtkSourceView { font-family: "Monospace"; }') + style_ctx.add_provider(self.provider, Gtk.STYLE_PROVIDER_PRIORITY_USER) + + sourceviewcontroller = SourceViewController(sv) + scroll.add(sv) + scroll.show() + + return (scroll, sourceviewcontroller) + + def _widget_num_by_source_file(self, source_file): + for i in range(0, self.notebook.get_n_pages()): + if self.widgets[source_file] == self.notebook.get_nth_page(i): + return i + return None diff --git a/ui/nailab.glade b/ui/nailab.glade index 8f6b474..721ed27 100644 --- a/ui/nailab.glade +++ b/ui/nailab.glade @@ -2,7 +2,11 @@ - + + True + False + gtk-missing-image + 1024 @@ -34,6 +38,7 @@ natural natural model_dataSources + True both @@ -47,24 +52,26 @@ - + True True - in - - True - True - word-char - True - True - True - 4 - True - True - True - True - + + + + + + + + + + + + + + + + @@ -202,6 +209,31 @@ True + + + True + False + Strategy + True + + + True + False + + + Execute + + True + False + image1 + False + + + + + + +