Commit bdae12c3 authored by Mickaël Desfrênes's avatar Mickaël Desfrênes
Browse files

OAI-PMH setSpec support

parent 729e38a7
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -2,5 +2,5 @@ from django.apps import AppConfig


class OaiConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'oai'
    default_auto_field = "django.db.models.BigAutoField"
    name = "oai"
+59 −9
Original line number Diff line number Diff line
@@ -14,6 +14,19 @@ from django.core.paginator import Paginator
PAGINATION_SIZE = 100


def _set_spec_to_collection(set_spec: str) -> Collection:
    try:
        last_id = int(set_spec.split(":")[-1])
        collection = Collection.objects.get(pk=last_id)
        return collection
    except (ValueError, Collection.DoesNotExist):
        raise ValueError("no such collection")


def _collection_to_set_spec(collection: Collection) -> str:
    return collection.oai_setspec()


def _validate_date(in_date: str) -> bool:
    try:
        res = bool(datetime.strptime(in_date, "%Y-%m-%d"))
@@ -74,7 +87,9 @@ def _get_record(owner: User, request: HttpRequest) -> HttpResponse:

def _identify(owner: User, request: HttpRequest) -> HttpResponse:
    earliest_record = (
        Collection.objects.filter(is_oai_record=True).order_by("created_at").first()
        Collection.objects.filter(is_oai_record=True, owner=owner)
        .order_by("created_at")
        .first()
    )
    return HttpResponse(
        render_to_string(
@@ -113,27 +128,56 @@ def _list_records(
    resumption_token = request.GET.get("resumptionToken") or request.POST.get(
        "resumptionToken"
    )

    #
    # Get page number (ie. resumption token)
    #
    if resumption_token:
        try:
            page_number = abs(int(resumption_token))
        except ValueError:
            pass
    from_date = request.GET.get("from") or request.POST.get("from")
    if not from_date:
        earliest_record = (
            Collection.objects.filter(is_oai_record=True).order_by("created_at").first()
        )
        from_date = earliest_record.created_at.strftime("%Y-%m-%d")
    if not _validate_date(from_date):

    #
    # Get from date
    #
    from_date = request.GET.get("from") or request.POST.get("until")
    if from_date and not _validate_date(from_date):
        return _oai_error(owner, request, {"badArgument": "bad from date format"})

    #
    # Get until date
    #
    until_date = request.GET.get("until") or request.POST.get("until")
    if until_date and not _validate_date(until_date):
        return _oai_error(owner, request, {"badArgument": "bad until date format"})

    #
    # Get setSpec collection
    #
    spec_collection = None
    set_spec = request.GET.get("set") or request.POST.get("set")
    if set_spec:
        spec_collection = _set_spec_to_collection(set_spec=set_spec)

    #
    # Build query
    #
    all_records = Collection.objects.filter(
        is_oai_record=True, created_at__gte=from_date
        is_oai_record=True, owner=owner, public_access=True
    ).order_by("created_at")
    if from_date:
        all_records = all_records.filter(created_at__gte=from_date)
    if until_date:
        all_records = all_records.filter(created_at__lte=until_date)
    if spec_collection:
        all_records = all_records.filter(
            parent__in=spec_collection.descendants_and_self_ids()
        )

    #
    # Paginate, render.
    #
    total_count = all_records.count()
    if total_count == 0:
        return _oai_error(owner, request, {"noRecordsMatch": "no records match"})
@@ -156,6 +200,7 @@ def _list_records(
                "total_pages": paginator.num_pages,
                "nb_of_already_delivered_identifiers": (page_number - 1)
                * PAGINATION_SIZE,
                "spec_collection": spec_collection,
            },
        ),
        content_type="text/xml",
@@ -163,12 +208,16 @@ def _list_records(


def _list_sets(owner: User, request: HttpRequest) -> HttpResponse:
    collections = Collection.objects.filter(
        owner=owner, is_oai_record=False, public_access=True
    )
    return HttpResponse(
        render_to_string(
            "oai/list_sets.xml",
            {
                "owner": owner,
                "base_url": settings.JAMA_SITE,
                "collections": collections,
            },
        ),
        content_type="text/xml",
@@ -201,5 +250,6 @@ def oai(request: HttpRequest, owner_id: int) -> HttpResponse:
            return _list_records(owner, request)
        if oai_verb == "ListSets":
            return _list_sets(owner, request)
    # each user/owner has his own endpoint
    except User.DoesNotExist:
        raise Http404()
+18 −0
Original line number Diff line number Diff line
@@ -335,6 +335,12 @@ class Collection(models.Model):
        """
        return Resource.objects.raw(sql, [self.id])

    def descendants_and_self_ids(self) -> List[int]:
        ids = [self.pk]
        for descendant in self.descendants():
            ids.append(descendant.pk)
        return ids

    def descendants_count(self) -> int:
        with connection.cursor() as cursor:
            cursor.execute(
@@ -448,6 +454,18 @@ class Collection(models.Model):
        except MetadataSet.DoesNotExist:
            return []

    def oai_setspec(self) -> str:
        """
        Get setSpec value for OAI-PMH xml.
        (see https://www.openarchives.org/OAI/openarchivesprotocol.html#Set)
        """
        ids = []
        ancestors = self.ancestors()
        for ancestor in ancestors[1:]:
            ids.append(str(ancestor.pk))
        ids.append(str(self.pk))
        return ":".join(ids)


class MetadataCollectionValue(models.Model):
    metadata = models.ForeignKey(Metadata, on_delete=models.CASCADE)
+6 −8
Original line number Diff line number Diff line
@@ -5,11 +5,9 @@
         http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd">
    <responseDate>{% now "c" %}</responseDate>
    <request verb="ListIdentifiers"
             from="{{ from }}"
             {% if until %}
             until="{{ until }}"
             {% endif %}
             set="physics:hep"
             {% if from %}from="{{ from }}"{% endif %}
             {% if until %}until="{{ until }}"{% endif %}
             {% if spec_collection %}set="{{ spec_collection.oai_setspec }}"{% endif %}
             metadataPrefix="oai_dc">
        {{ base_url }}oai/{{ owner.pk }}
    </request>
@@ -19,9 +17,9 @@
                <header>
                    <identifier>{{ collection.pk }}</identifier>
                    <datestamp>{{ collection.created_at | date:"Y-m-d" }}</datestamp>
                    {% for ancestor in collection.ancestors|slice:"1:" %}
                        <setSpec>{{ ancestor.title }}</setSpec>
                    {% endfor %}
                    {% if collection.parent %}
                    <setSpec>{{ collection.parent.oai_setspec }}</setSpec>
                    {% endif %}
                </header>
            </record>
        {% endfor %}
+8 −4
Original line number Diff line number Diff line
@@ -5,11 +5,15 @@
         http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd">
    <responseDate>{% now "c" %}</responseDate>
    <request verb="ListRecords"
             {% if from %}
             from="{{ from }}"
             {% endif %}
             {% if until %}
             until="{{ until }}"
             {% endif %}
             set="physics:hep"
             {% if spec_collection %}
             set="{{ spec_collection.oai_setspec }}"
             {% endif %}
             metadataPrefix="oai_dc">
        {{ base_url }}oai/{{ owner.pk }}
    </request>
@@ -19,9 +23,9 @@
                <header>
                    <identifier>{{ collection.pk }}</identifier>
                    <datestamp>{{ collection.created_at | date:"Y-m-d" }}</datestamp>
                    {% for ancestor in collection.ancestors|slice:"1:" %}
                        <setSpec>{{ ancestor.title }}</setSpec>
                    {% endfor %}
                    {% if collection.parent %}
                    <setSpec>{{ collection.parent.oai_setspec }}</setSpec>
                    {% endif %}
                </header>
                <metadata>
                    <oai_dc:dc
Loading