Passed
Pull Request — master (#418)
by Osma
01:45
created

annif.registry.initialize_projects()   A

Complexity

Conditions 1

Size

Total Lines 5
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nop 1
1
"""Registry that keeps track of Annif projects"""
2
3
import collections
4
import configparser
5
import os.path
6
from flask import current_app
7
import annif
8
import annif.util
9
from annif.exception import ConfigurationException
10
from annif.project import Access, AnnifProject
11
12
logger = annif.logger
13
14
15
class AnnifRegistry:
16
    """Class that keeps track of the Annif projects"""
17
18
    # Note: The individual projects are stored in a shared static variable,
19
    # keyed by the "registry ID" which is unique to the registry instance.
20
    # This is done to make it possible to serialize AnnifRegistry instances
21
    # without including the potentially huge project objects (which contain
22
    # backends with large models, vocabularies with lots of concepts etc).
23
    # Serialized AnnifRegistry instances can then be passed between
24
    # processes when using the multiprocessing module.
25
    _projects = {}
26
27
    def __init__(self, projects_file, datadir, init_projects):
28
        self._rid = id(self)
29
        self._projects[self._rid] = \
30
            self._create_projects(projects_file, datadir)
31
        if init_projects:
32
            for project in self._projects[self._rid].values():
33
                project.initialize()
34
35
    def _create_projects(self, projects_file, datadir):
36
        if not os.path.exists(projects_file):
37
            logger.warning(
38
                'Project configuration file "%s" is missing. ' +
39
                'Please provide one. You can set the path to the project ' +
40
                'configuration file using the ANNIF_PROJECTS environment ' +
41
                'variable or the command-line option "--projects".',
42
                projects_file)
43
            return {}
44
45
        config = configparser.ConfigParser()
46
        config.optionxform = annif.util.identity
47
        with open(projects_file, encoding='utf-8-sig') as projf:
48
            try:
49
                config.read_file(projf)
50
            except (configparser.DuplicateOptionError,
51
                    configparser.DuplicateSectionError) as err:
52
                raise ConfigurationException(err)
53
54
        # create AnnifProject objects from the configuration file
55
        projects = collections.OrderedDict()
56
        for project_id in config.sections():
57
            projects[project_id] = AnnifProject(project_id,
58
                                                config[project_id],
59
                                                datadir,
60
                                                self)
61
        return projects
62
63
    def get_projects(self, min_access=Access.private):
64
        """Return the available projects as a dict of project_id ->
65
        AnnifProject. The min_access parameter may be used to set the minimum
66
        access level required for the returned projects."""
67
68
        return {project_id: project
69
                for project_id, project in self._projects[self._rid].items()
70
                if project.access >= min_access}
71
72
    def get_project(self, project_id, min_access=Access.private):
73
        """return the definition of a single Project by project_id"""
74
75
        projects = self.get_projects(min_access)
76
        try:
77
            return projects[project_id]
78
        except KeyError:
79
            raise ValueError("No such project {}".format(project_id))
80
81
82
def initialize_projects(app):
83
    projects_file = app.config['PROJECTS_FILE']
84
    datadir = app.config['DATADIR']
85
    init_projects = app.config['INITIALIZE_PROJECTS']
86
    app.annif_registry = AnnifRegistry(projects_file, datadir, init_projects)
87
88
89
def get_projects(min_access=Access.private):
90
    """Return the available projects as a dict of project_id ->
91
    AnnifProject. The min_access parameter may be used to set the minimum
92
    access level required for the returned projects."""
93
    if not hasattr(current_app, 'annif_registry'):
94
        initialize_projects(current_app)
95
96
    return current_app.annif_registry.get_projects(min_access)
97
98
99
def get_project(project_id, min_access=Access.private):
100
    """return the definition of a single Project by project_id"""
101
102
    projects = get_projects(min_access)
103
    try:
104
        return projects[project_id]
105
    except KeyError:
106
        raise ValueError("No such project {}".format(project_id))
107