-
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
[PP-1456] Add loan count column to audio book playback time reports. #2002
Conversation
ba0e30d
to
7938839
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2002 +/- ##
=======================================
Coverage 90.68% 90.68%
=======================================
Files 342 342
Lines 40517 40538 +21
Branches 8782 8784 +2
=======================================
+ Hits 36742 36763 +21
Misses 2510 2510
Partials 1265 1265 ☔ View full report in Codecov by Sentry. |
d3ce2cb
to
e34ca0c
Compare
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 have some concerns here about how isbn/title could affect loan counts. A few other concerns, as well. I didn't really get into the tests yet, since they might be affected by how we decide to deal with these other concerns.
.where( | ||
LicensePool.identifier == identifier, | ||
Loan.patron == flask.request.patron, | ||
) | ||
.order_by(Loan.start.desc()) |
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.
There should probably be a further constraint here: the loan should have been active during the timestamp extracted from the posted data.
"playtime_entries", | ||
[ | ||
"tracking_id", | ||
"timestamp", |
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.
The appearance of timestamp
in this downgrade is unexpected, since the previous index did not include it...
__table_args__ = (
UniqueConstraint(
"tracking_id",
"identifier_str",
"collection_name",
"library_name",
name="unique_playtime_entry",
),
)```
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.
Very good catch.
__table_args__ = ( | ||
UniqueConstraint( | ||
"tracking_id", | ||
"identifier_str", | ||
"collection_name", | ||
"library_name", | ||
"loan_identifier", |
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'm not entirely sure that we need to update the constraint for playtime_entries, since a given patron shouldn't be "reading" the same book both with and without a loan_identifier within the already-existing constraints (tracking_id
/timestamp, identifier_str
, etc.).
That said, there's probably no harm in it (other than increasing the size of the index, of course).
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.
Maybe we should leave it in there as a sanity check? I don't know.
I defer to your judgement on this one, not only because I respect your opinion but also because you are the Playtime Tracking Man!!! :)
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.
Regardless of what we do for playtime_entries
, I think we DO want loan_identifier
included in the constraint on playtime_summaries
.
9cb8bca
to
e5c2823
Compare
e5c2823
to
f938efc
Compare
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.
👏🏽 Nice solution to the isbn and/or title mismatch issue. Additional comments below.
sql_max(coalesce(PlaytimeSummary.isbn, "")).label("isbn2"), | ||
sql_max(coalesce(PlaytimeSummary.title, "")).label("title2"), | ||
count(distinct(PlaytimeSummary.loan_identifier)).label("loan_count"), | ||
) | ||
.where(PlaytimeSummary.timestamp.between(start, until)) | ||
.group_by( | ||
PlaytimeSummary.identifier_str, | ||
PlaytimeSummary.collection_name, | ||
PlaytimeSummary.library_name, | ||
PlaytimeSummary.isbn, | ||
PlaytimeSummary.title, | ||
sum(PlaytimeSummary.total_seconds_played), | ||
PlaytimeSummary.identifier_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.
This is a clever solution to a tricky problem!
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.
(blush)
None if isbn == "" else isbn, | ||
collection_name, | ||
library_name, | ||
title, | ||
None if title == "" else title, |
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: I assume that an empty string would be fine for both isbn
and title
.
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.
It should be.
LicensePool.identifier == identifier, | ||
Loan.patron == flask.request.patron, | ||
Loan.start >= min_time_entry, | ||
Loan.start + default_loan_period < max_time_entry, |
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.
Can we not just use Loan.end
here?
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.
Good point - I didn't see the end field.
__table_args__ = ( | ||
UniqueConstraint( | ||
"tracking_id", | ||
"identifier_str", | ||
"collection_name", | ||
"library_name", | ||
"loan_identifier", |
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.
Regardless of what we do for playtime_entries
, I think we DO want loan_identifier
included in the constraint on playtime_summaries
.
# Attach the identifier to the collection | ||
pool = db.licensepool( | ||
db.edition( | ||
identifier_type=identifier.type, identifier_id=identifier.identifier | ||
), | ||
collection=collection, | ||
) | ||
patron = db.patron() | ||
|
||
loan_identifier = resolve_loan_identifier(loan=None) |
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.
Should we verify here that the loan_identifier
is what we expect it to be?
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.
Sure why not.
loan_identifier6, | ||
) | ||
|
||
# log a summary where a title that was previously unavailable is now available for the same loan |
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.
It's more likely that book (or the entire collection) will be gone, though this probably covers the case for now.
Loan.start >= min_time_entry, | ||
Loan.start + default_loan_period < max_time_entry, |
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.
It doesn't look like we create any loans in our tests. If not, I'm not sure how the loan
lookup gets tested.
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'll fix that.
e82dec9
to
11f85db
Compare
@tdilauro : I've made updates to the PR to address the issues you raised. |
11f85db
to
aa5fe45
Compare
The main fix here is to ensure that loans are not over counted when associated title and/or isbn metadata changes between playtime entry updates for a given loan. Other fixes include: Removal of loan identifier from index Change loan_identifier field length from 50 to 40 Remove incorrect column "timestamp" in alembic downgrade Changed comment Added constraint to loan query.
aa5fe45
to
f56fcd5
Compare
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 this one is ready to go!! 🚀
Description
As the title indicates, this PR adds a loan count column to the audio book playback time reports.
When playback time data comes in through the API, the code attempts to resolve the loan associated with the audio book and patron. If the loan cannot be found (ie because it was purged) we use the user's patron id. Then we create a sha1 of the loan or patron ID in order to uniquely identify the loan.
Then on the reporting side, we perform a count of the unique loans within the time range and join that count to the seconds summation query.
The default value for the loan_identifier is an empty string. Since we don't have a way of associating loans or patrons with existing playtime records, I don't see a good way of counting old playtime entries and summaries. Once the start date of the reporting window passes the date of the release of this update to production the loan counting will be correct.
Before that, it will be one loan per unique isbn, collection, library.
Motivation and Context
https://ebce-lyrasis.atlassian.net/browse/PP-1456
How Has This Been Tested?
Updated the existing tests and added addition assertions.
Checklist