<commit_before># file findingaids/fa/templatetags/ead.py
#
#   Copyright 2012 Emory University Library
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

"""
Custom template filters for converting EAD tags to HTML.
"""

from django import template
from django.utils.html import conditional_escape, escape
from django.utils.safestring import mark_safe

from eulxml.xmlmap.eadmap import EAD_NAMESPACE
from findingaids.fa.models import title_rdf_identifier

__all__ = ['format_ead', 'format_ead_rdfa', 'series_section_rdfa']

register = template.Library()

XLINK_NAMESPACE = 'http://www.w3.org/1999/xlink'
EXIST_NAMESPACE = 'http://exist.sourceforge.net/NS/exist'

# namespace info to pass to xpath to allow referencing ead ns
eadns = {'namespaces': {'e': EAD_NAMESPACE}}

# render attributes which can be converted to simple tags
# - key is render attribute, value is tuple of start/end tag or
#   other start/end wrapping strings
rend_attributes = {
    'bold': ('<span class="ead-bold">', '</span>'),
    'italic': ('<span class="ead-italic">', '</span>'),
    'doublequote': ('"', '"'),
}

# tag names that can be converted to simple tags
# - key is tag name (with namespace), value is a tuple of start/end tag
simple_tags = {
    '{%s}emph' % EAD_NAMESPACE: ('<em>', '</em>'),
    '{%s}title' % EAD_NAMESPACE: ('<span class="ead-title">', '</span>'),
    '{%s}match' % EXIST_NAMESPACE: ('<span class="exist-match">', '</span>'),
}


def format_extref(node,):
    'convert an extref node to an html link'
    url = node.get('{%s}href' % XLINK_NAMESPACE)
    href = ' href="%s"' % url if url is not None else ''
    rel = ''
    # special case: links in separated/related material should be relatedLink
    if node.xpath('ancestor::e:separatedmaterial or ancestor::e:relatedmaterial',
                  **eadns):
        rel = ' property="schema:relatedLink" '

    return ('<a%s%s>' % (rel, href), '</a>')


def format_date(node, default_rel):
    'display a date node with semantic information, if available'
    normal = node.get('normal', None)
    # default to dc:date property as a generic date
    date_type = node.get('type', 'dc:date')
    start, end = '', ''
    # display if we have a normalized date
    if normal is not None:
        start = '<span property="%s" content="%s">' % (date_type, normal)
        end = '</span>'
    return (start, end)


def format_title(node, default_rel):
    'display a title node as semantic information'
    title_type = node.get('type', None)
    title_source = node.get('source', None)
    title_authfileno = node.get('authfilenumber', None)
    # lower-case title source for consistency of checking (isbn/issn/oclc)
    if title_source is not None:
        title_source = title_source.strip().lower()
    # strip space from authfileno, to avoid any issues due to human error
    if title_authfileno is not None:
        title_authfileno = title_authfileno.strip()

    start, end = '', ''

    # special case we can't do anything with
    # if a title is inside the bioghist, we can assume it was created by the
    # originator, *however* there is no inverse relationship to specify
    # a title was created by a person.  We also can't assume
    # any relation to the collection.  So, skip these titles for now.
    if node.xpath('ancestor::e:bioghist', **eadns):
        return (start, end)
    # similar special case: if a title is inside a series scopecontent note
    # which is related to a series unititle person (see note on series_section_rdfa),
    # do not generate any RDFa for that title
    if node.xpath('ancestor::e:scopecontent/preceding-sibling::e:did/e:unittitle[e:corpname or e:persname]',
                  **eadns):
        return (start, end)

    # for now, ignore titles in correspondence series
    # (getting associated with the person inappropriately)
    if default_rel == 'schema:knows arch:correspondedWith':
        return start, end

    # if isbn # or issn is available, include it
    meta_tags = ''
    if title_source in ['isbn', 'issn'] and title_authfileno is not None:
        meta_tags += '<meta property="schema:%s" content="%s"/>' % \
            (title_source, title_authfileno)
    # title attribute carries genre information
    if title_type is not None:
        meta_tags += '<meta property="schema:genre" content="%s"/>' % title_type

    # generate URI/URN for item when possible
    # TODO: abstract into reusable function
    resource_id = None
    if title_authfileno is not None:
        resource_id = title_rdf_identifier(title_source, title_authfileno)

    # resource attribute for inclusion
    resource = ' resource="%s"' % resource_id if resource_id else ''

    # if title is inside the scopecontent, it needs to be wrapped as a document
    # just use the generic "mentions" relation
    if node.xpath('ancestor::e:scopecontent', **eadns):
        # mark as a generic document or periodical and include whatever meta tags are available
        itemtype = 'bibo:Periodical' if title_source == 'issn' else 'bibo:Document'
        start = '<span rel="schema:mentions" typeof="%s"%s><span property="dc:title">' \
                 % (itemtype, resource)
        end = '</span>%s</span>' % meta_tags

    # if default rel is set to mention, assume we are patching in extra titles
    # after file item unittitle context
    elif default_rel == 'schema:mentions':
        if title_source == 'issn':
            itemtype = 'bibo:Periodical'
        elif title_source in ['isbn', 'oclc']:
            itemtype = 'bibo:Document'
        else:
            # technically we should be checking if this is a printed materials series...
            itemtype = 'bibo:Manuscript'

        # if this title has a type but no authfilenumber and there is a
        # sibling title with an id, relate them
        if title_type is not None and title_authfileno is None \
                      and node.xpath('parent::e:unittitle/e:title[@authfilenumber]',
                                     **eadns):
            rel_id = None
            rel_authfileno = node.xpath('normalize-space(parent::e:unittitle/e:title/@authfilenumber)',
                                        **eadns).strip()
            rel_source = node.xpath('normalize-space(parent::e:unittitle/e:title/@source)',
                                    **eadns).lower()

            rel_id = title_rdf_identifier(rel_source, rel_authfileno)

            if rel_id is not None:
                meta_tags += '<meta property="dcterms:isPartOf" content="%s"/>' % rel_id

        start = '<span rel="schema:mentions" typeof="%s"%s><span property="dc:title">' \
                 % (itemtype, resource)
        end = '</span>%s</span>' % meta_tags
<commit_msg>fix extra title handling: relate to document as resource, not literal [#88345948]<commit_after># file findingaids/fa/templatetags/ead.py
#
#   Copyright 2012 Emory University Library
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

"""
Custom template filters for converting EAD tags to HTML.
"""

from django import template
from django.utils.html import conditional_escape, escape
from django.utils.safestring import mark_safe

from eulxml.xmlmap.eadmap import EAD_NAMESPACE
from findingaids.fa.models import title_rdf_identifier

__all__ = ['format_ead', 'format_ead_rdfa', 'series_section_rdfa']

register = template.Library()

XLINK_NAMESPACE = 'http://www.w3.org/1999/xlink'
EXIST_NAMESPACE = 'http://exist.sourceforge.net/NS/exist'

# namespace info to pass to xpath to allow referencing ead ns
eadns = {'namespaces': {'e': EAD_NAMESPACE}}

# render attributes which can be converted to simple tags
# - key is render attribute, value is tuple of start/end tag or
#   other start/end wrapping strings
rend_attributes = {
    'bold': ('<span class="ead-bold">', '</span>'),
    'italic': ('<span class="ead-italic">', '</span>'),
    'doublequote': ('"', '"'),
}

# tag names that can be converted to simple tags
# - key is tag name (with namespace), value is a tuple of start/end tag
simple_tags = {
    '{%s}emph' % EAD_NAMESPACE: ('<em>', '</em>'),
    '{%s}title' % EAD_NAMESPACE: ('<span class="ead-title">', '</span>'),
    '{%s}match' % EXIST_NAMESPACE: ('<span class="exist-match">', '</span>'),
}


def format_extref(node,):
    'convert an extref node to an html link'
    url = node.get('{%s}href' % XLINK_NAMESPACE)
    href = ' href="%s"' % url if url is not None else ''
    rel = ''
    # special case: links in separated/related material should be relatedLink
    if node.xpath('ancestor::e:separatedmaterial or ancestor::e:relatedmaterial',
                  **eadns):
        rel = ' property="schema:relatedLink" '

    return ('<a%s%s>' % (rel, href), '</a>')


def format_date(node, default_rel):
    'display a date node with semantic information, if available'
    normal = node.get('normal', None)
    # default to dc:date property as a generic date
    date_type = node.get('type', 'dc:date')
    start, end = '', ''
    # display if we have a normalized date
    if normal is not None:
        start = '<span property="%s" content="%s">' % (date_type, normal)
        end = '</span>'
    return (start, end)


def format_title(node, default_rel):
    'display a title node as semantic information'
    title_type = node.get('type', None)
    title_source = node.get('source', None)
    title_authfileno = node.get('authfilenumber', None)
    # lower-case title source for consistency of checking (isbn/issn/oclc)
    if title_source is not None:
        title_source = title_source.strip().lower()
    # strip space from authfileno, to avoid any issues due to human error
    if title_authfileno is not None:
        title_authfileno = title_authfileno.strip()

    start, end = '', ''

    # special case we can't do anything with
    # if a title is inside the bioghist, we can assume it was created by the
    # originator, *however* there is no inverse relationship to specify
    # a title was created by a person.  We also can't assume
    # any relation to the collection.  So, skip these titles for now.
    if node.xpath('ancestor::e:bioghist', **eadns):
        return (start, end)
    # similar special case: if a title is inside a series scopecontent note
    # which is related to a series unititle person (see note on series_section_rdfa),
    # do not generate any RDFa for that title
    if node.xpath('ancestor::e:scopecontent/preceding-sibling::e:did/e:unittitle[e:corpname or e:persname]',
                  **eadns):
        return (start, end)

    # for now, ignore titles in correspondence series
    # (getting associated with the person inappropriately)
    if default_rel == 'schema:knows arch:correspondedWith':
        return start, end

    # if isbn # or issn is available, include it
    meta_tags = ''
    if title_source in ['isbn', 'issn'] and title_authfileno is not None:
        meta_tags += '<meta property="schema:%s" content="%s"/>' % \
            (title_source, title_authfileno)
    # title attribute carries genre information
    if title_type is not None:
        meta_tags += '<meta property="schema:genre" content="%s"/>' % title_type

    # generate URI/URN for item when possible
    # TODO: abstract into reusable function
    resource_id = None
    if title_authfileno is not None:
        resource_id = title_rdf_identifier(title_source, title_authfileno)

    # resource attribute for inclusion
    resource = ' resource="%s"' % resource_id if resource_id else ''

    # if title is inside the scopecontent, it needs to be wrapped as a document
    # just use the generic "mentions" relation
    if node.xpath('ancestor::e:scopecontent', **eadns):
        # mark as a generic document or periodical and include whatever meta tags are available
        itemtype = 'bibo:Periodical' if title_source == 'issn' else 'bibo:Document'
        start = '<span rel="schema:mentions" typeof="%s"%s><span property="dc:title">' \
                 % (itemtype, resource)
        end = '</span>%s</span>' % meta_tags

    # if default rel is set to mention, assume we are patching in extra titles
    # after file item unittitle context
    elif default_rel == 'schema:mentions':
        if title_source == 'issn':
            itemtype = 'bibo:Periodical'
        elif title_source in ['isbn', 'oclc']:
            itemtype = 'bibo:Document'
        else:
            # technically we should be checking if this is a printed materials series...
            itemtype = 'bibo:Manuscript'

        # if this title has a type but no authfilenumber and there is a
        # sibling title with an id, relate them
        if title_type is not None and title_authfileno is None \
                      and node.xpath('parent::e:unittitle/e:title[@authfilenumber]',
                                     **eadns):
            rel_id = None
            rel_authfileno = node.xpath('normalize-space(parent::e:unittitle/e:title/@authfilenumber)',
                                        **eadns).strip()
            rel_source = node.xpath('normalize-space(parent::e:unittitle/e:title/@source)',
                                    **eadns).lower()

            rel_id = title_rdf_identifier(rel_source, rel_authfileno)

            if rel_id is not None:
                meta_tags += '<span property="dcterms:isPartOf" resource="%s"/>' % rel_id

        start = '<span rel="schema:mentions" typeof="%s"%s><span property="dc:title">' \
                 % (itemtype, resource)
        end = '</span>%s</span>' % meta_tags

{'fraction_non_alphanumeric': 0.11262657573878901, 'fraction_numerical': 0.002342081697320383, 'mean_word_length': 3.0689461883408073, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3084680', 'n_tokens_mistral': 4327, 'n_tokens_neox': 4243, 'n_words': 1447}