def runTest(self): def basename(name): if ":" in name: return name.split(":")[-1] else: return name db = sqlite3.connect(':memory:') db.row_factory = sqlite3.Row pi = PagesIndexer(db, None, tests.MockObject()) for i, name, cont in self.PAGES: db.execute( 'INSERT INTO pages(id, name, lowerbasename, sortkey, parent, source_file) VALUES (?, ?, ?, ?, 1, 1)', (i, name, basename(name).lower(), natural_sort_key(name))) ## Test PagesViewInternal methods iview = PagesViewInternal(db) i, pn = iview.resolve_pagename(Path(''), ['foo']) self.assertEqual((i, pn), (3, Path('Foo'))) i, pn = iview.resolve_link(Path('Foo'), HRef.new_from_wiki_link('Bar')) self.assertEqual((i, pn), (2, Path('Bar'))) ## Test the actual indexer pageindexer = tests.MaskedObject(pi, 'connect') indexer = LinksIndexer(db, pageindexer) for i, name, cont in self.PAGES: row = { 'id': i, 'name': name, 'sortkey': natural_sort_key(name), 'is_link_placeholder': False } indexer.on_page_row_inserted(pageindexer, row) ### pageindexer.setObjectAccess('insert_link_placeholder') for i, name, text in self.PAGES: tree = WikiParser().parse(text) row = {'id': i, 'name': name} indexer.on_page_changed(pageindexer, row, tree) indexer.update() links = sorted((r['source'], r['target']) for r in db.execute('SELECT * FROM links')) self.assertEqual(links, [(3, 2), (3, 4)]) ### pageindexer.setObjectAccess('remove_page') for i, name, cont in self.PAGES: row = {'id': i, 'name': name, 'is_link_placeholder': False} indexer.on_page_row_deleted(pageindexer, row) indexer.update() rows = db.execute('SELECT * FROM links').fetchall() self.assertEqual(rows, [])
def __init__(self, db): IndexView.__init__(self, db) self._pages = PagesViewInternal(db) # Test the db really has a tasklist try: db.execute('SELECT * FROM tasklist LIMIT 1') except sqlite3.OperationalError: raise ValueError, 'No tasklist in index'
class TasksView(IndexView): '''Database "view" that shows tasks that are indexed''' def __init__(self, db): IndexView.__init__(self, db) self._pages = PagesViewInternal(db) # Test the db really has a tasklist try: db.execute('SELECT * FROM tasklist LIMIT 1') except sqlite3.OperationalError: raise ValueError, 'No tasklist in index' def list_open_tasks(self, parent=None): '''List tasks @param parent: the parent task (as returned by this method) or C{None} to list all top level tasks @returns: a list of tasks at this level as sqlite Row objects ''' if parent: parentid = parent['id'] else: parentid = 0 # Sort: # started tasks by prio, due date, page + id to keep order in page # not-started tasks by start date, ... today = str(datetime.date.today()) for row in self.db.execute( ''' SELECT tasklist.* FROM tasklist LEFT JOIN pages ON tasklist.source = pages.id WHERE tasklist.open=1 and tasklist.parent=? and tasklist.start<=? ORDER BY tasklist.prio DESC, tasklist.due ASC, pages.name ASC, tasklist.id ASC ''', (parentid, today)): yield row for row in self.db.execute( ''' SELECT tasklist.* FROM tasklist LEFT JOIN pages ON tasklist.source = pages.id WHERE tasklist.open=1 and tasklist.parent=? and tasklist.start>? ORDER BY tasklist.start ASC, tasklist.prio DESC, tasklist.due ASC, pages.name ASC, tasklist.id ASC ''', (parentid, today)): yield row def list_open_tasks_flatlist(self): '''List tasks @returns: a list of tasks as sqlite Row objects ''' # Sort: # started tasks by prio, due date, page + id to keep order in page # not-started tasks by start date, ... today = str(datetime.date.today()) for row in self.db.execute( ''' SELECT tasklist.* FROM tasklist LEFT JOIN pages ON tasklist.source = pages.id WHERE tasklist.open=1 and tasklist.start<=? and hasopenchildren=0 ORDER BY tasklist.prio DESC, tasklist.due ASC, pages.name ASC, tasklist.id ASC ''', (today, )): yield row for row in self.db.execute( ''' SELECT tasklist.* FROM tasklist LEFT JOIN pages ON tasklist.source = pages.id WHERE tasklist.open=1 and tasklist.start>? and hasopenchildren=0 ORDER BY tasklist.start ASC, tasklist.prio DESC, tasklist.due ASC, pages.name ASC, tasklist.id ASC ''', (today, )): yield row def get_task(self, taskid): row = self.db.execute('SELECT * FROM tasklist WHERE id=?', (taskid, )).fetchone() return row def get_path(self, task): '''Get the L{Path} for the source of a task @param task: the task (as returned by L{list_tasks()} @returns: an L{IndexPath} object ''' return self._pages.get_pagename(task['source'])
class TasksView(IndexView): '''Database "view" that shows tasks that are indexed''' def __init__(self, db): IndexView.__init__(self, db) self._pages = PagesViewInternal(db) # Test the db really has a tasklist try: db.execute('SELECT * FROM tasklist LIMIT 1') except sqlite3.OperationalError: raise ValueError('No tasklist in index') def list_tasks_in_work_order(self, source): ''' list tasks in order that they can be worked :param source: :return: row dict with keys ['id', 'source', 'parent', 'haschildren', 'hasopenchildren', 'open', 'prio', 'start', 'due', 'tags', 'description'] ''' import networkx as nx parent_id = 0 G = nx.DiGraph() tasks = {} def add_nodes(parent, source, dependant): for row in self.db.execute( ''' SELECT tasklist.*, pages.name as name FROM tasklist LEFT JOIN pages ON tasklist.source = pages.id WHERE tasklist.open=1 and tasklist.parent=? and pages.name=? ORDER BY tasklist.prio DESC, tasklist.due ASC, pages.name ASC, tasklist.id ASC ''', (parent, source)): G.add_node(row['id']) tasks[row['id']] = row if dependant: # parent depends on child to complete to start # arg2 depends on arg1 being finished first G.add_edge(row['id'], dependant) if row['dependency_page']: _, dep_page = self._pages.resolve_link( Path(source), HRef.new_from_wiki_link(row['dependency_page'])) add_nodes(0, dep_page.name, row['id']) add_nodes(row['id'], source, row['id']) add_nodes(parent_id, source, parent_id) added = set() for id in nx.topological_sort(G): if set(G.predecessors(id)) & added: # task has predecessors that are listed as open # add to the list so that its parents don't get added either added.add(id) continue yield tasks[id] added.add(id) def list_open_tasks(self, parent=None, source=None): '''List tasks @param parent: the parent task (as returned by this method) or C{None} to list all top level tasks @returns: a list of tasks at this level as sqlite Row objects ''' if parent: parentid = parent['id'] else: parentid = 0 if source: today = str(datetime.date.today()) for row in self.db.execute( ''' SELECT tasklist.* FROM tasklist LEFT JOIN pages ON tasklist.source = pages.id WHERE tasklist.open=1 and tasklist.parent=? and pages.name=? and tasklist.start<=? ORDER BY tasklist.prio DESC, tasklist.due ASC, pages.name ASC, tasklist.id ASC ''', (parentid, source, today)): yield row else: # Sort: # started tasks by prio, due date, page + id to keep order in page # not-started tasks by start date, ... today = str(datetime.date.today()) for row in self.db.execute( ''' SELECT tasklist.* FROM tasklist LEFT JOIN pages ON tasklist.source = pages.id WHERE tasklist.open=1 and tasklist.parent=? and tasklist.start<=? ORDER BY tasklist.prio DESC, tasklist.due ASC, pages.name ASC, tasklist.id ASC ''', (parentid, today)): yield row for row in self.db.execute( ''' SELECT tasklist.* FROM tasklist LEFT JOIN pages ON tasklist.source = pages.id WHERE tasklist.open=1 and tasklist.parent=? and tasklist.start>? ORDER BY tasklist.start ASC, tasklist.prio DESC, tasklist.due ASC, pages.name ASC, tasklist.id ASC ''', (parentid, today)): yield row def list_open_tasks_flatlist(self): '''List tasks @returns: a list of tasks as sqlite Row objects ''' # Sort: # started tasks by prio, due date, page + id to keep order in page # not-started tasks by start date, ... today = str(datetime.date.today()) for row in self.db.execute( ''' SELECT tasklist.* FROM tasklist LEFT JOIN pages ON tasklist.source = pages.id WHERE tasklist.open=1 and tasklist.start<=? and hasopenchildren=0 ORDER BY tasklist.prio DESC, tasklist.due ASC, pages.name ASC, tasklist.id ASC ''', (today, )): yield row for row in self.db.execute( ''' SELECT tasklist.* FROM tasklist LEFT JOIN pages ON tasklist.source = pages.id WHERE tasklist.open=1 and tasklist.start>? and hasopenchildren=0 ORDER BY tasklist.start ASC, tasklist.prio DESC, tasklist.due ASC, pages.name ASC, tasklist.id ASC ''', (today, )): yield row def get_task(self, taskid): row = self.db.execute('SELECT * FROM tasklist WHERE id=?', (taskid, )).fetchone() return row def get_path(self, task): '''Get the L{Path} for the source of a task @param task: the task (as returned by L{list_tasks()} @returns: an L{IndexPath} object ''' return self._pages.get_pagename(task['source'])