Skip to content

Commit

Permalink
holdings: add optional fields for holdings display
Browse files Browse the repository at this point in the history
With this commit, new optional fields are allowed for
the holdings of type serial. The fields are:
- enumerationAndChronology
- supplementaryContent
- index
- missing_issues
- notes

The new field second_call_number is available for
all types of holdings records

Item and holdings call numbers have no minimum character constraint.

* Closes #1284
* Removes the condition to have an item second call number only
if first call number exist.
* Adapts tests and fixtures accordingly.
* Adapts the public interface to display the new fields.

Co-Authored-by: Aly Badr <aly.badr@rero.ch>
  • Loading branch information
Aly Badr committed Oct 13, 2020
1 parent d12b65f commit 1625ee4
Show file tree
Hide file tree
Showing 17 changed files with 459 additions and 133 deletions.
29 changes: 29 additions & 0 deletions data/patterns.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@
{
"template_name": "quarterly_one_level",
"rero_control_number": "R003826164",
"call_number": "SM af-82",
"second_call_number": "EN 4624",
"enumerationAndChronology": "No 61(2020)-",
"missing_issues": "Hors s\u00e9rie 18 (2020)",
"notes": [
{
"type": "general_note",
"content": "3 derniers fascicules en salle de lecture"
},
{
"type": "conservation_note",
"content": "3 derni\u00e8res ann\u00e9es"
}
],
"patterns": {
"template": "no {{first_enumeration.level_1}} {{first_chronology.level_2}} {{first_chronology.level_1}}",
"frequency": "rdafr:1010",
Expand Down Expand Up @@ -141,6 +155,21 @@
{
"template_name": "quarterly_two_levels",
"rero_control_number": "R003301493",
"second_call_number": "CDU 904(374)",
"enumerationAndChronology": "Jg. 20(2020)-",
"index": "1 vol. (2020-2021)",
"supplementaryContent": "Sonderausgabe \"Teacher's guide\"",
"missing_issues": "Hors s\u00e9rie 18 (2020)",
"notes": [
{
"type": "reception_note",
"content": "A transmettre \u00e0 Astrid pour d\u00e9pouillement"
},
{
"type": "claim_note",
"content": "Contacter M. Filipini, par t\u00e9l\u00e9phone, pour les r\u00e9clamations"
}
],
"patterns": {
"template": "Jg. {{first_enumeration.level_1}} Heft {{first_chronology.level_2}} {{first_chronology.level_1}}",
"frequency": "rdafr:1010",
Expand Down
45 changes: 39 additions & 6 deletions rero_ils/modules/documents/templates/rero_ils/_documents_get.html
Original file line number Diff line number Diff line change
Expand Up @@ -102,19 +102,52 @@
{% endif %}
</div>

<div class="col-sm-2 availability d-none">
<div class="col-sm-3 availability d-none">
{% if not record.harvested %}
{% if number_items > 0 %}
<i class="fa fa-circle text-{{ 'success' if holding.available else 'danger' }}"></i>
{{ _('available') if holding.available else _('not available') }}
{% if holding.holding_is_serial %}
<i class="fa fa-circle text-warning"></i>
{{ _('see collections and items') }}
{% else %}
<i class="fa fa-circle text-{{ 'danger' }}"></i> {{ _('not available') }}
{% if number_items > 0 %}
<i class="fa fa-circle text-{{ 'success' if holding.available else 'danger' }}"></i>
{{ _('available') if holding.available else _('not available') }}
{% else %}
<i class="fa fa-circle text-{{ 'danger' }}"></i> {{ _('not available') }}
{% endif %}
{% endif %}
{% else %}
&nbsp;
{% endif %}
</div>

<!-- display data for serials holdings -->
{% if holding.holding_is_serial %}
<div class="row col-sm-12 mt-3">
<!-- a line break only for serials holdings -->
{% if holding.call_number or holding.second_call_number %}
{{ dl(_('Call number'), holding | format_record_call_number ) }}
{% endif %}
{% if holding.enumerationAndChronology %}
{{ dl(_('Available collection'), holding.enumerationAndChronology) }}
{% endif %}
{% if holding.supplementaryContent %}
{{ dl(_('Supplementary content'), holding.supplementaryContent) }}
{% endif %}
{% if holding.index %}
{{ dl(_('Indexes'), holding.index) }}
{% endif %}
{% if holding.missing_issues %}
{{ dl(_('Missing issues'), holding.missing_issues) }}
{% endif %}
{% if holding|get_note('general_note') %}
{{ dl(_('Note'), holding|get_note('general_note')) }}
{% endif %}
</div>
{% endif %}

</div>


</div>
</div>
</div>
Expand Down Expand Up @@ -147,7 +180,7 @@
</div>
<div class="col-sm-2">
{% if item.call_number %}
{{ item | format_item_call_number }}
{{ item | format_record_call_number }}
{% endif %}
</div>
{% set locations = item|item_library_pickup_locations %}
Expand Down
8 changes: 4 additions & 4 deletions rero_ils/modules/documents/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,14 @@ def can_request(item):


@blueprint.app_template_filter()
def get_note(item, note_type):
"""Get a item note by its type.
def get_note(record, note_type):
"""Get a note by its type for a given holdings or item.
:param item: the item to check.
:param record: the record to check.
:param note_type: the type of note to find.
:return the requested note, None if no corresponding note is found.
"""
return item.get_note(note_type)
return record.get_note(note_type)


@blueprint.app_template_filter()
Expand Down
112 changes: 88 additions & 24 deletions rero_ils/modules/holdings/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
from ..minters import id_minter
from ..organisations.api import Organisation
from ..providers import Provider
from ..utils import extracted_data_from_ref, get_base_url, get_ref_for_pid, \
from ..utils import extracted_data_from_ref, get_ref_for_pid, \
get_schema_for_resource

# holing provider
Expand Down Expand Up @@ -129,6 +129,8 @@ def extended_validation(self, **kwargs):
- document type is not ebook and holding type is electronic.
- holding type is serial and the next_expected_date
is not given for a regular frequency.
- if not a serial holdings contain one of optional serials fields.
- if notes array has multiple notes with same type
"""
document_pid = extracted_data_from_ref(
self.get('document').get('$ref'))
Expand All @@ -151,6 +153,22 @@ def extended_validation(self, **kwargs):
msg = _('Holding is not attached to the correct document type.'
' document: {pid}')
return _(msg.format(pid=document_pid))
# the enumeration and chronology optional fields are only allowed for
# serial holdings
if not is_serial:
fields = [
'enumerationAndChronology', 'notes', 'index', 'missing_issues',
'supplementaryContent'
]
for field in fields:
if self.get(field):
msg = _('{field} is allowed only for serial holdings')
return _(msg.format(field=field))
# No multiple notes with same type
note_types = [note.get('type') for note in self.get('notes', [])]
if len(note_types) != len(set(note_types)):
return _('Can not have multiple notes of same type.')

return True

@property
Expand All @@ -162,6 +180,11 @@ def is_serial(self):
document.get('issuance', {}).get('main_type') == 'rdami:1003'
return is_serial and is_issuance

@property
def holding_is_serial(self):
"""Shortcut to check if holding is a serial holding record."""
return self.holdings_type == 'serial'

@property
def holdings_type(self):
"""Shortcut to return the type of the holding."""
Expand Down Expand Up @@ -200,6 +223,25 @@ def available(self):
items.append(Item.get_record_by_pid(item_pid))
return Holding.isAvailable(items)

@property
def notes(self):
"""Return notes related to this holding.
:return an array of all notes related to the holding. Each note should
have two keys : `type` and `content`.
"""
return self.get('notes', [])

def get_note(self, note_type):
"""Return an holdings note by its type.
:param note_type: the type of note (see ``HoldingNoteTypes``)
:return the content of the note, None if note type is not found
"""
notes = [note.get('content') for note in self.notes
if note.get('type') == note_type]
return next(iter(notes), None)

@classmethod
def isAvailable(cls, items):
"""."""
Expand Down Expand Up @@ -551,40 +593,62 @@ def get_holdings_by_document_item_type(

def create_holding(
document_pid=None, location_pid=None, item_type_pid=None,
electronic_location=None, holdings_type=None, patterns=None):
"""Create a new holding."""
electronic_location=None, holdings_type=None, patterns=None,
enumerationAndChronology=None, supplementaryContent=None, index=None,
missing_issues=None, call_number=None, second_call_number=None,
notes=[]):
"""Create a new holdings record from a given list of fields.
:param document_pid: the document pid.
:param location_pid: the location pid.
:param item_type_pid: the item type pid.
:param electronic_location: the location for online items.
:param holdings_type: the type of holdings record.
:param patterns: the patterns and chronology for the holdings.
:param enumerationAndChronology: the Enumeration and Chronology.
:param supplementaryContent: the Supplementary Content.
:param index: the index of the holdings.
:param missing_issues: the missing issues.
:param notes: the notes of the holdings record.
:param call_number: the call_number of the holdings record.
:param second_call_number: the second_call_number of the holdings record.
:return: the created holdings record.
"""
if not (document_pid and location_pid and item_type_pid):
raise MissingRequiredParameterError(
"One of the parameters 'document_pid' "
"or 'location_pid' or 'item_type_pid' is required."
)
data = {}
# add mandatory holdings fields
data['$schema'] = get_schema_for_resource('hold')
base_url = get_base_url()
url_api = '{base_url}/api/{doc_type}/{pid}'
data['location'] = {
'$ref': url_api.format(
base_url=base_url,
doc_type='locations',
pid=location_pid)
}
data['location'] = {'$ref': get_ref_for_pid('loc', location_pid)}
data['circulation_category'] = {
'$ref': url_api.format(
base_url=base_url,
doc_type='item_types',
pid=item_type_pid)
}
data['document'] = {
'$ref': url_api.format(
base_url=base_url,
doc_type='documents',
pid=document_pid)
}
if electronic_location:
data['electronic_location'] = [electronic_location]
'$ref': get_ref_for_pid('itty', item_type_pid)}
data['document'] = {'$ref': get_ref_for_pid('doc', document_pid)}

if not holdings_type:
holdings_type = 'standard'
data['holdings_type'] = holdings_type

# add optional holdings fields if given
holdings_fields = [
{'key': 'enumerationAndChronology', 'value': enumerationAndChronology},
{'key': 'supplementaryContent', 'value': supplementaryContent},
{'key': 'index', 'value': index},
{'key': 'missing_issues', 'value': missing_issues},
{'key': 'notes', 'value': notes},
{'key': 'call_number', 'value': call_number},
{'key': 'second_call_number', 'value': second_call_number}
]
for field in holdings_fields:
value = field['value']
if value:
data[field['key']] = field['value']

if electronic_location:
data['electronic_location'] = [electronic_location]

if patterns and holdings_type == 'serial':
data['patterns'] = patterns
holding = Holding.create(
Expand Down
14 changes: 14 additions & 0 deletions rero_ils/modules/holdings/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ def create_patterns(infile, verbose, debug, lazy):
except IndexError as error:
break
patterns = record.get('patterns')
enumerationAndChronology = record.get('enumerationAndChronology')
supplementaryContent = record.get('supplementaryContent')
index = record.get('index')
missing_issues = record.get('missing_issues')
notes = record.get('notes')
call_number = record.get('call_number')
second_call_number = record.get('second_call_number')
for org_pid in Organisation.get_all_pids():
circ_category_pid = get_circ_category(org_pid)
location_pid = get_random_location(org_pid)
Expand All @@ -130,6 +137,13 @@ def create_patterns(infile, verbose, debug, lazy):
location_pid=location_pid,
item_type_pid=circ_category_pid,
holdings_type='serial',
enumerationAndChronology=enumerationAndChronology,
supplementaryContent=supplementaryContent,
index=index,
missing_issues=missing_issues,
notes=notes,
call_number=call_number,
second_call_number=second_call_number,
patterns=patterns)
# create minimum 3 and max 9 received issues for this holdings
create_issues_from_holding(holdings_record)
Expand Down
Loading

0 comments on commit 1625ee4

Please sign in to comment.