Skip to content

Commit

Permalink
Merge pull request #189 from opaduchak/fix/ENG-6747
Browse files Browse the repository at this point in the history
[ENG-6747] BE: Mendeley reconfigure error
  • Loading branch information
adlius authored Dec 21, 2024
2 parents 4dd0b25 + 7d488dc commit 802956a
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 46 deletions.
35 changes: 31 additions & 4 deletions addon_imps/citations/mendeley.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,43 @@ async def list_root_collections(self) -> ItemSampleResult:
]
)

async def get_item_info(self, item_id: str):
if item_id == "ROOT":
return ItemResult(
item_id="ROOT",
item_type=ItemType.COLLECTION,
item_name="ROOT",
)
item_type, parsed_id = item_id.split(":", maxsplit=1)
if item_type == "collection":
return await self._fetch_collection(parsed_id)
elif item_type == "document":
return await self._fetch_item_details(parsed_id)

async def _fetch_collection(self, item_id: str):
async with self.network.GET(f"folders/{item_id}") as response:
collection = await response.json_content()
return ItemResult(
item_id=f'{ItemType.COLLECTION}:{collection["id"]}',
item_name=collection["name"],
item_type=ItemType.COLLECTION,
)

async def list_collection_items(
self,
collection_id: str,
filter_items: ItemType | None = None,
) -> ItemSampleResult:
parsed_id = (
collection_id
if collection_id == "ROOT"
else collection_id.split(":", maxsplit=1)[1]
)
tasks = []
if filter_items != ItemType.COLLECTION:
tasks.append(self._fetch_collection_documents(collection_id))
tasks.append(self._fetch_collection_documents(parsed_id))
if filter_items != ItemType.DOCUMENT:
tasks.append(self._fetch_subcollections(collection_id))
tasks.append(self._fetch_subcollections(parsed_id))
items = await join_list(tasks)

return ItemSampleResult(items=items, total_count=len(items))
Expand Down Expand Up @@ -81,7 +108,7 @@ async def _fetch_item_details(
item_name = item_details.get("title", f"Untitled Document {item_id}")
csl_data = _citation_for_mendeley_document(item_id, item_details)
return ItemResult(
item_id=item_id,
item_id=f"{ItemType.DOCUMENT}:{item_id}",
item_name=item_name,
item_type=ItemType.DOCUMENT,
item_path=item_details.get("path", []),
Expand All @@ -93,7 +120,7 @@ def _parse_collection_response(
) -> ItemSampleResult:
items = [
ItemResult(
item_id=collection["id"],
item_id=f'{ItemType.COLLECTION}:{collection["id"]}',
item_name=collection["name"],
item_type=ItemType.COLLECTION,
)
Expand Down
74 changes: 51 additions & 23 deletions addon_imps/citations/zotero_org.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
)


ROOT_ITEM_ID = "ROOT"


class ZoteroOrgCitationImp(CitationAddonImp):
async def get_external_account_id(self, auth_result_extras: dict[str, str]) -> str:
user_id = auth_result_extras.get("userID")
Expand Down Expand Up @@ -57,27 +60,34 @@ async def list_root_collections(self) -> ItemSampleResult:
collections = await response.json_content()
items = [
ItemResult(
item_id=f'{collection["id"]}:',
item_id=f'{ItemType.COLLECTION}:{collection["id"]}:{ROOT_ITEM_ID}',
item_name=collection["data"].get("name", "Unnamed Library"),
item_type=ItemType.COLLECTION,
)
for collection in collections
]
items.append(
ItemResult(
item_id="personal:",
item_id=f"{ItemType.COLLECTION}:personal:{ROOT_ITEM_ID}",
item_name="My Library",
item_type=ItemType.COLLECTION,
)
)
return ItemSampleResult(items=items, total_count=len(items))

async def get_item_info(self, item_id: str) -> ItemResult:
item_type, library, id_ = item_id.split(":")
if item_type == ItemType.COLLECTION:
return await self._fetch_collection(library, id_)
elif item_type == ItemType.DOCUMENT:
return await self._fetch_document(library, id_)

async def list_collection_items(
self,
collection_id: str,
filter_items: ItemType | None = None,
) -> ItemSampleResult:
library, collection = collection_id.split(":")
_, library, collection = collection_id.split(":")
tasks = []
if filter_items != ItemType.COLLECTION:
tasks.append(self.fetch_collection_documents(library, collection))
Expand All @@ -87,39 +97,57 @@ async def list_collection_items(
return ItemSampleResult(items=all_items, total_count=len(all_items))

async def fetch_subcollections(self, library, collection):
prefix = self.resolve_collection_prefix(library, collection)
async with self.network.GET(f"{prefix}/collections/top") as response:
prefix = f"{self.resolve_collection_prefix(library, collection)}/collections"
if collection == "ROOT":
prefix = f"{prefix}/top"
async with self.network.GET(prefix) as response:
items_json = await response.json_content()
return [
ItemResult(
item_id=f'{library}:{item["key"]}',
item_name=item["data"].get("name", "Unnamed title"),
item_type=ItemType.COLLECTION,
)
for item in items_json
]
return [self._parse_collection(item, library) for item in items_json]

@staticmethod
def _parse_collection(item: dict, library: str) -> ItemResult:
return ItemResult(
item_id=f'{ItemType.COLLECTION}:{library}:{item["key"]}',
item_name=item["data"].get("name", "Unnamed title"),
item_type=ItemType.COLLECTION,
)

async def fetch_collection_documents(self, library, collection):
prefix = self.resolve_collection_prefix(library, collection)
async with self.network.GET(
f"{prefix}/items/top", query={"format": "csljson"}
) as response:
items_json = await response.json_content()
return [
ItemResult(
item_id=f'{library}:{item["id"]}',
item_name=item.get("title", "Unnamed title"),
item_type=ItemType.DOCUMENT,
csl=item,
)
for item in items_json["items"]
]
return [self._parse_document(item, library) for item in items_json["items"]]

@staticmethod
def _parse_document(item: dict, library: str) -> ItemResult:
return ItemResult(
item_id=f'{ItemType.DOCUMENT}:{library}:{item["id"]}',
item_name=item.get("title", "Unnamed title"),
item_type=ItemType.DOCUMENT,
csl=item,
)

def resolve_collection_prefix(self, library: str, collection):
def resolve_collection_prefix(self, library: str, collection="ROOT"):
if library == "personal":
prefix = f"users/{self.config.external_account_id}"
else:
prefix = f"groups/{library}"
if collection != "ROOT":
prefix = f"{prefix}/collections/{collection}"
return prefix

async def _fetch_collection(self, library: str, collection_id: str) -> ItemResult:
prefix = self.resolve_collection_prefix(library, collection_id)
async with self.network.GET(prefix, query={"format": "csljson"}) as response:
raw_collection = await response.json_content()
return self._parse_collection(raw_collection, library)

async def _fetch_document(self, library: str, document_id: str) -> ItemResult:
prefix = self.resolve_collection_prefix(library)
async with self.network.GET(
f"{prefix}/items/{document_id}", query={"format": "csljson"}
) as response:
raw_collection = await response.json_content()
return self._parse_document(raw_collection, library)
14 changes: 9 additions & 5 deletions addon_imps/tests/citations/test_mendeley.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ async def test_fetch_collection_documents(self):

expected_items = [
ItemResult(
item_id="doc1",
item_id="document:doc1",
item_name="Doc Title 1",
item_type=ItemType.DOCUMENT,
item_path=[],
Expand All @@ -122,7 +122,7 @@ async def test_fetch_collection_documents(self):
},
),
ItemResult(
item_id="doc2",
item_id="document:doc2",
item_name="Doc Title 2",
item_type=ItemType.DOCUMENT,
item_path=[],
Expand Down Expand Up @@ -150,10 +150,14 @@ async def test_fetch_subcollections(self):
expected_result = ItemSampleResult(
items=[
ItemResult(
item_id="1", item_name="Collection 1", item_type=ItemType.COLLECTION
item_id="collection:1",
item_name="Collection 1",
item_type=ItemType.COLLECTION,
),
ItemResult(
item_id="2", item_name="Collection 2", item_type=ItemType.COLLECTION
item_id="collection:2",
item_name="Collection 2",
item_type=ItemType.COLLECTION,
),
],
total_count=2,
Expand Down Expand Up @@ -205,7 +209,7 @@ async def test_list_collection_items(self):
) in cases:
with self.subTest(item_filter):
result = await self.mendeley_imp.list_collection_items(
"collection_id", filter_items=item_filter
"collection:collection_id", filter_items=item_filter
)
for call in calls_to_be_made:
call.assert_awaited_once_with("collection_id")
Expand Down
37 changes: 25 additions & 12 deletions addon_imps/tests/citations/test_zotero.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,17 @@ async def test_list_root_collections(self):

expected_items = [
ItemResult(
item_id="collection-1:",
item_id="collection:collection-1:ROOT",
item_name="Collection 1",
item_type=ItemType.COLLECTION,
),
ItemResult(
item_id="collection-2:",
item_id="collection:collection-2:ROOT",
item_name="Collection 2",
item_type=ItemType.COLLECTION,
),
ItemResult(
item_id="personal:",
item_id="collection:personal:ROOT",
item_name="My Library",
item_type=ItemType.COLLECTION,
),
Expand Down Expand Up @@ -108,7 +108,9 @@ async def test_list_collection_items(self):
self.zotero_imp.fetch_subcollections = create_autospec(
self.zotero_imp.fetch_subcollections, return_value=collections
)
result = await self.zotero_imp.list_collection_items("personal:collection-123")
result = await self.zotero_imp.list_collection_items(
"collection:personal:collection-123"
)

expected_items = docs + collections
self.zotero_imp.fetch_collection_documents.assert_awaited_once_with(
Expand Down Expand Up @@ -156,7 +158,7 @@ async def test_list_collection_items_with_filter(self):
for item_type, call, not_call in cases:
with self.subTest(item_type):
result = await self.zotero_imp.list_collection_items(
"personal:collection-123", filter_items=item_type
"collection:personal:collection-123", filter_items=item_type
)
call.assert_awaited_once_with("personal", "collection-123")
not_call.assert_not_called()
Expand All @@ -180,13 +182,13 @@ async def test_fetch_collection_documents(self):
)
expected_result = [
ItemResult(
item_id="personal:item-1",
item_id="document:personal:item-1",
item_name="Item Title 1",
item_type=ItemType.DOCUMENT,
csl={"id": "item-1", "title": "Item Title 1"},
),
ItemResult(
item_id="personal:item-2",
item_id="document:personal:item-2",
item_name="Item Title 2",
item_type=ItemType.DOCUMENT,
csl={"id": "item-2", "title": "Item Title 2"},
Expand Down Expand Up @@ -214,17 +216,28 @@ async def test_fetch_subcollections(self):
)
expected_result = [
ItemResult(
item_id="personal:collection-1",
item_id="collection:personal:collection-1",
item_name="Collection 1",
item_type=ItemType.COLLECTION,
),
ItemResult(
item_id="personal:collection-2",
item_id="collection:personal:collection-2",
item_name="Collection 2",
item_type=ItemType.COLLECTION,
),
]
result = await self.zotero_imp.fetch_subcollections("personal", "collection")
with self.subTest("ROOT collection"):
result = await self.zotero_imp.fetch_subcollections("personal", "ROOT")

self.assertEqual(result, expected_result)
self.zotero_imp.network.GET.assert_called_once_with("la/collections/top")
self.assertEqual(result, expected_result)
self.zotero_imp.network.GET.assert_called_once_with("la/collections/top")

self.zotero_imp.network.GET.reset_mock()

with self.subTest("ordinary collection"):
result = await self.zotero_imp.fetch_subcollections(
"personal", "some-collection"
)

self.assertEqual(result, expected_result)
self.zotero_imp.network.GET.assert_called_once_with("la/collections")
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ def get_root_folder_for_provider(node_settings, service_name):
case "bitbucket":
return f"repository:{node_settings.user}/{node_settings.repo}"
case "zotero":
return f"{node_settings.library_id}/{node_settings.list_id}"
return f"collection:{node_settings.library_id}:{node_settings.list_id}"
case "mendeley":
return node_settings.list_id
return f"collection:{node_settings.list_id}"
case "boa":
return None

Expand Down
4 changes: 4 additions & 0 deletions addon_toolkit/interfaces/citation.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ def list_collection_items(
) -> ItemSampleResult:
"""Lists directories (or collections) and sources (books) inside root"""

@immediate_operation(capability=AddonCapabilities.ACCESS)
def get_item_info(self, item_id: str) -> ItemResult:
"""Lists directories (or collections) and sources (books) inside root"""


@dataclasses.dataclass(frozen=True)
class CitationAddonImp(AddonImp):
Expand Down

0 comments on commit 802956a

Please sign in to comment.