Skip to content

Commit

Permalink
add attachment doc path lookup
Browse files Browse the repository at this point in the history
  • Loading branch information
plandes committed Sep 19, 2024
1 parent e00a9fe commit 09b14d7
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 25 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- BetterBibtex citation key output with the `lookup` command line action.
- Integration tests with mock Zotero database. Now all tests are self
contained.
- An API and command line to give attachment (paper document) paths based on an
item key. This can be combined with the item ID to citation ID mapping if
starting with a BetterBibtex citation ID.


## [0.9.1] - 2024-01-02
Expand Down
17 changes: 14 additions & 3 deletions resources/app.conf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#@meta {desc: 'command line config', date: '2024-09-18'}

[cli]
apps = list: prog_cli, ${cli_config_default:apps}, app, capp, papp
apps = list: prog_cli, ${cli_config_default:apps}, app, qapp, capp, papp
app_removes = list: override_cli
cleanups = list: prog_cli, ${cli_config_default:cleanups}, app, app_decorator, capp, papp
doc = This project exports your local Zotero library to a usable HTML website.
Expand Down Expand Up @@ -60,14 +60,25 @@ option_overrides = dict: {
'metavar': 'DIR'}}
mnemonic_overrides = dict: {'print_structure': 'print'}

[qapp]
class_name = zensols.zotsite.QueryApplication
resource = instance: zs_resource

[qapp_decorator]
option_excludes = set: resource
option_overrides = dict: {
'key': {'short_name': 'k'}}
mnemonic_overrides = dict: {
'find_path': 'docpath'}

[capp]
class_name = zensols.zotsite.CiteApplication
resource = instance: zs_resource

[capp_decorator]
option_excludes = set: resource
option_overrides = dict: {
'key': {'short_name': 'k'}}

[papp]
class_name = zensols.zotsite.PrototypeApplication
export_app = instance: app
cite_app = instance: capp
40 changes: 25 additions & 15 deletions resources/zotero.sql
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,45 @@ select c.collectionId c_id, ci.itemId c_iid,
where c.libraryId = ? and
c.collectionName like ?


-- name=select_items_attachments
select c.collectionId c_id, c.parentCollectionId c_pid,
c.collectionName c_name,
select c.collectionId c_id, c.parentCollectionId c_pid, c.collectionName c_name,
it.itemId i_id, ia.parentItemId i_pid, it.key, iy.typeName type,
ia.contentType content_type, ia.path,
itn.title n_title, itn.note n_note, itn.parentItemId n_pid
from items it, itemTypes iy
left join itemAttachments ia on it.itemId = ia.itemId
left join collectionItems ci on ci.itemId = it.itemId
left join collections c on c.collectionId = ci.collectionId
left join itemNotes itn on it.itemId = itn.itemId
left join itemAttachments ia on it.itemId = ia.itemId
left join collectionItems ci on ci.itemId = it.itemId
left join collections c on c.collectionId = ci.collectionId
left join itemNotes itn on it.itemId = itn.itemId
where it.itemTypeId = iy.itemTypeId and
it.itemId not in (select itemId from deletedItems)
it.itemId not in (select itemId from deletedItems)
order by ci.orderIndex;

-- name=select_item_metadata
select f.fieldName name, iv.value
from items i, itemTypes it, itemData id, itemDataValues iv, fields f
where i.itemTypeId = it.itemTypeId and
i.itemId = id.itemId and
id.valueId = iv.valueId and
id.fieldId = f.fieldId and
i.itemId = ? and
i.itemId not in (select itemId from deletedItems);
i.itemId = id.itemId and
id.valueId = iv.valueId and
id.fieldId = f.fieldId and
i.itemId = ? and
i.itemId not in (select itemId from deletedItems);

-- name=select_item_creators
select c.firstName, c.lastName
from itemCreators ic, creators c
where ic.creatorID = c.creatorID and
ic.itemID = ?
order by ic.orderIndex;
ic.itemID = ?
order by ic.orderIndex;

-- name=select_item_paths
select it.key, ia.path
from items it, itemTypes iy
left join itemAttachments ia on it.itemId = ia.itemId
left join collectionItems ci on ci.itemId = it.itemId
left join collections c on c.collectionId = ci.collectionId
left join itemNotes itn on it.itemId = itn.itemId
where it.itemTypeId = iy.itemTypeId and
it.itemId not in (select itemId from deletedItems) and
path is not null
order by ci.orderIndex;
46 changes: 40 additions & 6 deletions src/python/zensols/zotsite/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""
__author__ = 'Paul Landes'

from typing import Dict, Any
from typing import Iterable, Dict, Any
from dataclasses import dataclass, field
import re
import logging
Expand Down Expand Up @@ -74,6 +74,43 @@ def print_structure(self):
self.site_creator.print_structure()


@dataclass
class QueryApplication(object):
"""Query the Zotero database.
"""
resource: Resource = field()
"""Zotsite first class objects."""

def find_path(self, key: str = None):
"""Output paths in the form ``<item key>=<path>``.
:param key: key in format ``<library ID>_<item key>``, standard input
if not given, or ``all`` for every entry
"""
def strip_lib_id(s: str) -> str:
"""See TODO in :obj:`.ZoteroDatabase.paths`."""
return re.sub(r'^1_', '', s)

paths: Dict[str, str] = self.resource.zotero_db.paths
keys: Iterable[str]
if key is None:
keys = map(lambda s: s.strip(), sys.stdin.readlines())
keys = map(strip_lib_id)
elif key == 'all':
keys = paths.keys()
else:
keys = [strip_lib_id(key)]
if len(keys) == 1:
print(paths[keys[0]])
else:
key: str
for key in keys:
path: Path = paths[key]
print(f'{key}={path}')


@dataclass
class CiteApplication(object):
"""Map Zotero keys to BetterBibtex citekeys.
Expand All @@ -85,7 +122,7 @@ class CiteApplication(object):
def lookup(self, format: str = '{itemKey}={citationKey}', key: str = None):
"""Look up a citation key and print out BetterBibtex field(s).
:param key: key in format ``<libraryID>_<citationKey>``, standard input
:param key: key in format ``<library ID>_<item key>``, standard input
if not given, or ``all`` for every entry
:param format: the format of the output or ``json`` for all fields
Expand Down Expand Up @@ -114,9 +151,6 @@ class PrototypeApplication(object):
CLI_META = {'is_usage_visible': False}

config_factory: ConfigFactory = field()
export_app: ExportApplication = field()
cite_app: CiteApplication = field()

def proto(self):
lib = self.export_app.resource.zotero_db.get_library()
print(lib)
self.config_factory('qapp').find_path(item_key='1_VBAMCMJX')
24 changes: 23 additions & 1 deletion src/python/zensols/zotsite/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
from typing import Tuple, List, Dict, Any, Union, Optional
from dataclasses import dataclass, field
import logging
import re
from pathlib import Path
from zensols.db import DbPersister
from . import Collection, Library, Item, Note, Name
from . import Collection, Library, Item, Note, Name, ZoteroApplicationError


logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -188,3 +190,23 @@ def get_library(self) -> Library:
finally:
# deallocate pooled connection (configured in ``obj.conf``)
self._persister.conn_manager.dispose_all()

@property
def paths(self) -> Dict[str, Path]:
"""The paths of items by key.
"""
def map_row(r: Tuple[Any, ...]) -> Tuple[str, Path]:
key: str = r['key']
path: Path = storage_dir / key / re.sub(pat, '', r['path'])
return key, path

pat: re.Pattern = re.compile(r'^storage:')
storage_dir: Path = self._data_dir / 'storage'
try:
# TODO: add library and collection_like to params and SQL
rows: Tuple[Dict[str, Any], ...] = self._select('item_paths')
return dict(map(map_row, rows))
except Exception as e:
raise ZoteroApplicationError(
f'Could not access Zotero database: {e}') from e

0 comments on commit 09b14d7

Please sign in to comment.