Completed
Push — master ( 597e67...7ec5d6 )
by Osma
15s queued 13s
created

annif.rest.suggest()   B

Complexity

Conditions 5

Size

Total Lines 30
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 23
nop 5
dl 0
loc 30
rs 8.8613
c 0
b 0
f 0
1
"""Definitions for REST API operations. These are wired via Connexion to
2
methods defined in the Swagger specification."""
3
4
import connexion
5
import annif.registry
6
from annif.corpus import Document, DocumentList, SubjectSet
7
from annif.suggestion import SuggestionFilter
8
from annif.exception import AnnifException
9
from annif.project import Access
10
11
12
def project_not_found_error(project_id):
13
    """return a Connexion error object when a project is not found"""
14
15
    return connexion.problem(
16
        status=404,
17
        title='Project not found',
18
        detail="Project '{}' not found".format(project_id))
19
20
21
def server_error(err):
22
    """return a Connexion error object when there is a server error (project
23
    or backend problem)"""
24
25
    return connexion.problem(
26
        status=503,
27
        title='Service unavailable',
28
        detail=err.format_message())
29
30
31
def list_projects():
32
    """return a dict with projects formatted according to Swagger spec"""
33
34
    return {
35
        'projects': [
36
            proj.dump() for proj in annif.registry.get_projects(
37
                min_access=Access.public).values()]}
38
39
40
def show_project(project_id):
41
    """return a single project formatted according to Swagger spec"""
42
43
    try:
44
        project = annif.registry.get_project(
45
            project_id, min_access=Access.hidden)
46
    except ValueError:
47
        return project_not_found_error(project_id)
48
    return project.dump()
49
50
51
def _suggestion_to_dict(suggestion, subject_index, language):
52
    subject = subject_index[suggestion.subject_id]
53
    return {
54
        'uri': subject.uri,
55
        'label': subject.labels[language],
56
        'notation': subject.notation,
57
        'score': suggestion.score
58
    }
59
60
61
def suggest(project_id, text, limit, threshold, language=None):
62
    """suggest subjects for the given text and return a dict with results
63
    formatted according to Swagger spec"""
64
65
    try:
66
        project = annif.registry.get_project(
67
            project_id, min_access=Access.hidden)
68
    except ValueError:
69
        return project_not_found_error(project_id)
70
71
    try:
72
        lang = language or project.vocab_lang
73
    except AnnifException as err:
74
        return server_error(err)
75
76
    if lang not in project.vocab.languages:
77
        return connexion.problem(
78
            status=400,
79
            title='Bad Request',
80
            detail=f'language "{lang}" not supported by vocabulary')
81
82
    try:
83
        hit_filter = SuggestionFilter(project.subjects, limit, threshold)
84
        result = project.suggest(text)
85
    except AnnifException as err:
86
        return server_error(err)
87
88
    hits = hit_filter(result).as_list()
89
    return {'results': [_suggestion_to_dict(hit, project.subjects, lang)
90
                        for hit in hits]}
91
92
93
def _documents_to_corpus(documents, subject_index):
94
    corpus = [Document(text=d['text'],
95
                       subject_set=SubjectSet(
96
                           [subject_index.by_uri(subj['uri'])
97
                            for subj in d['subjects']]))
98
              for d in documents
99
              if 'text' in d and 'subjects' in d]
100
    return DocumentList(corpus)
101
102
103
def learn(project_id, documents):
104
    """learn from documents and return an empty 204 response if succesful"""
105
106
    try:
107
        project = annif.registry.get_project(
108
            project_id, min_access=Access.hidden)
109
    except ValueError:
110
        return project_not_found_error(project_id)
111
112
    try:
113
        corpus = _documents_to_corpus(documents, project.subjects)
114
        project.learn(corpus)
115
    except AnnifException as err:
116
        return server_error(err)
117
118
    return None, 204
119