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-2018] Replace email attachments with links to S3 on time tracking… #2214

Merged
merged 2 commits into from
Dec 18, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def run(self) -> None:

uid = uuid.uuid4()
key = (
f"inventory_and_holds/{library.short_name}/"
f"{S3Service.DOWNLOADS_PREFIX}/inventory_and_holds/{library.short_name}/"
f"inventory-and-holds-for-library-{file_name_modifier}-{uid}.zip"
)
self.s3_service.store_stream(
Expand Down
32 changes: 26 additions & 6 deletions src/palace/manager/scripts/playtime_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import argparse
import csv
import os
import uuid
from collections import defaultdict
from collections.abc import Iterable
from datetime import datetime, timedelta
Expand All @@ -18,6 +19,7 @@

from palace.manager.core.config import Configuration
from palace.manager.scripts.base import Script
from palace.manager.service.storage.s3 import S3Service
from palace.manager.sqlalchemy.model.time_tracking import PlaytimeEntry, PlaytimeSummary
from palace.manager.util.datetime_helpers import previous_months, utc_now

Expand Down Expand Up @@ -182,17 +184,17 @@ def do_run(self):

email_subject = f"{subject_prefix}Playtime Summaries {formatted_start_date} - {formatted_until_date}"
reporting_name_with_no_spaces = reporting_name.replace(" ", "_") + "-"
attachment_extension = "csv"
attachment_name = (
link_extension = "csv"
linked_file_name = (
f"playtime-summary-{reporting_name_with_no_spaces}"
f"{formatted_start_date}-{formatted_until_date}.{attachment_extension}"
f"{formatted_start_date}-{formatted_until_date}.{link_extension}"
)

# Write to a temporary file so we don't overflow the memory
with TemporaryFile(
"w+",
prefix=f"playtimereport{formatted_until_date}",
suffix=attachment_extension,
suffix=link_extension,
) as temp:
# Write the data as a CSV
writer = csv.writer(temp)
Expand All @@ -207,12 +209,30 @@ def do_run(self):
recipient = os.environ.get(
Configuration.REPORTING_EMAIL_ENVIRONMENT_VARIABLE
)

if recipient:
uid = uuid.uuid4()
key = (
f"{S3Service.DOWNLOADS_PREFIX}/{reporting_name}/"
f"{linked_file_name}"
)

s3_service = self.services.storage.public()
s3_service.store_stream(
key,
temp,
content_type="text/csv",
)

s3_file_link = s3_service.generate_url(key)

self.services.email.send_email(
subject=email_subject,
receivers=[recipient],
text="",
attachments={attachment_name: temp.read()},
text=(
f"Download Report here -> {s3_file_link} \n\n"
f"This report will be available for download for 30 days."
),
)
else:
self.log.error("No reporting email found, logging complete report.")
Expand Down
1 change: 1 addition & 0 deletions src/palace/manager/service/storage/s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ def exception(self) -> BaseException | None:

class S3Service(LoggerMixin):
MINIMUM_MULTIPART_UPLOAD_SIZE = 5 * 1024 * 1024 # 5MB
DOWNLOADS_PREFIX = "downloads"

def __init__(
self,
Expand Down
16 changes: 11 additions & 5 deletions tests/manager/scripts/test_playtime_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from palace.manager.sqlalchemy.model.time_tracking import PlaytimeEntry, PlaytimeSummary
from palace.manager.util.datetime_helpers import datetime_utc, previous_months, utc_now
from tests.fixtures.database import DatabaseTransactionFixture
from tests.fixtures.services import ServicesEmailFixture
from tests.fixtures.services import ServicesEmailFixture, ServicesFixture


def create_playtime_entries(
Expand Down Expand Up @@ -533,6 +533,7 @@ def test_do_run(
self,
db: DatabaseTransactionFixture,
services_email_fixture: ServicesEmailFixture,
services_fixture: ServicesFixture,
):
identifier = db.identifier()
collection = db.default_collection()
Expand Down Expand Up @@ -663,6 +664,11 @@ def test_do_run(
)

reporting_name = "test cm"

mock_s3_service = MagicMock()
mock_s3_service.generate_url.return_value = "http://test"
services_fixture.services.storage.public.override(mock_s3_service)

with (
patch("palace.manager.scripts.playtime_entries.csv.writer") as writer,
patch(
Expand Down Expand Up @@ -799,15 +805,15 @@ def test_do_run(
# verify the number of unique loans
assert len(loan_identifiers) == sum([x.args[0][7] for x in call_args[1:]])
assert services_email_fixture.mock_emailer.send.call_count == 1
mock_s3_service.store_stream.assert_called_once()
mock_s3_service.generate_url.assert_called_once()
assert services_email_fixture.mock_emailer.send.call_args == call(
subject=f"{reporting_name}: Playtime Summaries {cutoff} - {until}",
sender=services_email_fixture.sender_email,
receivers=["[email protected]"],
text="",
text="Download Report here -> http://test \n\nThis report will be available for download for 30 days.",
html=None,
attachments={
f"playtime-summary-{reporting_name.replace(' ', '_')}-{cutoff}-{until}.csv": ""
}, # Mock objects do not write data
attachments=None,
)

def test_no_reporting_email(self, db: DatabaseTransactionFixture):
Expand Down
Loading