Skip to content

Commit

Permalink
loan: better anonymize function
Browse files Browse the repository at this point in the history
Co-Authored-by: Peter Weber <peter.weber@rero.ch>
  • Loading branch information
rerowep committed May 15, 2024
1 parent 3e40d97 commit 5fda4e0
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 50 deletions.
99 changes: 51 additions & 48 deletions rero_ils/modules/loans/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,13 @@ def create(cls, data, id_=None, delete_pid=True,
dbcommit=False, reindex=False, **kwargs):
"""Create the loan record.
:param cls - class object
:param data - dictionary representing a loan record.
:param id_ - UUID, it would be generated if it is not given.
:param delete_pid - remove the pid present in the data if True,
:param dbcommit - commit the changes in the db after the creation.
:param reindex - index the record after the creation.
:param cls: class object
:param data: dictionary representing a loan record.
:param id_: UUID, it would be generated if it is not given.
:param delete_pid: remove the pid present in the data if True,
:param dbcommit: commit the changes in the db after the creation.
:param reindex: index the record after the creation.
:returns: the created record
"""
data['$schema'] = current_jsonschemas.path_to_url(cls._schema)
# default state assignment
Expand All @@ -213,7 +214,13 @@ def create(cls, data, id_=None, delete_pid=True,
reindex=reindex, **kwargs)

def update(self, data, commit=False, dbcommit=False, reindex=False):
"""Update loan record."""
"""Update loan record.
:param commit: if True push the db transaction.
:param dbcommit: make the change effective in db.
:param reindex: reindex the record.
:returns: the modified record
"""
self._loan_build_org_ref(data)
# set the field to_anonymize
if not self.get('to_anonymize') and Loan.can_anonymize(loan_data=data):
Expand All @@ -222,22 +229,23 @@ def update(self, data, commit=False, dbcommit=False, reindex=False):
data=data, commit=commit, dbcommit=dbcommit, reindex=reindex)
return self

def anonymize(self, loan, commit=True, dbcommit=False, reindex=False):
def anonymize(self, commit=True, dbcommit=False, reindex=False):
"""Anonymize a loan.
:param loan: the loan to update.
:param dbcommit - commit the changes in the db after the creation.
:param reindex - index the record after the creation.
:param commit: if True push the db transaction.
:param dbcommit: make the change effective in db.
:param reindex: reindex the record.
:returns: the modified record
"""
from rero_ils.modules.loans.logs.api import LoanOperationLog
loan['to_anonymize'] = True
self['to_anonymize'] = True
try:
super().update(loan, commit, dbcommit, reindex)
super().update(self, commit, dbcommit, reindex)
# Anonymize loan operation logs
LoanOperationLog.anonymize_logs(loan['pid'])
LoanOperationLog.anonymize_logs(self.pid)
except Exception as err:
current_app.logger.error(
f'Can not anonymize loan: {loan.get("pid")} {err}')
f'Can not anonymize loan: {self.get("pid")} {err}')
return self

def date_fields2datetime(self):
Expand Down Expand Up @@ -292,7 +300,7 @@ def patron_by_pid(pid, known_patrons):
:param pid: the patron pid.
:param known_patrons: already known patrons.
:return the corresponding patron.
:return: the corresponding patron.
"""
fields = ['pid', 'first_name', 'last_name', 'patron.barcode']
if pid not in known_patrons:
Expand All @@ -309,7 +317,7 @@ def location_by_pid(pid, known_locations):
:param pid: the location pid.
:param known_locations: already known locations.
:return the corresponding location.
:return: the corresponding location.
"""
fields = ['pid', 'name', 'library', 'pickup_name']
if pid not in known_locations:
Expand All @@ -327,7 +335,7 @@ def library_name_by_pid(pid, known_libraries):
:param pid: the library pid.
:param known_libraries: already known libraries.
:return the corresponding library.
:return: the corresponding library.
"""
if pid not in known_libraries:
results = LibrariesSearch()\
Expand All @@ -343,7 +351,7 @@ def holding_by_pid(pid, known_holdings):
:param pid: the holdings pid.
:param known_holdings: already known holdings.
:return the corresponding holdings.
:return: the corresponding holdings.
"""
from ..holdings.api import HoldingsSearch
if pid not in known_holdings:
Expand All @@ -360,7 +368,7 @@ def item_by_pid(pid, known_items):
:param pid: the item pid.
:param known_items: already known items.
:return the corresponding item.
:return: the corresponding item.
"""
fields = ['pid', 'barcode', 'call_number',
'second_call_number', 'library', 'location',
Expand All @@ -380,7 +388,7 @@ def item_type_by_pid(pid, known_ittys):
:param pid: the item_type pid.
:param known_ittys: already known item types
:return the corresponding item type
:return: the corresponding item type
"""
if pid not in known_ittys:
results = ItemTypesSearch()\
Expand Down Expand Up @@ -491,15 +499,14 @@ def is_loan_due_soon(self, tstamp=None):
:returns: True if is due soon
"""
date = tstamp or datetime.now(timezone.utc)
due_soon_date = self.get('due_soon_date')
if due_soon_date:
if due_soon_date := self.get('due_soon_date'):
return ciso8601.parse_datetime(due_soon_date) <= date
return False

def has_pending_transaction(self):
"""Check if a loan has pending patron transactions.
:return True if some open transaction is found, False otherwise
:return: True if some open transaction is found, False otherwise
"""
if pid := self.pid:
return PatronTransactionsSearch() \
Expand Down Expand Up @@ -614,9 +621,9 @@ def library_pid(self):
@property
def checkout_library_pid(self):
"""Get the checkout library pid."""
checkout_location = Location.get_record_by_pid(
self.get('checkout_location_pid'))
if checkout_location:
if checkout_location := Location.get_record_by_pid(
self.get('checkout_location_pid')
):
return checkout_location.library_pid

@cached_property
Expand Down Expand Up @@ -671,7 +678,7 @@ def get_overdue_fees(self):
loan is overdue, the circulation policy used will be related to the
checkout location, not the extended location.
:return An array of tuple. Each tuple are composed with two values :
:return: An array of tuple. Each tuple are composed with two values :
the fee amount and a related timestamp.
Ex: [
(0.1, datetime.date('2021-01-28')),
Expand Down Expand Up @@ -761,9 +768,9 @@ def get_notification_candidates(self, trigger):
generated based on another loan than itself (in case of request).
:param trigger: the fired action trigger (optional)
:return a list of tuple. Each tuple represent a notification candidate.
Each tuple is composed by 2 elements : the loan object, and the
related notification type.
:return: a list of tuple. Each tuple represent a notification
candidate. Each tuple is composed by 2 elements :
the loan object, and the related notification type.
"""
from rero_ils.modules.items.api import Item
candidates = []
Expand Down Expand Up @@ -867,18 +874,16 @@ def create_notification(self, trigger=None, _type=None, counter=0):
reminder_type = DUE_SOON_REMINDER_TYPE
if n_type != NotificationType.DUE_SOON:
reminder_type = OVERDUE_REMINDER_TYPE
reminder = cipo.get_reminder(reminder_type, counter)
# Reminder does not exists on the circulation policy.
if not reminder:
create = False
else:
if cipo.get_reminder(reminder_type, counter):
record['context']['reminder_counter'] = counter
else:
create = False

# create the notification and enqueue it.
if create:
notification = self._create_notification_resource(
record, dispatch=dispatch)
if notification:
if notification := self._create_notification_resource(
record, dispatch=dispatch):
notifications.append(notification)
return notifications

Expand Down Expand Up @@ -912,7 +917,7 @@ def get_anonymized_candidates(cls):
(we need to keep transactions for the last 3 months for circulation
management).
:return a generator of `Loan` candidate to anonymize.
:return: a generator of `Loan` candidate to anonymize.
"""
three_month_ago = datetime.now() - relativedelta(months=3)
six_month_ago = datetime.now() - relativedelta(months=6)
Expand All @@ -938,15 +943,15 @@ def get_anonymized_candidates(cls):
def is_concluded(self):
"""Check if loan can be considered as concluded or not.
:return True is the loan is concluded, False otherwise
:return: True is the loan is concluded, False otherwise
"""
return self.get('state') in LoanState.CONCLUDED and \
not self.has_pending_transaction()

def age(self):
"""Return the age of a loan in days.
:return the number of days since last transaction date.
:return: the number of days since last transaction date.
"""
if value := self.get('transaction_date'):
trans_date = ciso8601.parse_datetime(value)
Expand All @@ -968,7 +973,7 @@ def can_anonymize(cls, loan_data=None, patron=None):
:param loan_data: the loan data to check (could be a `Loan` or a dict).
:param patron: the patron to check.
:return True if the loan can be anonymized, False otherwise.
:return: True if the loan can be anonymized, False otherwise.
"""
# force `loan_data` as a `Loan` object instance if it's not yet.
loan = loan_data if isinstance(loan_data, cls) else cls(loan_data)
Expand Down Expand Up @@ -1019,7 +1024,7 @@ def action_required_params(action=None):
"""List of required parameters for circulation actions.
:param action: the action name to check.
:return the list of required parameters than the `Loan` must define to
:return: the list of required parameters than the `Loan` must define to
validate the action.
"""
shared_params = ['transaction_location_pid', 'transaction_user_pid']
Expand Down Expand Up @@ -1088,9 +1093,7 @@ def get_loans_by_item_pid_by_patron_pid(item_pid, patron_pid,
filter_states=filter_states,
)
search_result = search.execute()
if search_result.hits:
return search_result.hits.hits[0]['_source']
return {}
return search_result.hits.hits[0]['_source'] if search_result.hits else {}


def get_loans_stats_by_patron_pid(patron_pid):
Expand Down Expand Up @@ -1220,7 +1223,7 @@ def get_overdue_loan_pids(patron_pid=None, tstamp=None):
:param patron_pid: the patron pid. If none, return all overdue loans.
:param tstamp: a timestamp to define the execution time of the function.
Default to `datetime.now()`.
:return a generator of loan pid
:return: a generator of loan pid
"""
end_date = tstamp or datetime.now(timezone.utc)
end_date = end_date.strftime('%Y-%m-%dT%H:%M:%S.000Z')
Expand All @@ -1245,7 +1248,7 @@ def get_overdue_loans(patron_pid=None, tstamp=None):
:param patron_pid: the patron pid. If none, return all overdue loans.
:param tstamp: a timestamp to define the execution time of the function
:return a generator of Loan
:return: a generator of Loan
"""
for pid in get_overdue_loan_pids(patron_pid, tstamp):
yield Loan.get_record_by_pid(pid)
Expand Down
2 changes: 1 addition & 1 deletion rero_ils/modules/loans/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def loan_anonymizer(dbcommit=True, reindex=True):
counter = 0
for loan in Loan.get_anonymized_candidates():
if Loan.can_anonymize(loan_data=loan, patron=None):
loan.anonymize(loan, dbcommit=dbcommit, reindex=reindex)
loan.anonymize(dbcommit=dbcommit, reindex=reindex)
counter += 1

set_timestamp('anonymize-loans', count=counter)
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/loans/test_loans_operation_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def test_anonymize_logs(item2_on_loan_martigny_patron_and_loan_on_loan):
assert log['loan']['patron']['pid'] == patron['pid']
assert log['loan']['patron']['name'] == 'Roduit, Louis'

loan.anonymize(loan)
loan.anonymize(dbcommit=True, reindex=True)

logs = LoanOperationLogsSearch().get_logs_by_record_pid(loan['pid'])
assert len(logs) == 3
Expand Down

0 comments on commit 5fda4e0

Please sign in to comment.