Passed
Push — master ( 0b428a...1a5811 )
by Osma
07:16 queued 12s
created

annif.registry.AnnifRegistry._init_vars()   A

Complexity

Conditions 2

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
"""Registry that keeps track of Annif projects"""
2
3
import collections
4
import re
5
6
from flask import current_app
7
8
import annif
9
from annif.config import parse_config
10
from annif.exception import ConfigurationException
11
from annif.project import Access, AnnifProject
12
from annif.util import parse_args
13
from annif.vocab import AnnifVocabulary
14
15
logger = annif.logger
16
17
18
class AnnifRegistry:
19
    """Class that keeps track of the Annif projects and vocabularies"""
20
21
    # Note: The individual projects and vocabularies are stored in shared
22
    # static variables, keyed by the "registry ID" which is unique to the
23
    # registry instance. This is done to make it possible to serialize
24
    # AnnifRegistry instances without including the potentially huge objects
25
    # (which contain backends with large models, vocabularies with lots of
26
    # concepts etc). Serialized AnnifRegistry instances can then be passed
27
    # between processes when using the multiprocessing module.
28
    _projects = {}
29
    _vocabs = {}
30
31
    def __init__(self, projects_config_path, datadir, init_projects):
32
        self._rid = id(self)
33
        self._projects_config_path = projects_config_path
34
        self._datadir = datadir
35
        self._init_vars()
36
        if init_projects:
37
            for project in self._projects[self._rid].values():
38
                project.initialize()
39
40
    def _init_vars(self):
41
        # initialize the static variables, if necessary
42
        if self._rid not in self._projects:
43
            self._projects[self._rid] = self._create_projects()
44
            self._vocabs[self._rid] = {}
45
46
    def _create_projects(self):
47
        # parse the configuration
48
        config = parse_config(self._projects_config_path)
49
50
        # handle the case where the config file doesn't exist
51
        if config is None:
52
            return {}
53
54
        # create AnnifProject objects from the configuration file
55
        projects = collections.OrderedDict()
56
        for project_id in config.project_ids:
57
            projects[project_id] = AnnifProject(
58
                project_id, config[project_id], self._datadir, self
59
            )
60
        return projects
61
62
    def get_projects(self, min_access=Access.private):
63
        """Return the available projects as a dict of project_id ->
64
        AnnifProject. The min_access parameter may be used to set the minimum
65
        access level required for the returned projects."""
66
67
        self._init_vars()
68
        return {
69
            project_id: project
70
            for project_id, project in self._projects[self._rid].items()
71
            if project.access >= min_access
72
        }
73
74
    def get_project(self, project_id, min_access=Access.private):
75
        """return the definition of a single Project by project_id"""
76
77
        projects = self.get_projects(min_access)
78
        try:
79
            return projects[project_id]
80
        except KeyError:
81
            raise ValueError("No such project {}".format(project_id))
82
83
    def get_vocab(self, vocab_spec, default_language):
84
        """Return an (AnnifVocabulary, language) pair corresponding to the
85
        vocab_spec. If no language information is specified, use the given
86
        default language."""
87
88
        match = re.match(r"([\w-]+)(\((.*)\))?$", vocab_spec)
89
        if match is None:
90
            raise ValueError(f"Invalid vocabulary specification: {vocab_spec}")
91
        vocab_id = match.group(1)
92
        posargs, kwargs = parse_args(match.group(3))
93
        language = posargs[0] if posargs else default_language
94
        vocab_key = (vocab_id, language)
95
96
        self._init_vars()
97
        if vocab_key not in self._vocabs[self._rid]:
98
            self._vocabs[self._rid][vocab_key] = AnnifVocabulary(
99
                vocab_id, self._datadir
100
            )
101
        return self._vocabs[self._rid][vocab_key], language
102
103
104
def initialize_projects(app):
105
    projects_config_path = app.config["PROJECTS_CONFIG_PATH"]
106
    datadir = app.config["DATADIR"]
107
    init_projects = app.config["INITIALIZE_PROJECTS"]
108
    app.annif_registry = AnnifRegistry(projects_config_path, datadir, init_projects)
109
110
111
def get_projects(min_access=Access.private):
112
    """Return the available projects as a dict of project_id ->
113
    AnnifProject. The min_access parameter may be used to set the minimum
114
    access level required for the returned projects."""
115
    if not hasattr(current_app, "annif_registry"):
116
        initialize_projects(current_app)
117
118
    return current_app.annif_registry.get_projects(min_access)
119
120
121
def get_project(project_id, min_access=Access.private):
122
    """return the definition of a single Project by project_id"""
123
124
    projects = get_projects(min_access)
125
    try:
126
        return projects[project_id]
127
    except KeyError:
128
        raise ValueError(f"No such project '{project_id}'")
129
130
131
def get_vocabs(min_access=Access.private):
132
    """Return the available vocabularies as a dict of vocab_id ->
133
    AnnifVocabulary. The min_access parameter may be used to set the minimum
134
    access level required for the returned vocabularies."""
135
136
    vocabs = {}
137
    for proj in get_projects(min_access).values():
138
        try:
139
            vocabs[proj.vocab.vocab_id] = proj.vocab
140
        except ConfigurationException:
141
            pass
142
143
    return vocabs
144
145
146
def get_vocab(vocab_id, min_access=Access.private):
147
    """return a single AnnifVocabulary by vocabulary id"""
148
149
    vocabs = get_vocabs(min_access)
150
    try:
151
        return vocabs[vocab_id]
152
    except KeyError:
153
        raise ValueError(f"No such vocabulary '{vocab_id}'")
154