Skip to content

Commit 2b8f36b

Browse files
committed
FastAPI default
1 parent 0d6bd2b commit 2b8f36b

10 files changed

+175
-114
lines changed

pyldapi/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pyldapi.profile import Profile
77
from pyldapi.helpers import setup
88

9-
__version__ = '3.12'
9+
__version__ = '4.0'
1010

1111
__all__ = [
1212
'Renderer',

pyldapi/data.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MEDIATYPE_NAMES = {
2+
"text/html": "HTML",
3+
"text/turtle": "Turtle",
4+
"application/rdf+xml": "RDF/XML",
5+
"application/ld+json": "JSON-LD",
6+
"application/json": "JSON",
7+
"application/n-triples": "N-triples",
8+
}
9+
RDF_MEDIATYPES = [
10+
"text/turtle",
11+
"application/rdf+xml",
12+
"application/ld+json",
13+
"application/n-triples",
14+
]
15+
RDF_FILE_EXTS = {
16+
"text/turtle": "ttl",
17+
"application/rdf+xml": "rdf",
18+
"application/ld+json": "jsonld",
19+
"application/n-triples": "nt",
20+
"text/n3": "n3",
21+
}

pyldapi/renderer.py

+40-51
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# -*- coding: utf-8 -*-
22
from abc import ABCMeta
33

4+
from pathlib import Path
5+
46
from fastapi import Response
57
from fastapi.responses import JSONResponse
68
from fastapi.templating import Jinja2Templates
@@ -12,43 +14,23 @@
1214
from pyldapi.exceptions import ProfilesMediatypesException
1315
import re
1416
import connegp
17+
from .data import MEDIATYPE_NAMES, RDF_MEDIATYPES
1518

16-
templates = Jinja2Templates(directory="templates")
17-
MEDIATYPE_NAMES = None
19+
templates_dir = Path(__file__).parent.parent / "pyldapi" / "templates"
20+
templates = Jinja2Templates(directory=str(templates_dir))
1821

1922

2023
class Renderer(object, metaclass=ABCMeta):
2124
"""
2225
Abstract class as a parent for classes that validate the profiles & mediatypes for an API-delivered resource (typically
2326
either registers or objects) and also creates an 'alternates profile' for them, based on all available profiles & mediatypes.
2427
"""
25-
global MEDIATYPE_NAMES
26-
27-
RDF_MEDIA_TYPES = ['text/turtle', 'application/rdf+xml', 'application/ld+json', 'text/n3', 'application/n-triples']
28-
RDF_SERIALIZER_TYPES_MAP = {
29-
"text/turtle": "turtle",
30-
"text/n3": "n3",
31-
"application/n-triples": "nt",
32-
"application/ld+json": "json-ld",
33-
"application/rdf+xml": "xml",
34-
# Some common but incorrect mimetypes
35-
"application/rdf": "xml",
36-
"application/rdf xml": "xml",
37-
"application/json": "json-ld",
38-
"application/ld json": "json-ld",
39-
"text/ttl": "turtle",
40-
"text/ntriples": "nt",
41-
"text/n-triples": "nt",
42-
"text/plain": "nt", # text/plain is the old/deprecated mimetype for n-triples
43-
}
4428

4529
def __init__(self,
4630
request,
4731
instance_uri,
4832
profiles,
4933
default_profile_token,
50-
alternates_template=None,
51-
**kwargs
5234
):
5335
"""
5436
Constructor
@@ -73,9 +55,6 @@ def __init__(self,
7355
self.request = request
7456
self.instance_uri = instance_uri
7557

76-
# self.mediatype_names = kwargs.get('MEDIATYPE_NAMES')
77-
self.local_uris = kwargs.get('LOCAL_URIS')
78-
7958
# ensure alternates token isn't hogged by user
8059
for k, v in profiles.items():
8160
if k == 'alternates':
@@ -87,7 +66,7 @@ def __init__(self,
8766
'http://www.w3.org/ns/dx/conneg/altr', # the ConnegP URI for Alt Rep Data Model
8867
'Alternate Representations',
8968
'The representation of the resource that lists all other representations (profiles and Media Types)',
90-
['text/html', 'application/json'] + self.RDF_MEDIA_TYPES,
69+
['text/html', 'application/json'] + RDF_MEDIATYPES,
9170
'text/html',
9271
languages=['en'], # default 'en' only for now
9372
)
@@ -105,9 +84,6 @@ def __init__(self,
10584

10685
self.default_profile_token = default_profile_token
10786

108-
# TODO: supply an alternates.html template
109-
self.alt_template = alternates_template
110-
11187
# get profile & mediatype for this request, flag any errors but do not except out
11288
self.profile = self._get_profile()
11389
self.mediatype = self._get_mediatype()
@@ -451,20 +427,7 @@ def _make_rdf_response(self, graph, mimetype=None, headers=None, delete_graph=Tr
451427
if headers is None:
452428
headers = self.headers
453429

454-
if mimetype is not None:
455-
response_mimetype = mimetype
456-
serial_mediatype = self.RDF_SERIALIZER_TYPES_MAP.get(mimetype)
457-
elif self.mediatype is not None:
458-
response_mimetype = self.mediatype
459-
if self.mediatype in ['application/rdf+json', 'application/json']:
460-
serial_mediatype = 'json-ld'
461-
else:
462-
serial_mediatype = self.RDF_SERIALIZER_TYPES_MAP.get(self.mediatype)
463-
else:
464-
serial_mediatype = "turtle"
465-
response_mimetype = "text/turtle"
466-
467-
response_text = graph.serialize(format=serial_mediatype, encoding='utf-8')
430+
response_text = graph.serialize(format=mimetype or "text/turtle")
468431

469432
if delete_graph:
470433
# destroy the triples in the triplestore, then delete the triplestore
@@ -475,11 +438,34 @@ def _make_rdf_response(self, graph, mimetype=None, headers=None, delete_graph=Tr
475438

476439
return Response(
477440
response_text,
478-
media_type=response_mimetype,
441+
media_type=mimetype,
479442
headers=headers
480443
)
481444

482-
def _render_alt_profile_html(self, template_context=None):
445+
def _render_alt_profile_html(
446+
self,
447+
alt_template: str = "alt.html",
448+
additional_alt_template_context=None,
449+
alt_template_context_replace=False
450+
):
451+
"""Renders the Alternates Profile in HTML
452+
453+
If an alt_template value (str, file name) is given, a template of that name will be looked for in the app's
454+
template directory, else alt.html will be used.
455+
456+
If a dictionary is given for additional_alt_template_context, this will either replace the standard template
457+
context, if alt_template_context_replace is True, or just be added to it, if alt_template_context_replace is
458+
False
459+
460+
:param alt_template: the name of the template to use
461+
:type alt_template: str (file name, within the application's templates directory)
462+
:param additional_alt_template_context: Additional or complete context for the template
463+
:type additional_alt_template_context: dict
464+
:param alt_template_context_replace: To replace (True) or add to (False) the standard template context
465+
:type alt_template_context_replace: bool
466+
:return: a rendered template (HTML)
467+
:rtype: TemplateResponse
468+
"""
483469
profiles = {}
484470
for token, profile in self.profiles.items():
485471
profiles[token] = {
@@ -495,13 +481,16 @@ def _render_alt_profile_html(self, template_context=None):
495481
'uri': self.instance_uri,
496482
'default_profile_token': self.default_profile_token,
497483
'profiles': profiles,
498-
'MEDIATYPE_NAMES': MEDIATYPE_NAMES,
484+
'mediatype_names': MEDIATYPE_NAMES,
499485
'request': self.request
500486
}
501-
if template_context is not None and isinstance(template_context, dict):
502-
_template_context.update(template_context)
487+
if additional_alt_template_context is not None and isinstance(additional_alt_template_context, dict):
488+
if alt_template_context_replace:
489+
_template_context = additional_alt_template_context
490+
else:
491+
_template_context.update(additional_alt_template_context)
503492

504-
return templates.TemplateResponse(self.alt_template or 'alt.html',
493+
return templates.TemplateResponse(alt_template,
505494
context=_template_context,
506495
headers=self.headers)
507496

@@ -531,7 +520,7 @@ def _render_alt_profile(self):
531520
return self
532521
if self.mediatype == 'text/html':
533522
return self._render_alt_profile_html()
534-
elif self.mediatype in Renderer.RDF_MEDIA_TYPES:
523+
elif self.mediatype in RDF_MEDIATYPES:
535524
return self._render_alt_profile_rdf()
536525
else: # application/json
537526
return self._render_alt_profile_json()

pyldapi/renderer_container.py

+27-35
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
# -*- coding: utf-8 -*-
2+
from pathlib import Path
3+
24
from fastapi import Response
35
from fastapi.responses import JSONResponse
46
from fastapi.templating import Jinja2Templates
@@ -7,17 +9,17 @@
79
from pyldapi.renderer import Renderer
810
from pyldapi.profile import Profile
911
from pyldapi.exceptions import ProfilesMediatypesException, CofCTtlError
12+
from .data import RDF_MEDIATYPES, MEDIATYPE_NAMES
1013

11-
templates = Jinja2Templates(directory="templates")
12-
MEDIATYPE_NAMES = None
14+
templates_dir = Path(__file__).parent.parent / "pyldapi" / "templates"
15+
templates = Jinja2Templates(directory=str(templates_dir))
1316

1417

1518
class ContainerRenderer(Renderer):
1619
"""
1720
Specific implementation of the abstract Renderer for displaying Register information
1821
"""
19-
DEFAULT_ITEMS_PER_PAGE = 20
20-
global MEDIATYPE_NAMES
22+
DEFAULT_ITEMS_PER_PAGE = 100
2123

2224
def __init__(self,
2325
request,
@@ -32,9 +34,7 @@ def __init__(self,
3234
profiles=None,
3335
default_profile_token=None,
3436
super_register=None,
35-
page_size_max=1000,
36-
register_template=None,
37-
**kwargs):
37+
page_size_max=1000):
3838
"""
3939
Constructor
4040
@@ -63,9 +63,9 @@ def __init__(self,
6363
:type default_profile_token: str
6464
:param super_register: A super-Register URI for this register. Can be within this API or external.
6565
:type super_register: str
66-
:param register_template: The Jinja2 template to use for rendering the HTML profile of the register. If None,
66+
:param members_template: The Jinja2 template to use for rendering the HTML profile of the register. If None,
6767
then it will default to try and use a template called :code:`alt.html`.
68-
:type register_template: str or None
68+
:type members_template: str or None
6969
:param per_page: Number of items to show per page if not specified in request. If None, then it will default to
7070
RegisterRenderer.DEFAULT_ITEMS_PER_PAGE.
7171
:type per_page: int or None
@@ -84,7 +84,7 @@ def __init__(self,
8484
'https://w3id.org/profile/mem',
8585
'Members Profile',
8686
'A very basic RDF data model-only profile that lists the sub-items (members) of collections (rdf:Bag)',
87-
['text/html'] + Renderer.RDF_MEDIA_TYPES,
87+
['text/html'] + RDF_MEDIATYPES,
8888
'text/html'
8989
)
9090
})
@@ -95,8 +95,7 @@ def __init__(self,
9595
request,
9696
instance_uri,
9797
profiles,
98-
default_profile_token,
99-
**kwargs
98+
default_profile_token
10099
)
101100
if self.vf_error is None:
102101
self.label = label
@@ -120,9 +119,7 @@ def __init__(self,
120119

121120
self.super_register = super_register
122121
self.page_size_max = page_size_max
123-
self.members_template = register_template
124122
self.paging_error = self._paging()
125-
self.template_extras = kwargs
126123

127124
def _paging(self):
128125
# calculate last page
@@ -218,15 +215,12 @@ def render(self):
218215
return Response(self.paging_error, status_code=400, media_type='text/plain')
219216
return response
220217

221-
def _render_mem_profile_html(self, template_context=None):
222-
# pagination = Pagination(
223-
# members_uri=self.instance_uri,
224-
# page=self.page,
225-
# per_page=self.per_page,
226-
# total=self.members_total_count,
227-
# page_parameter='page',
228-
# per_page_parameter='per_page'
229-
# )
218+
def _render_mem_profile_html(
219+
self,
220+
mem_template: str = "mem.html",
221+
additional_mem_template_context=None,
222+
mem_template_context_replace=False
223+
):
230224
_template_context = {
231225
'uri': self.instance_uri,
232226
'label': self.label,
@@ -240,19 +234,18 @@ def _render_mem_profile_html(self, template_context=None):
240234
'prev_page': self.prev_page,
241235
'next_page': self.next_page,
242236
'last_page': self.last_page,
243-
'MEDIATYPE_NAMES': MEDIATYPE_NAMES,
244-
# 'pagination': pagination,
237+
'mediatype_names': MEDIATYPE_NAMES,
245238
'request': self.request
246239
}
247-
if self.template_extras is not None:
248-
_template_context.update(self.template_extras)
249-
if template_context is not None and isinstance(template_context, dict):
250-
_template_context.update(template_context)
240+
if additional_mem_template_context is not None and isinstance(additional_mem_template_context, dict):
241+
if mem_template_context_replace:
242+
_template_context = additional_mem_template_context
243+
else:
244+
_template_context.update(additional_mem_template_context)
251245

252-
return templates.TemplateResponse(
253-
name=self.members_template or 'members.html',
254-
context=_template_context,
255-
headers=self.headers)
246+
return templates.TemplateResponse(mem_template,
247+
context=_template_context,
248+
headers=self.headers)
256249

257250
def _generate_mem_profile_rdf(self):
258251
g = Graph()
@@ -337,9 +330,8 @@ class ContainerOfContainersRenderer(ContainerRenderer):
337330
338331
This sub-class auto-fills many of the :class:`.RegisterRenderer` options.
339332
"""
340-
global MEDIATYPE_NAMES
341333

342-
def __init__(self, request, instance_uri, label, comment, profiles, cofc_file_path, default_profile_token='mem', *args, **kwargs):
334+
def __init__(self, request, instance_uri, label, comment, profiles, cofc_file_path, default_profile_token='mem'):
343335
"""
344336
Constructor
345337

pyldapi/templates/alt.html

+8-5
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,17 @@ <h4>Default Profile: <a href="{{ request.base_url }}?_profile={{ default_profile
1919
<td style="padding-right: 30px;"><a href="{{ request.base_url }}?_profile={{ token }}&_mediatype={{ vals['default_mediatype'] }}">{{ token }}</a></td>
2020
<td>{{ vals['label'] }}</td>
2121
<td>
22-
{% for f in vals['mediatypes'] %}
23-
<a href="{{ request.base_url }}?_profile={{ token }}&_mediatype={{ f }}">{{ f }}</a><br />
24-
{% endfor %}
22+
{% for f in vals['mediatypes'] -%}
23+
{% if f in mediatype_names -%}
24+
<a href="{{ request.base_url }}?_profile={{ token }}&_mediatype={{ f }}">{{ mediatype_names[f] }}</a><br />
25+
{% else -%}
26+
<a href="{{ request.base_url }}?_profile={{ token }}&_mediatype={{ f }}">{{ f }}</a><br />
27+
{% endif %}{% endfor %}
2528
</td>
2629
<td style="text-align: center;">
27-
{% for l in vals['languages'] %}
30+
{%- for l in vals['languages'] %}
2831
<a href="{{ request.base_url }}?_profile={{ token }}&_lang={{ l }}">{{ l }}</a><br />
29-
{% endfor %}
32+
{% endfor -%}
3033
</td>
3134
<td>{{ vals['comment'] }}</td>
3235
{% if vals['namespace'] is not none %}

0 commit comments

Comments
 (0)