-
Notifications
You must be signed in to change notification settings - Fork 7
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
Adds API to get library-specific info on inventory reports. (PP-1210) #1840
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1840 +/- ##
==========================================
+ Coverage 90.10% 90.12% +0.01%
==========================================
Files 299 300 +1
Lines 39530 39563 +33
Branches 8585 8595 +10
==========================================
+ Hits 35618 35655 +37
+ Misses 2590 2586 -4
Partials 1322 1322 ☔ View full report in Codecov by Sentry. |
IntegrationConfiguration.goal == Goals.LICENSE_GOAL, | ||
not_( | ||
IntegrationConfiguration.settings_dict.contains( | ||
{"include_in_inventory_report": False} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got most of this select
from the code for generating the reports, but I'm not sure where the "include_in_inventory_report" setting is coming from. I do see it here in OPDS collection settings, but I don't see it getting set to false on other collection types that probably shouldn't be included in inventory reports. I looked around a bit, but didn't see it set to either true
or false
. Here's the query I used:
SELECT *
FROM integration_configurations ic
WHERE ic.goal = 'LICENSE_GOAL'
AND (
ic.settings @> '{"include_in_inventory_report": true}' OR
ic.settings @> '{"include_in_inventory_report": false}'
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So getting this info in a pure SQL query is hard, mostly due to our use of inheritance to set the include_in_inventory_report
setting.
Some background on how this is set:
You are correct this setting is getting set in the OPDS collection settings:
circulation/src/palace/manager/core/opds_import.py
Lines 131 to 141 in b1ab61a
include_in_inventory_report: bool = FormField( | |
True, | |
form=ConfigurationFormItem( | |
label=_("Include in inventory report?"), | |
type=ConfigurationFormItemType.SELECT, | |
options={ | |
"true": "(Default) Yes", | |
"false": "No", | |
}, | |
), | |
) |
We don't store the default values for our serialized settings in the database. This is done in the dict()
method for the Pydantic settings class:
circulation/src/palace/manager/integration/settings.py
Lines 337 to 341 in 530f25b
def dict(self, *args: Any, **kwargs: Any) -> dict[str, Any]: | |
"""Override the dict method to remove the default values""" | |
if "exclude_defaults" not in kwargs: | |
kwargs["exclude_defaults"] = True |
I thought this was better called out in the comments, but I can't find it right now, so we should probably add a better comment adding these details. But we're not storing the defaults when we serialize to the database, so it is easier to update the defaults in the future.
When we are interacting with the settings via the Pydantic class (how I was envisioning we would mostly interact with them, rather then querying, this is transparent, since the defaults get re-filled when the values are loaded from the database).
So the only value that ever gets saved out to the DB is include_in_inventory_report == False
. So that is why the code that finds the integrations when generating the reports is structured the way it is.
First it finds any license integrations where include_in_inventory_report == False
is not in the settings via a query.
circulation/src/palace/manager/celery/tasks/generate_inventory_and_hold_reports.py
Lines 66 to 79 in 530f25b
# resolve integrations | |
integrations = session.scalars( | |
select(IntegrationConfiguration) | |
.join(IntegrationLibraryConfiguration) | |
.where( | |
IntegrationLibraryConfiguration.library_id == self.library_id, | |
IntegrationConfiguration.goal == Goals.LICENSE_GOAL, | |
not_( | |
IntegrationConfiguration.settings_dict.contains( | |
{"include_in_inventory_report": False} | |
) | |
), | |
) | |
).all() |
Then it filters down this list, to only those integrations that actually contain this setting:
circulation/src/palace/manager/celery/tasks/generate_inventory_and_hold_reports.py
Lines 80 to 86 in 530f25b
registry = LicenseProvidersRegistry() | |
integration_ids: list[int] = [] | |
for integration in integrations: | |
settings = registry[integration.protocol].settings_load(integration) | |
if not isinstance(settings, OPDSImporterSettings): | |
continue | |
integration_ids.append(integration.id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this code is missing that last filtering step, it will return collections without the setting, which I don't think is what we want here.
Perhaps since this query is so complicated, it should get broken out to a utility function that both the report generation and the controller can call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the query in inventory_report_info
is going to return unrelated collections, so some additional filtering is needed. I added some explanation in a comment. After that change this PR looks good to me.
IntegrationConfiguration.goal == Goals.LICENSE_GOAL, | ||
not_( | ||
IntegrationConfiguration.settings_dict.contains( | ||
{"include_in_inventory_report": False} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So getting this info in a pure SQL query is hard, mostly due to our use of inheritance to set the include_in_inventory_report
setting.
Some background on how this is set:
You are correct this setting is getting set in the OPDS collection settings:
circulation/src/palace/manager/core/opds_import.py
Lines 131 to 141 in b1ab61a
include_in_inventory_report: bool = FormField( | |
True, | |
form=ConfigurationFormItem( | |
label=_("Include in inventory report?"), | |
type=ConfigurationFormItemType.SELECT, | |
options={ | |
"true": "(Default) Yes", | |
"false": "No", | |
}, | |
), | |
) |
We don't store the default values for our serialized settings in the database. This is done in the dict()
method for the Pydantic settings class:
circulation/src/palace/manager/integration/settings.py
Lines 337 to 341 in 530f25b
def dict(self, *args: Any, **kwargs: Any) -> dict[str, Any]: | |
"""Override the dict method to remove the default values""" | |
if "exclude_defaults" not in kwargs: | |
kwargs["exclude_defaults"] = True |
I thought this was better called out in the comments, but I can't find it right now, so we should probably add a better comment adding these details. But we're not storing the defaults when we serialize to the database, so it is easier to update the defaults in the future.
When we are interacting with the settings via the Pydantic class (how I was envisioning we would mostly interact with them, rather then querying, this is transparent, since the defaults get re-filled when the values are loaded from the database).
So the only value that ever gets saved out to the DB is include_in_inventory_report == False
. So that is why the code that finds the integrations when generating the reports is structured the way it is.
First it finds any license integrations where include_in_inventory_report == False
is not in the settings via a query.
circulation/src/palace/manager/celery/tasks/generate_inventory_and_hold_reports.py
Lines 66 to 79 in 530f25b
# resolve integrations | |
integrations = session.scalars( | |
select(IntegrationConfiguration) | |
.join(IntegrationLibraryConfiguration) | |
.where( | |
IntegrationLibraryConfiguration.library_id == self.library_id, | |
IntegrationConfiguration.goal == Goals.LICENSE_GOAL, | |
not_( | |
IntegrationConfiguration.settings_dict.contains( | |
{"include_in_inventory_report": False} | |
) | |
), | |
) | |
).all() |
Then it filters down this list, to only those integrations that actually contain this setting:
circulation/src/palace/manager/celery/tasks/generate_inventory_and_hold_reports.py
Lines 80 to 86 in 530f25b
registry = LicenseProvidersRegistry() | |
integration_ids: list[int] = [] | |
for integration in integrations: | |
settings = registry[integration.protocol].settings_load(integration) | |
if not isinstance(settings, OPDSImporterSettings): | |
continue | |
integration_ids.append(integration.id) |
IntegrationConfiguration.goal == Goals.LICENSE_GOAL, | ||
not_( | ||
IntegrationConfiguration.settings_dict.contains( | ||
{"include_in_inventory_report": False} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this code is missing that last filtering step, it will return collections without the setting, which I don't think is what we want here.
Perhaps since this query is so complicated, it should get broken out to a utility function that both the report generation and the controller can call.
@jonathangreen I think this is ready for another look, when you have a chance. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me! The new route name seems like a much better fit.
Made one comment, but its super minor.
) -> list[IntegrationConfiguration]: | ||
"""Return a list of collections to report for the given library.""" | ||
if session is None: | ||
session = Session.object_session(library) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: Since session
is always supplied, and the fallback isn't tested, maybe we just make session
required.
Description
/admin/reports/generate_inventory_report/:library_short_name
GET method endpoint to provide information about the collections that will be included if an inventory report is generated..../apidoc/swagger#/admin.inventory
.../apidoc/redoc#tag/admin.inventory
...api_routes.MockControllerMethod
class to make it a little easier to verify calls to controller methods on routes for which content is validated on the way out.Motivation and Context
It is useful to know which collections will be included in the inventory reports. This is primarily for users of the Palace Manage admin user interface.
[Jira PP-1210]
How Has This Been Tested?
curl
and with an updated Admin UI client.Checklist