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

[PP-1456] Add loan count column to audio book playback time reports. #2002

Merged
merged 14 commits into from
Sep 4, 2024

Conversation

dbernstein
Copy link
Contributor

@dbernstein dbernstein commented Aug 21, 2024

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

  • I have updated the documentation accordingly.
  • All new and existing tests passed.

@dbernstein dbernstein marked this pull request as draft August 21, 2024 22:40
@dbernstein dbernstein force-pushed the track-loan-in-playtime-entries branch from ba0e30d to 7938839 Compare August 22, 2024 01:14
Copy link

codecov bot commented Aug 22, 2024

Codecov Report

Attention: Patch coverage is 95.65217% with 1 line in your changes missing coverage. Please review.

Project coverage is 90.68%. Comparing base (de62c4e) to head (f56fcd5).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
.../palace/manager/api/controller/playtime_entries.py 92.30% 1 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

@dbernstein dbernstein marked this pull request as ready for review August 22, 2024 04:11
@dbernstein dbernstein force-pushed the track-loan-in-playtime-entries branch 2 times, most recently from d3ce2cb to e34ca0c Compare August 22, 2024 04:12
@dbernstein dbernstein added the feature New feature label Aug 22, 2024
@dbernstein dbernstein requested a review from a team August 22, 2024 15:44
Copy link
Contributor

@tdilauro tdilauro left a 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.

src/palace/manager/api/controller/playtime_entries.py Outdated Show resolved Hide resolved
Comment on lines 68 to 84
.where(
LicensePool.identifier == identifier,
Loan.patron == flask.request.patron,
)
.order_by(Loan.start.desc())
Copy link
Contributor

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",
Copy link
Contributor

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",
        ),
    )```

Copy link
Contributor Author

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",
Copy link
Contributor

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).

Copy link
Contributor Author

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!!! :)

Copy link
Contributor

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.

src/palace/manager/sqlalchemy/model/time_tracking.py Outdated Show resolved Hide resolved
Copy link
Contributor

@tdilauro tdilauro left a 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.

Comment on lines +238 to +247
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,
Copy link
Contributor

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!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(blush)

Comment on lines +327 to +330
None if isbn == "" else isbn,
collection_name,
library_name,
title,
None if title == "" else title,
Copy link
Contributor

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.

Copy link
Contributor Author

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,
Copy link
Contributor

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?

Copy link
Contributor Author

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",
Copy link
Contributor

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)
Copy link
Contributor

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?

Copy link
Contributor Author

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
Copy link
Contributor

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.

Comment on lines 81 to 82
Loan.start >= min_time_entry,
Loan.start + default_loan_period < max_time_entry,
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll fix that.

@dbernstein dbernstein force-pushed the track-loan-in-playtime-entries branch 3 times, most recently from e82dec9 to 11f85db Compare September 3, 2024 16:31
@dbernstein
Copy link
Contributor Author

@tdilauro : I've made updates to the PR to address the issues you raised.

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.
Copy link
Contributor

@tdilauro tdilauro left a 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!! 🚀

@dbernstein dbernstein merged commit 3f5d5ae into main Sep 4, 2024
20 checks passed
@dbernstein dbernstein deleted the track-loan-in-playtime-entries branch September 4, 2024 20:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants