Skip to content

Commit

Permalink
Enhance codebase with event stream changes
Browse files Browse the repository at this point in the history
  • Loading branch information
subhashb committed Jul 17, 2024
1 parent 2304c5c commit 56f9393
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 46 deletions.
2 changes: 1 addition & 1 deletion src/lending/app/dailysheet.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def overdue_checkouts(self):
)


@lending.event_handler(stream_name="patron")
@lending.event_handler(stream_category="library::patron")
class DailySheetManager:
@handle(HoldExpired)
def handle_hold_expired(self, event: HoldExpired):
Expand Down
2 changes: 1 addition & 1 deletion src/lending/model/book.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class Book:
)


@lending.event_handler(part_of=Book, stream_name="patron")
@lending.event_handler(part_of=Book, stream_category="library::patron")
class PatronHoldEventsHandler:
@handle(HoldPlaced)
def mark_book_on_hold(self, event: HoldPlaced):
Expand Down
4 changes: 3 additions & 1 deletion src/lending/model/patron.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class HoldExpired:
"""Event raised when a hold on a book placed by a patron expires"""

patron_id = Identifier(required=True)
patron_type = String(required=True)
hold_id = Identifier(required=True)
branch_id = Identifier(required=True)
book_id = Identifier(required=True)
Expand Down Expand Up @@ -90,6 +91,7 @@ def expire(self):
self.raise_(
HoldExpired(
patron_id=self._owner.id,
patron_type=self._owner.patron_type,
hold_id=self.id,
branch_id=self.branch_id,
book_id=self.book_id,
Expand Down Expand Up @@ -217,7 +219,7 @@ def overdue(self):
)


@lending.aggregate
@lending.aggregate(fact_events=True)
class Patron:
"""A user of the public library who can place holds on books, check out books,
and interact with their current holds and checkouts via their patron profile.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ Feature: Track Checkouts
Then the daily sheet contains the ACTIVE checkout record

Scenario: Patron returns a book on or before the due date
Given a patron has checked out a book
Given a circulating book is available
And a patron is logged in
And the patron has checked out a book
When the patron returns the book
Then the daily sheet contains the RETURNED checkout record

Expand Down
121 changes: 79 additions & 42 deletions tests/lending/bdd/daily_sheet/step_defs/daily_sheet_steps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import date, timedelta

import pytest
from protean import UnitOfWork
from protean.exceptions import ValidationError
from protean.globals import current_domain, g
from pytest_bdd import given, then, when
Expand All @@ -11,6 +12,7 @@
DailySheet,
DailySheetService,
HoldType,
Patron,
checkout,
place_hold,
)
Expand All @@ -30,69 +32,83 @@ def reset_globals():

@given("a circulating book is available")
def persisted_circulating_book(book):
current_domain.repository_for(Book).add(book)
g.current_book = book
current_domain.repository_for(Book).add(book)


@given("a regular patron is logged in")
@given("a patron is logged in")
@given("the patron is logged in")
def regular_patron(regular_patron):
current_domain.repository_for(Patron).add(regular_patron)
g.current_user = regular_patron


@given("a patron has an active hold")
@given("a closed-ended hold is placed")
def patron_with_active_hold(patron, book):
current_domain.repository_for(Patron).add(patron)
g.current_user = patron

g.current_book = book
current_domain.repository_for(Book).add(book)
g.current_book = book

place_hold(g.current_user, book, "1", HoldType.CLOSED_ENDED)()
with UnitOfWork():
refreshed_patron = current_domain.repository_for(Patron).get(patron.id)
place_hold(refreshed_patron, book, "1", HoldType.CLOSED_ENDED)()
current_domain.repository_for(Patron).add(refreshed_patron)


@given("the hold has reached its expiry date")
def hold_expired():
g.current_user.holds[0].expires_on = date.today() - timedelta(days=1)
with UnitOfWork():
refreshed_patron = current_domain.repository_for(Patron).get(g.current_user.id)
refreshed_patron.holds[0].expires_on = date.today() - timedelta(days=1)
current_domain.repository_for(Patron).add(refreshed_patron)


@given("a patron has checked out a book")
@given("the patron has checked out a book")
def patron_with_checkout(regular_patron, book):
g.current_user = regular_patron

g.current_book = book
current_domain.repository_for(Book).add(book)

checkout(g.current_user, g.current_book, "1")()
with UnitOfWork():
refreshed_patron = current_domain.repository_for(Patron).get(regular_patron.id)
checkout(refreshed_patron, g.current_book, "1")()
current_domain.repository_for(Patron).add(refreshed_patron)


@given("the checkout is beyond its due date")
def the_checkout_is_beyond_its_due_date():
g.current_user.checkouts[0].due_on = date.today() - timedelta(days=1)
with UnitOfWork():
refreshed_patron = current_domain.repository_for(Patron).get(g.current_user.id)
refreshed_patron.checkouts[0].due_on = date.today() - timedelta(days=1)
current_domain.repository_for(Patron).add(refreshed_patron)


@given("the system has generated a daily sheet for expiring holds")
def generated_daily_sheet_for_expiring_holds(patron, book):
# Log in Patron
current_domain.repository_for(Patron).add(patron)
g.current_user = patron

# Persist Book
g.current_book = book
current_domain.repository_for(Book).add(book)
g.current_book = book

# Place Hold
place_hold(g.current_user, g.current_book, "1", HoldType.CLOSED_ENDED)()
current_domain.publish(g.current_user._events)
refreshed_patron = current_domain.repository_for(Patron).get(patron.id)
place_hold(refreshed_patron, g.current_book, "1", HoldType.CLOSED_ENDED)()
current_domain.repository_for(Patron).add(refreshed_patron)

# Expire Hold
g.current_user.holds[0].expires_on = date.today() - timedelta(days=1)
refreshed_patron = current_domain.repository_for(Patron).get(patron.id)
refreshed_patron.holds[0].expires_on = date.today() - timedelta(days=1)
current_domain.repository_for(Patron).add(refreshed_patron)

# Update DailySheet's expiry
refreshed_patron = current_domain.repository_for(Patron).get(patron.id)
daily_sheet_repo = current_domain.repository_for(DailySheet)
record = daily_sheet_repo.find_hold_for_patron(
g.current_user.id, g.current_user.holds[0].id
refreshed_patron.id, refreshed_patron.holds[0].id
)
record.hold_expires_on = date.today() - timedelta(days=1)
daily_sheet_repo.add(record)
Expand All @@ -102,25 +118,32 @@ def generated_daily_sheet_for_expiring_holds(patron, book):
def generated_daily_sheet_for_overdue_checkouts(patron, book):
# Log in Patron
g.current_user = patron
current_domain.repository_for(Patron).add(patron)

# Persist Book
g.current_book = book
current_domain.repository_for(Book).add(book)

# Checkout Book
checkout(g.current_user, g.current_book, "1")()
current_domain.publish(g.current_user._events)

# Update Book's Due Date
g.current_user.checkouts[0].due_on = date.today() - timedelta(days=1)

# Update DailySheet's Due Date
daily_sheet_repo = current_domain.repository_for(DailySheet)
record = daily_sheet_repo.find_checkout_for_patron(
g.current_user.id, g.current_user.checkouts[0].id
)
record.checkout_due_on = date.today() - timedelta(days=1)
daily_sheet_repo.add(record)
with UnitOfWork():
refreshed_patron = current_domain.repository_for(Patron).get(patron.id)
checkout(refreshed_patron, g.current_book, "1")()
current_domain.repository_for(Patron).add(refreshed_patron)

with UnitOfWork():
# Update Book's Due Date
refreshed_patron = current_domain.repository_for(Patron).get(g.current_user.id)
refreshed_patron.checkouts[0].due_on = date.today() - timedelta(days=1)
current_domain.repository_for(Patron).add(refreshed_patron)

# Update DailySheet's Due Date
refreshed_patron = current_domain.repository_for(Patron).get(g.current_user.id)
daily_sheet_repo = current_domain.repository_for(DailySheet)
record = daily_sheet_repo.find_checkout_for_patron(
refreshed_patron.id, refreshed_patron.checkouts[0].id
)
record.checkout_due_on = date.today() - timedelta(days=1)
daily_sheet_repo.add(record)


@when("the patron places a hold on the book")
Expand All @@ -129,41 +152,53 @@ def generated_daily_sheet_for_overdue_checkouts(patron, book):
@when("the patron tries to place an additional hold")
def place_hold_on_book():
try:
place_hold(g.current_user, g.current_book, "1", HoldType.CLOSED_ENDED)()
current_domain.publish(g.current_user._events)
refreshed_patron = current_domain.repository_for(Patron).get(g.current_user.id)
place_hold(refreshed_patron, g.current_book, "1", HoldType.CLOSED_ENDED)()
current_domain.repository_for(Patron).add(refreshed_patron)
except ValidationError as exc:
g.current_exception = exc


@when("the patron cancels the hold")
def cancel_hold():
patron = g.current_user
patron.cancel_hold(patron.holds[0].id)
current_domain.publish(g.current_user._events)
with UnitOfWork():
refreshed_patron = current_domain.repository_for(Patron).get(g.current_user.id)
refreshed_patron.cancel_hold(refreshed_patron.holds[0].id)
current_domain.repository_for(Patron).add(refreshed_patron)


@when("the system checks for expiring holds")
@when("the system processes the overdue checkouts")
@when("the system processes the expiring holds")
def run_daily_sheet_service():
DailySheetService(patrons=[g.current_user]).run()
current_domain.publish(g.current_user._events)
with UnitOfWork():
patron = current_domain.repository_for(Patron).get(g.current_user.id)
DailySheetService(patrons=[patron]).run()
current_domain.repository_for(Patron).add(patron)


@when("the patron checks out the book")
def checkout_book():
try:
checkout(g.current_user, g.current_book, "1")()
current_domain.publish(g.current_user._events)
with UnitOfWork():
refreshed_patron = current_domain.repository_for(Patron).get(
g.current_user.id
)
checkout(refreshed_patron, g.current_book, "1")()
current_domain.repository_for(Patron).add(refreshed_patron)
except ValidationError as exc:
g.current_exception = exc


@when("the patron returns the book")
def patron_return_book():
try:
g.current_user.return_book(g.current_book.id)
current_domain.publish(g.current_user._events)
with UnitOfWork():
refreshed_patron = current_domain.repository_for(Patron).get(
g.current_user.id
)
refreshed_patron.return_book(g.current_book.id)
current_domain.repository_for(Patron).add(refreshed_patron)
except ValidationError as exc:
g.current_exception = exc

Expand Down Expand Up @@ -221,18 +256,20 @@ def generate_daily_sheet_for_overdue_checkouts(patron, book):
@then(cfparse("the daily sheet contains the {status} hold record"))
def confirm_daily_sheet_contains_hold(status):
repo = current_domain.repository_for(DailySheet)
refreshed_patron = current_domain.repository_for(Patron).get(g.current_user.id)
daily_sheet = repo.find_hold_for_patron(
g.current_user.id, g.current_user.holds[0].id
refreshed_patron.id, refreshed_patron.holds[0].id
)
assert daily_sheet is not None
assert daily_sheet.hold_status == status


@then(cfparse("the daily sheet contains the {status} checkout record"))
def confirm_daily_sheet_contains_checkout(status):
refreshed_patron = current_domain.repository_for(Patron).get(g.current_user.id)
repo = current_domain.repository_for(DailySheet)
daily_sheet = repo.find_checkout_for_patron(
g.current_user.id, g.current_user.checkouts[0].id
refreshed_patron.id, refreshed_patron.checkouts[0].id
)
assert daily_sheet is not None
assert daily_sheet.checkout_status == status
Expand Down

0 comments on commit 56f9393

Please sign in to comment.