Skip to content
Snippets Groups Projects
Commit 6a6a5df4 authored by Arkhangelskiy, Timofey's avatar Arkhangelskiy, Timofey
Browse files

Add diagnostics

parent 680d244a
Branches
No related tags found
No related merge requests found
......@@ -32,6 +32,7 @@ docs/_build
*.7z
*.rar
*.gz
*.doc
*.docx
*.pdf
*.min.css.map
......
......@@ -7,6 +7,8 @@
All documentation is available [here](https://fcs-clarin.readthedocs.io/en/latest/).
CLARIN FCS specifications this endpoint implements are available [here](https://office.clarin.eu/v/CE-2017-1046-FCS-Specification-v89.pdf).
## Requirements
......
from .enums import *
import jinja2
class Diagnostic:
"""
Contains methods for issuing diagnostic messages (fatal or non-fatal)
as per FCS specifications.
Read more about SRU diagnostics here: https://www.loc.gov/standards/sru/diagnostics/ .
SRU diagnostics list is located here: http://www.loc.gov/standards/sru/diagnostics/diagnosticsList.html .
Additional FCS diagnostics are listed in the FCS specifications.
"""
fatalFCSDiagnostics = {3, 10, 11} # FCS specifications, 4.2
fatalSRUDiagnostics = {8, 10} # A subset actually used by this endpoint
def __init__(self, diagType: DiagnosticTypes, diagID: int,
details: str = '',
message: str = ''):
"""
Initialize a diagnostic with a given numerical ID.
"""
self.diagType = diagType
self.diagID = diagID
self.details = details
self.message = message
self.templateLoader = jinja2.FileSystemLoader(searchpath="./static")
self.templateEnv = jinja2.Environment(loader=self.templateLoader)
def is_fatal(self):
"""
Return True iff the diagnostic with the given ID is fatal,
i.e. the query should not be processed further.
"""
if self.diagType == DiagnosticTypes.fcs:
return self.diagID in self.fatalFCSDiagnostics
elif self.diagType == DiagnosticTypes.sru:
return self.diagID in self.fatalSRUDiagnostics
return True
def uri(self):
"""
Return URI representing this diagnostic.
"""
if self.diagType == DiagnosticTypes.fcs:
return 'http://clarin.eu/fcs/diagnostic/' + str(self.diagID)
elif self.diagType == DiagnosticTypes.sru:
return 'info:srw/diagnostic/1/' + str(self.diagID)
return ''
def __repr__(self):
"""
Return the XML version of this diagnostic.
"""
template = self.templateEnv.get_template('diagnostic.xml')
xmlText = template.render(uri=self.uri(),
details=self.details,
message=self.message)
return xmlText.strip()
if __name__ == '__main__':
# Test
d = Diagnostic(DiagnosticTypes.fcs, 3)
d.message = '123'
print(d)
from enum import Enum
# All enumerators used for query validation are defined here
class CorpPlatform(str, Enum):
tsakorpus = 'tsakorpus'
annis = 'annis'
litterae = 'litterae'
class Operation(str, Enum):
explain = 'explain'
searchRetrieve = 'searchRetrieve'
scan = 'scan'
class SRUVersion(str, Enum):
v1_2 = '1.2'
v2_0 = '2.0'
class QueryType(str, Enum):
# Query language (parameter used since SRU 2.0)
fcs = 'fcs'
cql = 'cql' # Contextual Query Language; default
class DiagnosticTypes(str, Enum):
# Diagnostic type (defines its namespace)
sru = 'sru' # Defined at http://www.loc.gov/standards/sru/diagnostics/diagnosticsList.html
fcs = 'fcs' # Defined in the FCS specifications, 4.2
if __name__ == '__main__':
pass
from .enums import *
from .diagnostics import Diagnostic
class QueryParser:
"""
This class contains commonly used methods for initial parsing of a GET
query. It does not include platform-specific methods.
"""
def __init__(self):
pass
def validate_query(self, operation, version, queryType, query,
xFcsEndpointDescription, xFcsContext,
xFcsDataviews, xFcsRewritesAllowed):
"""
Check if the query parameters contain a valid combination of values.
:param operation:
:param version:
:param queryType:
:param query:
:param xFcsEndpointDescription:
:param xFcsContext:
:param xFcsDataviews:
:param xFcsRewritesAllowed:
:return: Return a list of diagnostics describing problems with the query.
If the query is prima facie valid and can be processed further, an empty
list will be returned.
"""
diagnostics = []
# Check if additional parameters combine with the operation requested
# (FCS specifications, 4.1)
if len(xFcsEndpointDescription) > 0 and operation != Operation.explain:
diagnostics.append(Diagnostic(DiagnosticTypes.sru, 8, details='x-fcs-endpoint-description'))
if len(xFcsContext) > 0 and operation != Operation.searchRetrieve:
diagnostics.append(Diagnostic(DiagnosticTypes.sru, 8, details='x-fcs-context'))
if len(xFcsDataviews) > 0 and operation != Operation.searchRetrieve:
diagnostics.append(Diagnostic(DiagnosticTypes.sru, 8, details='x-fcs-dataviews'))
if len(xFcsRewritesAllowed) > 0 and operation != Operation.searchRetrieve:
diagnostics.append(Diagnostic(DiagnosticTypes.sru, 8, details='x-fcs-rewrites-allowed'))
return diagnostics
if __name__ == '__main__':
pass
......@@ -3,38 +3,19 @@ from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from common.query_parser import QueryParser
from common.enums import *
from common.diagnostics import Diagnostic
import json
import uvicorn
from enum import Enum
class CorpPlatform(str, Enum):
tsakorpus = 'tsakorpus'
annis = 'annis'
litterae = 'litterae'
class Operation(str, Enum):
explain = 'explain'
searchRetrieve = 'searchRetrieve'
scan = 'scan'
class SRUVersion(str, Enum):
v1_2 = '1.2'
v2_0 = '2.0'
class QueryType(str, Enum):
# Query language (parameter used since SRU 2.0)
fcs = 'fcs'
cql = 'cql' # Contextual Query Language; default
app = FastAPI()
app.mount('/static', StaticFiles(directory='static'), name='static')
templates = Jinja2Templates(directory='static')
app.qp = QueryParser()
@app.get('/')
def root():
......@@ -66,6 +47,12 @@ def endpoint(
alias='x-fcs-rewrites-allowed'
)
):
diagnostics = app.qp.validate_query(operation, version, queryType, query,
xFcsEndpointDescription, xFcsContext,
xFcsDataviews, xFcsRewritesAllowed)
if any(d.is_fatal() for d in diagnostics):
return '\n'.join(str(d) for d in diagnostics)
if platform == CorpPlatform.annis:
return {'platform': 'annis', 'operation': operation, 'version': version}
elif platform == CorpPlatform.litterae:
......
{# Per FCS specifications (section 1.5), SRU 2.0 diagnostics must use this namespace and prefixed namespace syntax #}
<diag:diagnostic xmlns:diag="http://docs.oasis-open.org/ns/search-ws/diagnostic">
<diag:uri>{{ uri }}</diag:uri>{% if details|length > 0 %}
<diag:details>{{ details }}</diag:details>{% endif %}{% if message|length > 0 %}
<diag:message>{{ message }}</diag:message>{% endif %}
</diag:diagnostic>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment