diff --git a/ci/Dockerfile b/ci/Dockerfile index 62c6f494..d312322c 100644 --- a/ci/Dockerfile +++ b/ci/Dockerfile @@ -23,7 +23,8 @@ RUN apt-get -qy update && apt-get install -y \ time \ tree \ netcat-openbsd \ - moreutils + moreutils \ + git RUN useradd --create-home user diff --git a/plugins/bearnotes/setup.py b/plugins/bearnotes/setup.py index 8170061a..163d548b 100644 --- a/plugins/bearnotes/setup.py +++ b/plugins/bearnotes/setup.py @@ -9,5 +9,8 @@ 'bear-db = wildland_bearnotes.backend:BearDBStorageBackend', 'bear-note = wildland_bearnotes.backend:BearNoteStorageBackend', ] - } + }, + install_requires=[ + 'pybear @ git+https://github.com/golemfoundation/pybear#egg=0.0.20200914', + ], ) diff --git a/plugins/bearnotes/wildland_bearnotes/backend.py b/plugins/bearnotes/wildland_bearnotes/backend.py index 98b254f9..dd661f0d 100644 --- a/plugins/bearnotes/wildland_bearnotes/backend.py +++ b/plugins/bearnotes/wildland_bearnotes/backend.py @@ -21,25 +21,27 @@ Bear storage backend ''' -from pathlib import PurePosixPath, Path -from typing import Iterable, Optional, List, Set, Dict, Tuple -import logging -from dataclasses import dataclass -from functools import partial import errno +import logging import os -import threading import re - import sqlite3 +import threading +from functools import partial +from pathlib import PurePosixPath, Path +from typing import Iterable, Optional, List, Set, Dict, Tuple + +import bear # pylint: disable=import-error,wrong-import-order import click from wildland.storage import Storage from wildland.container import Container from wildland.storage_backends.base import StorageBackend -from wildland.storage_backends.generated import \ - GeneratedStorageMixin, CachedDirEntry, \ - StaticFileEntry +from wildland.storage_backends.generated import ( + CachedDirEntry, + GeneratedStorageMixin, + StaticFileEntry, +) from wildland.storage_backends.watch import SimpleStorageWatcher from wildland.manifest.manifest import Manifest from wildland.manifest.schema import Schema @@ -48,23 +50,12 @@ logger = logging.getLogger('storage-bear') -@dataclass -class BearNote: +def get_md(note) -> bytes: ''' - Individual Bear note. + Get the contents of note Markdown file. ''' - ident: str - title: str - text: str - tags: List[str] - - def get_md(self) -> bytes: - ''' - Get the contents of note Markdown file. - ''' - content = 'title: ' + self.title + '\n---\n' + self.text + '\n' - return content.encode('utf-8') + return f'title: {note.title}\n---\n{note.text}\n'.encode('utf-8') def get_note_paths(tags: List[str]) -> List[PurePosixPath]: @@ -135,8 +126,8 @@ def connect(self): self.db_lock = self.conn_locks[self.path] self.conn_refcount[self.path] += 1 else: - self.db = sqlite3.connect(self.path, check_same_thread=False) - self.db.row_factory = sqlite3.Row + self.db = bear.Bear(self.path, connect=False) + self.db.connect(check_same_thread=False) self.db_lock = threading.RLock() self.conn_cache[self.path] = self.db self.conn_locks[self.path] = self.db_lock @@ -155,7 +146,7 @@ def disconnect(self): del self.conn_refcount[self.path] del self.conn_cache[self.path] del self.conn_locks[self.path] - self.db.close() +# self.db.close() self.db = None self.db_lock = None @@ -167,9 +158,8 @@ def get_note_idents(self) -> Iterable[str]: assert self.db and self.db_lock with self.db_lock: - cursor = self.db.cursor() - cursor.execute('SELECT ZUNIQUEIDENTIFIER FROM ZSFNOTE') - return [row['ZUNIQUEIDENTIFIER'] for row in cursor.fetchall()] + for note in self.db.notes(): + yield note.id def get_note_idents_with_tags(self) -> Iterable[Tuple[str, List[str]]]: ''' @@ -179,61 +169,18 @@ def get_note_idents_with_tags(self) -> Iterable[Tuple[str, List[str]]]: assert self.db and self.db_lock with self.db_lock: - cursor = self.db.cursor() - - result: Dict[str, List[str]] = { - ident: [] - for ident in self.get_note_idents() - } - cursor.execute(''' - SELECT ZUNIQUEIDENTIFIER, ZSFNOTETAG.ZTITLE FROM ZSFNOTE - JOIN Z_7TAGS ON Z_7TAGS.Z_7NOTES = ZSFNOTE.Z_PK - JOIN ZSFNOTETAG ON Z_7TAGS.Z_14TAGS = ZSFNOTETAG.Z_PK - ''') - for row in cursor.fetchall(): - ident = row['ZUNIQUEIDENTIFIER'] - tag = row['ZTITLE'] - result.setdefault(ident, []).append(tag) - return result.items() - - def get_note(self, ident: str) -> Optional[BearNote]: - ''' - Retrieve a single note. - ''' + for note in self.db.notes(): + yield note.id, [tag.title for tag in note.tags()] - assert self.db and self.db_lock - - with self.db_lock: - cursor = self.db.cursor() - cursor.execute(''' - SELECT Z_PK, ZTITLE, ZTEXT FROM ZSFNOTE - WHERE ZUNIQUEIDENTIFIER = ? - ''', [ident]) - row = cursor.fetchone() - if not row: - return None - - tags = self.get_tags(row['Z_PK']) - return BearNote(ident=ident, title=row['ZTITLE'], text=row['ZTEXT'], - tags=tags) - - def get_tags(self, pk: int) -> List[str]: + def get_note(self, ident: str) -> Optional[bear.Note]: ''' - Retrieve a list of tags for a note. + Retrieve a single note. ''' assert self.db and self.db_lock with self.db_lock: - cursor = self.db.cursor() - cursor.execute(''' - SELECT ZTITLE FROM ZSFNOTETAG - JOIN Z_7TAGS ON Z_7TAGS.Z_7NOTES = ? - AND Z_7TAGS.Z_14TAGS = ZSFNOTETAG.Z_PK - ''', [pk]) - - return [tag_row['ZTITLE'] for tag_row in cursor.fetchall()] - + return self.db.get_note(ident) class BearDBWatcher(SimpleStorageWatcher): ''' @@ -397,7 +344,7 @@ def _get_note(self, ident): note = self.bear_db.get_note(ident) if not note: raise FileNotFoundError(errno.ENOENT, '') - return note.get_md() + return get_md(note) class BearNoteStorageBackend(GeneratedStorageMixin, StorageBackend): @@ -464,4 +411,4 @@ def _dir_root(self): raise FileNotFoundError(errno.ENOENT, '') name = re.sub(r'[\0\\/:*?"<>|]', '-', note.title) - yield StaticFileEntry(f'{name}.md', note.get_md()) + yield StaticFileEntry(f'{name}.md', get_md(note))