Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

collections: added task to compute num of records #1853

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions invenio_rdm_records/collections/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ def create(cls, slug, title, query, ctree=None, parent=None, order=None, depth=2
)

@classmethod
def resolve(cls, *, id_=None, slug=None, ctree_id=None, depth=2):
"""Resolve a collection by ID or slug.
def read(cls, *, id_=None, slug=None, ctree_id=None, depth=2):
"""Read a collection by ID or slug.

To resolve by slug, the collection tree ID must be provided.
To read by slug, the collection tree ID must be provided.
"""
res = None
if id_:
Expand All @@ -89,10 +89,21 @@ def resolve(cls, *, id_=None, slug=None, ctree_id=None, depth=2):
return res

@classmethod
def resolve_many(cls, ids_=None, depth=2):
"""Resolve many collections by ID."""
_ids = ids_ or []
return [cls(c, depth) for c in cls.model_cls.read_many(_ids)]
def read_many(cls, ids_, depth=2):
"""Read many collections by ID."""
return [cls(c, depth) for c in cls.model_cls.read_many(ids_)]

@classmethod
def read_all(cls, depth=2):
"""Read all collections."""
return [cls(c, depth) for c in cls.model_cls.read_all()]

def update(self, **kwargs):
"""Update the collection."""
if "search_query" in kwargs:
Collection.validate_query(kwargs["search_query"])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about using self here, even if it is a classmethod, instead of hardcoding?

Suggested change
Collection.validate_query(kwargs["search_query"])
self.validate_query(self, kwargs["search_query"])

Otherwise, it is probably better to define validate_query as a static method, given that I can't override it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

created an issue: #1867

self.model.update(**kwargs)
return self

def add(self, slug, title, query, order=None, depth=2):
"""Add a subcollection to the collection."""
Expand Down Expand Up @@ -128,7 +139,10 @@ def query(self):
@cached_property
def ancestors(self):
"""Get the collection ancestors."""
return Collection.resolve_many(self.split_path_to_ids())
ids_ = self.split_path_to_ids()
if not ids_:
return []
return Collection.read_many(ids_)

@cached_property
def subcollections(self):
Expand Down
20 changes: 18 additions & 2 deletions invenio_rdm_records/collections/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,25 @@ def get_by_slug(cls, slug, tree_id):
return cls.query.filter(cls.slug == slug, cls.tree_id == tree_id).one_or_none()

@classmethod
def read_many(cls, ids):
def read_many(cls, ids_):
"""Get many collections by ID."""
return cls.query.filter(cls.id.in_(ids)).order_by(cls.path, cls.order)
return cls.query.filter(cls.id.in_(ids_)).order_by(cls.path, cls.order)

@classmethod
def read_all(cls):
"""Get all collections.

The collections are ordered by ``path`` and ``order``, which means:

- By path: the collections are ordered in a breadth-first manner (first come the root collection, then the next level, and so on)
- By order: between the same level collections, they are ordered by the specified order field.
"""
return cls.query.order_by(cls.path, cls.order)

def update(self, **kwargs):
"""Update a collection."""
for key, value in kwargs.items():
setattr(self, key, value)

@classmethod
def get_children(cls, model):
Expand Down
7 changes: 7 additions & 0 deletions invenio_rdm_records/collections/resources/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2024 CERN.
#
# Invenio-RDM is free software; you can redistribute it and/or modify
# it under the terms of the MIT License; see LICENSE file for more details.
"""Collection resource module."""
50 changes: 50 additions & 0 deletions invenio_rdm_records/collections/resources/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2024 CERN.
#
# Invenio-RDM is free software; you can redistribute it and/or modify
# it under the terms of the MIT License; see LICENSE file for more details.
"""Collection resource config."""

from flask_resources import (
HTTPJSONException,
JSONSerializer,
ResourceConfig,
ResponseHandler,
create_error_handler,
)
from invenio_records_resources.resources.records.args import SearchRequestArgsSchema
from invenio_records_resources.resources.records.headers import etag_headers
from marshmallow.fields import Integer

from invenio_rdm_records.resources.serializers import UIJSONSerializer

from ..errors import CollectionNotFound


class CollectionsResourceConfig(ResourceConfig):
"""Configuration for the Collection resource."""

blueprint_name = "collections"
url_prefix = "/collections"

routes = {
"search-records": "/<id>/records",
}

request_view_args = {"id": Integer()}
request_search_args = SearchRequestArgsSchema
error_handlers = {
CollectionNotFound: create_error_handler(
HTTPJSONException(
code=404,
description="Collection was not found.",
)
),
}
response_handlers = {
"application/json": ResponseHandler(JSONSerializer(), headers=etag_headers),
"application/vnd.inveniordm.v1+json": ResponseHandler(
UIJSONSerializer(), headers=etag_headers
),
}
43 changes: 43 additions & 0 deletions invenio_rdm_records/collections/resources/resource.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2024 CERN.
#
# Invenio-RDM is free software; you can redistribute it and/or modify
# it under the terms of the MIT License; see LICENSE file for more details.
"""Collection resource."""

from flask import g
from flask_resources import Resource, resource_requestctx, response_handler, route
from invenio_records_resources.resources.records.resource import (
request_search_args,
request_view_args,
)


class CollectionsResource(Resource):
"""Collection resource."""

def __init__(self, config, service):
"""Instantiate the resource."""
super().__init__(config)
self.service = service

def create_url_rules(self):
"""Create the URL rules for the record resource."""
routes = self.config.routes
return [
route("GET", routes["search-records"], self.search_records),
]

@request_view_args
@request_search_args
@response_handler(many=True)
def search_records(self):
"""Search records in a collection."""
id_ = resource_requestctx.view_args["id"]
records = self.service.search_collection_records(
g.identity,
id_,
params=resource_requestctx.args,
)
return records.to_dict(), 200
5 changes: 0 additions & 5 deletions invenio_rdm_records/collections/searchapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,5 @@ def search_app_context():
sort_options=current_app.config["RDM_SORT_OPTIONS"],
headers={"Accept": "application/vnd.inveniordm.v1+json"},
pagination_options=(10, 25, 50, 100),
# endpoint=/communities/eu/records
# endpoint=/api/records
# hidden_params=[
# ["q", collection.query]
# ]
)
}
7 changes: 7 additions & 0 deletions invenio_rdm_records/collections/services/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2024 CERN.
#
# Invenio-RDM is free software; you can redistribute it and/or modify
# it under the terms of the MIT License; see LICENSE file for more details.
"""Collection service module."""
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@ class CollectionServiceConfig(ServiceConfig, ConfiguratorMixin):
schema = CollectionSchema

links_item = {
"search": ConditionalLink(
cond=lambda coll, ctx: coll.community,
if_=CollectionLink("/api/communities/{community}/records"),
else_="/api/records",
),
"search": CollectionLink("/api/collections/{id}/records"),
"self_html": ConditionalLink(
cond=lambda coll, ctx: coll.community,
if_=CollectionLink(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ def vars(collection, vars):
"community": collection.community.slug,
"tree": collection.collection_tree.slug,
"collection": collection.slug,
"id": collection.id,
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ def to_dict(self):
res = {
"root": self._collection.id,
self._collection.id: {
**self._schema.dump(self._collection),
**self._schema.dump(
self._collection, context={"identity": self._identity}
),
"children": list(),
"links": self._links_tpl.expand(self._identity, self._collection),
},
Expand All @@ -76,7 +78,7 @@ def to_dict(self):
if _c.id not in res:
# Add the subcollection to the dictionary
res[_c.id] = {
**self._schema.dump(_c),
**self._schema.dump(_c, context={"identity": self._identity}),
"children": list(),
"links": self._links_tpl.expand(self._identity, _c),
}
Expand Down Expand Up @@ -121,22 +123,30 @@ def query(self):
class CollectionList(ServiceListResult):
"""Collection list item."""

def __init__(self, collections):
def __init__(self, identity, collections, schema, links_tpl, links_item_tpl):
"""Instantiate a Collection list item."""
self._identity = identity
self._collections = collections
self._schema = schema
self._links_tpl = links_tpl
self._links_item_tpl = links_item_tpl

def to_dict(self):
"""Serialize the collection list to a dictionary."""
res = []
for collection in self._collections:
_r = collection.to_dict()
_r["links"] = CollectionItem(collection).links
_r = CollectionItem(
self._identity, collection, self._schema, self._links_item_tpl
).to_dict()
res.append(_r)
return res

def __iter__(self):
"""Iterate over the collections."""
return iter(self._collections)
return (
CollectionItem(self._identity, x, self._schema, self._links_item_tpl)
for x in self._collections
)


class CollectionTreeItem:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ class CollectionSchema(Schema):

slug = fields.Str()
title = fields.Str()
depth = fields.Int()
depth = fields.Int(dump_only=True)
order = fields.Int()
id = fields.Int()
id = fields.Int(dump_only=True)
num_records = fields.Int()
search_query = fields.Str(load_only=True)
Loading