Thanks to visit codestin.com
Credit goes to github.com

Skip to content
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
2 changes: 1 addition & 1 deletion rero_ils/modules/contributions/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ def get_authorized_access_point(self, language):
"""Get localized authorized_access_point.

:param language: language for authorized access point.
:returns: authorized access point in given lamguage.
:returns: authorized access point in given language.
"""
return self._get_mef_localized_value(
key='authorized_access_point',
Expand Down
26 changes: 8 additions & 18 deletions rero_ils/modules/documents/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from invenio_circulation.search.api import search_by_pid
from jsonschema.exceptions import ValidationError

from .models import DocumentIdentifier, DocumentMetadata
from .models import DocumentIdentifier, DocumentMetadata, DocumentSubjectType
from .utils import edition_format_text, publication_statement_text, \
series_statement_format_text, title_format_text_head
from ..acq_order_lines.api import AcqOrderLinesSearch
Expand Down Expand Up @@ -321,24 +321,14 @@ def replace_refs(self):
if agent:
contribution['agent'] = agent
subjects = self.get('subjects', [])
resolved_subjects = []
for subject in subjects:
subject_ref = subject.get('$ref')
subject_type = subject.get('type')
if subject_ref and \
subject_type in ['bf:Person', 'bf:Organisation']:
subject, _ = Contribution.get_record_by_ref(subject_ref)
if subject:
subject.update({'type': subject_type})
resolved_subjects.append(subject)
else:
current_app.logger.error(
'NO SUBJECT CONTRIBUTION REF FOUND:'
f' {self.pid} {subject_ref}')
else:
resolved_subjects.append(subject)
if resolved_subjects:
self['subjects'] = resolved_subjects
ref = subject.get('$ref')
type = subject.get('type')
if ref and type in [DocumentSubjectType.PERSON,
DocumentSubjectType.ORGANISATION]:
data, _ = Contribution.get_record_by_ref(ref)
del subject['$ref']
subject.update(data)

return super().replace_refs()

Expand Down
26 changes: 26 additions & 0 deletions rero_ils/modules/documents/commons/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
#
# RERO ILS
# Copyright (C) 2022 RERO
# Copyright (C) 2022 UCLouvain
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Commons classes used by RERO documents."""

from .subjects import Subject, SubjectFactory

__all__ = [
Subject,
SubjectFactory
]
222 changes: 222 additions & 0 deletions rero_ils/modules/documents/commons/subjects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
# -*- coding: utf-8 -*-
#
# RERO ILS
# Copyright (C) 2022 RERO
# Copyright (C) 2022 UCLouvain
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Common classes used for documents.

Subjects class represent all possible subjects data from a document resource.
It exists two kinds of subject: local subjects and references subjects.
* A local subject is a data structure where all subject metadata are include
in the self structure.
* A referenced subject is a data structure where a `$ref` key is a link to an
URI where found some metadata.

HOW TO USE :

>> from rero_ils.modules.documents.commons import Subject, SubjectFactory
>> Subject s = SubjectFactory.createSubject(subject_data)
>> print(s.render(language='fre'))

As we don't know (we don't want to know) which kind of subject as describe into
`subject` data, we can't use the specialized corresponding class. Instead, we
can use a factory class that build for us the correct subject.
So we NEVER need to use other classes than `Subject` and `SubjectFactory`.
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass, field

from rero_ils.modules.contributions.api import Contribution
from rero_ils.modules.documents.models import DocumentSubjectType


# =============================================================================
# SUBJECT CLASSES
# =============================================================================
@dataclass
class Subject(ABC):
"""Document subject representation."""

type: str = field(init=False)
data: dict = field(default_factory=dict)

def __post_init__(self):
"""Post initialization dataclass magic function."""
if 'type' not in self.data:
raise AttributeError('"type" attribute is required')
self.type = self.data['type']

@abstractmethod
def render(self, **kwargs) -> str:
"""Render the subject as a string."""
raise NotImplementedError()


@dataclass
class ReferenceSubject(Subject, ABC):
"""Document subject related to a reference URI."""

reference: str = field(init=False)

def __post_init__(self):
"""Post initialization dataclass magic function."""
super().__post_init__()
if '$ref' not in self.data:
raise AttributeError('"$ref" attribute is required')
self.reference = self.data['$ref']

def render(self, language=None, **kwargs) -> str:
"""Render the subject as a string.

:param language: preferred language for the subject.
:return the string representation of this subject.
"""
sub, _ = Contribution.get_record_by_ref(self.reference)
return sub.get_authorized_access_point(language=language)


@dataclass
class LocalSubject(Subject, ABC):
"""Local document subject."""

part_separator: str = ' - '

def _get_subdivision_terms(self) -> list[str]:
"""Get subject subdivision terms.

:return the subdivision terms list.
"""
return self.data.get('genreForm_subdivisions', []) \
+ self.data.get('topic_subdivisions', []) \
+ self.data.get('temporal_subdivisions', []) \
+ self.data.get('place_subdivisions', [])

@abstractmethod
def get_main_label(self) -> str:
"""Get the main label of the subject."""
raise NotImplementedError()

def render(self, **kwargs) -> str:
"""Render the subject as a string.

:return the best possible label for this subject.
"""
parts = [self.get_main_label()] + self._get_subdivision_terms()
return LocalSubject.part_separator.join(parts)


@dataclass
class TermLocalSubject(LocalSubject):
"""Local document subject representing base on `term` field."""

def get_main_label(self) -> str:
"""Get the main label of the subject."""
if 'term' not in self.data:
raise AttributeError('"term" doesn\'t exist for this subject')
return self.data['term']


@dataclass
class PreferredNameLocalSubject(LocalSubject):
"""Local document subject representing base on `preferred_name` field."""

def get_main_label(self) -> str:
"""Get the main label of the subject."""
if 'preferred_name' not in self.data:
msg = '"preferred_name" doesn\'t exist for this subject'
raise AttributeError(msg)
return self.data['preferred_name']


@dataclass
class TitleLocalSubject(LocalSubject):
"""Local document subject representing base on `title` field."""

def get_main_label(self) -> str:
"""Get the main label of the subject."""
if 'title' not in self.data:
msg = '"title" doesn\'t exist for this subject'
raise AttributeError(msg)
parts = [self.data['title']]
if 'creator' in self.data:
parts.append(self.data['creator'])
return ' / '.join(parts)


# =============================================================================
# SUBJECT FACTORIES
# =============================================================================

class SubjectFactory:
"""Document subject factory."""

@staticmethod
def create_subject(data) -> Subject:
"""Factory method to create the concrete subject class.

:param data: the dictionary representing the subject.
:return the created subject.
"""
factory_class = LocalSubjectFactory
if '$ref' in data:
factory_class = ReferenceSubjectFactory
return factory_class()._build_subject(data)

@abstractmethod
def _build_subject(self, data) -> Subject:
"""Build a subject from data.

:param data: the dictionary representing the subject.
:return the built subject.
"""
raise NotImplementedError


class ReferenceSubjectFactory(SubjectFactory):
"""Document referenced subject factory."""

def _build_subject(self, data) -> ReferenceSubject:
"""Build a subject from data.

:param data: the dictionary representing the subject.
:return the built subject.
"""
return ReferenceSubject(data=data)


class LocalSubjectFactory(SubjectFactory):
"""Document local subject factory."""

mapper = {
DocumentSubjectType.ORGANISATION: PreferredNameLocalSubject,
DocumentSubjectType.PERSON: PreferredNameLocalSubject,
DocumentSubjectType.PLACE: PreferredNameLocalSubject,
DocumentSubjectType.TEMPORAL: TermLocalSubject,
DocumentSubjectType.TOPIC: TermLocalSubject,
DocumentSubjectType.WORK: TitleLocalSubject,
}

def _build_subject(self, data) -> Subject:
"""Build a subject from data.

:param data: the dictionary representing the subject.
:return the built subject.
"""
subject_type = data.get('type')
if subject_type not in self.mapper.keys():
raise AttributeError(f'{subject_type} isn\'t a valid subject type')
return self.mapper[subject_type](data=data)
Loading