def test_db_session(self): ''' Test getting session for SQLite DB which passively creates a new DB (new .sqlite file) if it isn't present yet. ''' from frostmark import user_data from frostmark import db_base from frostmark.db import get_session from sqlalchemy.orm.session import Session folder = dirname(abspath(user_data.__file__)) self.assertNotIn(db_base.DB_NAME, listdir(folder)) # two sessions to check whether SQLA raises an Exception # e.g. if a table already exists and create_all() is called ses_one = get_session() self.assertIsInstance(ses_one, Session) ses_two = get_session() self.assertIsInstance(ses_two, Session) ses_one.close() ses_two.close() self.assertIn(db_base.DB_NAME, listdir(folder)) remove(join(folder, db_base.DB_NAME))
def test_edit_parent_folder_missingparent(self): ''' Test editing parent to a non-existing Folder. ''' from frostmark import db_base, user_data from frostmark.db import get_session from frostmark.models import Folder from frostmark.editor import Editor data = dirname(abspath(user_data.__file__)) self.assertNotIn(db_base.DB_NAME, listdir(data)) item = {'id': 321, 'folder_name': 'child'} session = get_session() try: session.add(Folder(**item)) session.commit() finally: session.close() with self.assertRaises(Exception): Editor.change_parent_folder(folder_id=item['id'], parent_id=2**31) self.assertIn(db_base.DB_NAME, listdir(data)) remove(join(data, db_base.DB_NAME))
def test_edit_parent_bookmark_missingchild(self): ''' Test editing parent of a non-existing Bookmark. ''' from frostmark import db_base, user_data from frostmark.db import get_session from frostmark.models import Bookmark from frostmark.editor import Editor data = dirname(abspath(user_data.__file__)) self.assertNotIn(db_base.DB_NAME, listdir(data)) item = { 'id': 321, 'title': 'child', 'url': '<url>', 'icon': b'<bytes>' } session = get_session() try: session.add(Bookmark(**item)) session.commit() finally: session.close() with self.assertRaises(Exception): Editor.change_parent_bookmark(bookmark_id=2**31, parent_id=item['id']) self.assertIn(db_base.DB_NAME, listdir(data)) remove(join(data, db_base.DB_NAME))
def change_parent_bookmark(bookmark_id: int, parent_id: int): ''' Change bookmark's parent if both the parent and the child exist and if the new parent isn't the child itself. ''' if bookmark_id == parent_id: raise Exception('Bookmark can not be its own parent') session = get_session() try: child = session.query(Bookmark).filter( Bookmark.id == bookmark_id).first() parent = session.query(Folder).filter( Folder.id == parent_id).first() if not child or not parent: raise Exception( f'Child: {child} or parent: {parent} does not exist') child.folder_id = parent.id session.commit() finally: session.close()
def test_edit_parent_folder(self): ''' Test editing a parent folder of a Folder. ''' from frostmark import db_base, user_data from frostmark.db import get_session from frostmark.models import Folder from frostmark.editor import Editor data = dirname(abspath(user_data.__file__)) self.assertNotIn(db_base.DB_NAME, listdir(data)) items = [{ 'id': 321, 'folder_name': 'old parent' }, { 'id': 666, 'folder_name': 'child', 'parent_folder_id': 321 }, { 'id': 123, 'folder_name': 'new parent' }] session = get_session() try: for item in items: session.add(Folder(**item)) session.commit() for item in items: self.assertEqual( session.query(Folder).filter( Folder.folder_name == item['folder_name']).first().id, item['id']) self.assertEqual( session.query(Folder).filter( Folder.id == items[1]['id']).first().parent_folder_id, items[0]['id']) Editor.change_parent_folder(folder_id=items[1]['id'], parent_id=items[2]['id']) self.assertEqual( session.query(Folder).filter( Folder.id == items[1]['id']).first().parent_folder_id, items[2]['id']) finally: session.close() self.assertIn(db_base.DB_NAME, listdir(data)) remove(join(data, db_base.DB_NAME))
def rename_bookmark(bookmark_id: int, name: str): ''' Change bookmark's title. ''' session = get_session() try: child = session.query(Bookmark).filter( Bookmark.id == bookmark_id).first() child.title = name session.commit() finally: session.close()
def rename_folder(folder_id: int, name: str): ''' Change folder's name. ''' session = get_session() try: child = session.query(Folder).filter( Folder.id == folder_id).first() child.folder_name = name session.commit() finally: session.close()
def change_bookmark_url(bookmark_id: int, url: str): ''' Change bookmark's title. ''' session = get_session() try: child = session.query(Bookmark).filter( Bookmark.id == bookmark_id).first() child.url = url session.commit() finally: session.close()
def test_change_bookmark_url(self): ''' Test renaming a Bookmark item. ''' from frostmark import db_base, user_data from frostmark.db import get_session from frostmark.models import Bookmark from frostmark.editor import Editor data = dirname(abspath(user_data.__file__)) self.assertNotIn(db_base.DB_NAME, listdir(data)) item = { 'id': 321, 'title': 'child', 'url': '<url>', 'icon': b'<bytes>' } session = get_session() try: session.add(Bookmark(**item)) session.commit() self.assertEqual( session.query(Bookmark).filter( Bookmark.title == item['title']).first().id, item['id']) self.assertEqual( session.query(Bookmark).filter( Bookmark.id == item['id']).first().title, item['title']) Editor.change_bookmark_url(bookmark_id=item['id'], url=f"http://{item['url']}") self.assertEqual( session.query(Bookmark).filter( Bookmark.id == item['id']).first().url, f"http://{item['url']}") finally: session.close() self.assertIn(db_base.DB_NAME, listdir(data)) remove(join(data, db_base.DB_NAME))
def fetch_folder_tree() -> Node: ''' Fetch folders only from the internal database, assemble a tree and return the root Node. ''' session = get_session() folders = [vars(item) for item in session.query(Folder).all()] session.close() for folder in folders: if folder['id'] != 0: continue folder['parent_folder_id'] = None tree = assemble_folder_tree(items=folders, key='parent_folder_id', node_type=Folder) return tree
def test_rename_folder(self): ''' Test renaming a Folder item. ''' from frostmark import db_base, user_data from frostmark.db import get_session from frostmark.models import Folder from frostmark.editor import Editor data = dirname(abspath(user_data.__file__)) self.assertNotIn(db_base.DB_NAME, listdir(data)) item = {'id': 321, 'folder_name': 'old parent'} session = get_session() try: session.add(Folder(**item)) session.commit() self.assertEqual( session.query(Folder).filter( Folder.folder_name == item['folder_name']).first().id, item['id']) self.assertEqual( session.query(Folder).filter( Folder.id == item['id']).first().folder_name, item['folder_name']) Editor.rename_folder(folder_id=item['id'], name=item['folder_name'][:3]) self.assertEqual( session.query(Folder).filter( Folder.id == item['id']).first().folder_name, item['folder_name'][:3]) finally: session.close() self.assertIn(db_base.DB_NAME, listdir(data)) remove(join(data, db_base.DB_NAME))
def fetch_bookmark_tree() -> Node: ''' Fetch folders and bookmarks from the internal database, assemble a bookmark tree and return the root Node ''' session = get_session() folders = [vars(item) for item in session.query(Folder).all()] bookmarks = [vars(item) for item in session.query(Bookmark).all()] session.close() for folder in folders: if folder['id'] != 0: continue folder['parent_folder_id'] = None tree = assemble_bookmark_tree(items=bookmarks, key='folder_id', folder_tree_root=assemble_folder_tree( items=folders, key='parent_folder_id', node_type=Folder), node_type=Bookmark) return tree
def test_edit_parent_bookmark(self): ''' Test editing a parent folder of a bookmark. ''' from frostmark import db_base, user_data from frostmark.db import get_session from frostmark.models import Folder, Bookmark from frostmark.editor import Editor data = dirname(abspath(user_data.__file__)) self.assertNotIn(db_base.DB_NAME, listdir(data)) items = [{ 'id': 321, 'folder_name': 'old parent', 'type': Folder }, { 'id': 666, 'title': 'child', 'folder_id': 321, 'type': Bookmark, 'url': '<url>', 'icon': b'<bytes>' }, { 'id': 123, 'folder_name': 'new parent', 'type': Folder }] session = get_session() try: for item in items: item_type = item.pop('type') if item_type == Folder: session.add(Folder(**item)) elif item_type == Bookmark: session.add(Bookmark(**item)) item['type'] = item_type session.commit() for item in items: item_type = item.pop('type') if item_type == Folder: self.assertEqual( session.query(Folder).filter( Folder.folder_name == item['folder_name']).first().id, item['id']) elif item_type == Bookmark: self.assertEqual( session.query(Bookmark).filter( Bookmark.title == item['title']).first().id, item['id']) item['type'] = item_type self.assertEqual( session.query(Bookmark).filter( Bookmark.id == items[1]['id']).first().folder_id, items[0]['id']) Editor.change_parent_bookmark(bookmark_id=items[1]['id'], parent_id=items[2]['id']) self.assertEqual( session.query(Bookmark).filter( Bookmark.id == items[1]['id']).first().folder_id, items[2]['id']) finally: session.close() self.assertIn(db_base.DB_NAME, listdir(data)) remove(join(data, db_base.DB_NAME))
def import_from(self, path: str): ''' Import bookmarks from particular path into internal storage. ''' # pylint: disable=too-many-locals backend = self.backend # get the bookmarks tree from file if isinstance(backend, FirefoxImporter): source = self._path_session(path=path, base=backend.BASE) elif isinstance(backend, OperaImporter): source = path elif isinstance(backend, ChromeImporter): source = path tree = backend.assemble_import_tree(source) # open internal DB nodes = traverse(tree) folders = {node.id: node for node in nodes if node.node_type == Folder} bookmarks = { node.id: node for node in nodes if node.node_type == Bookmark } # sqla objects frost = get_session() sqla_folders = {} # add folder structure to the database sorted_folders = sorted( folders.values(), # parent_folder_id is None for root folder key=lambda item: item.parent_folder_id or 0) # first sort the tree by parent IDs so that there is each parent # available, however in case there is a kind-of circular relationship # between the folders e.g. the ID of a child is smaller than ID of # a parent which might be caused by browser importing old bookmarks # from database directly or from a different browser while incorrectly # setting IDs (or better said re-using already existing IDs when # possible), therefore sorting by parent ID would work, but when trying # to access the parent a KeyError would be raised because of parent # not being available yet due to higher ID than the child has # # for that reason try to sort with parent ID first and postpone # the relationship evaluation by using second/third/etc/... item # in the sorted list until there is parent ID available (or throw # IndexError in the end which would pretty much mean that the browser # DB is just broken due to missing parent / dangling children) idx = 0 while sorted_folders: folder = sorted_folders[idx] kwargs = {'folder_name': folder.folder_name} if folder.parent_folder_id: # in case there is a parent, get the Folder object # and pull its ID after flush() (otherwise it's None) try: real_id = sqla_folders[folder.parent_folder_id].id except KeyError: idx += 1 continue kwargs['parent_folder_id'] = real_id new_folder = Folder(**kwargs) frost.add(new_folder) # flush to obtain folder ID, # especially necessary for nested folders frost.flush() # preserve the original ID and point to a SQLA object sqla_folders[folder.id] = new_folder # remove current folder idx = 0 sorted_folders.remove(folder) # add bookmarks for key in sorted(bookmarks.keys()): book = bookmarks[key] kwargs = { 'title': book.title, 'url': book.url, 'icon': b'', 'folder_id': sqla_folders[book.folder_id].id } # no need to flush, nothing required a bookmark # ID to be present before final commit() new_book = Bookmark(**kwargs) frost.add(new_book) # write data into internal DB frost.commit()