Passed
Pull Request — master (#610)
by Osma
06:08
created

annif.vocab.get_vocab()   A

Complexity

Conditions 3

Size

Total Lines 9
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nop 3
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
"""Vocabulary management functionality for Annif"""
2
3
import os.path
4
import annif
5
import annif.corpus
6
import annif.util
7
from annif.datadir import DatadirMixin
8
from annif.exception import NotInitializedException
9
10
logger = annif.logger
11
12
13 View Code Duplication
class AnnifVocabulary(DatadirMixin):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
14
    """Class representing a subject vocabulary which can be used by multiple
15
    Annif projects."""
16
17
    # defaults for uninitialized instances
18
    _subjects = None
19
20
    # constants
21
    INDEX_FILENAME_DUMP = "subjects.dump.gz"
22
    INDEX_FILENAME_TTL = "subjects.ttl"
23
    INDEX_FILENAME_CSV = "subjects.csv"
24
25
    def __init__(self, vocab_id, datadir, language):
26
        DatadirMixin.__init__(self, datadir, 'vocabs', vocab_id)
27
        self.vocab_id = vocab_id
28
        self.language = language
29
        self._skos_vocab = None
30
31
    def _create_subject_index(self, subject_corpus):
32
        subjects = annif.corpus.SubjectIndex()
33
        subjects.load_subjects(subject_corpus)
34
        annif.util.atomic_save(subjects, self.datadir,
35
                               self.INDEX_FILENAME_CSV)
36
        return subjects
37
38
    def _update_subject_index(self, subject_corpus):
39
        old_subjects = self.subjects
40
        new_subjects = annif.corpus.SubjectIndex()
41
        new_subjects.load_subjects(subject_corpus)
42
        updated_subjects = annif.corpus.SubjectIndex()
43
44
        for old_subject in old_subjects:
45
            if new_subjects.contains_uri(old_subject.uri):
46
                new_subject = new_subjects[new_subjects.by_uri(
47
                    old_subject.uri)]
48
            else:  # subject removed from new corpus
49
                new_subject = annif.corpus.Subject(uri=old_subject.uri,
50
                                                   labels=None,
51
                                                   notation=None)
52
            updated_subjects.append(new_subject)
53
        for new_subject in new_subjects:
54
            if not old_subjects.contains_uri(new_subject.uri):
55
                updated_subjects.append(new_subject)
56
        annif.util.atomic_save(updated_subjects, self.datadir,
57
                               self.INDEX_FILENAME_CSV)
58
        return updated_subjects
59
60
    @property
61
    def subjects(self):
62
        if self._subjects is None:
63
            path = os.path.join(self.datadir, self.INDEX_FILENAME_CSV)
64
            if os.path.exists(path):
65
                logger.debug('loading subjects from %s', path)
66
                self._subjects = annif.corpus.SubjectIndex.load(path)
67
            else:
68
                raise NotInitializedException(
69
                    "subject file {} not found".format(path))
70
        return self._subjects
71
72
    @property
73
    def skos(self):
74
        """return the subject vocabulary from SKOS file"""
75
        if self._skos_vocab is not None:
76
            return self._skos_vocab
77
78
        # attempt to load graph from dump file
79
        dumppath = os.path.join(self.datadir, self.INDEX_FILENAME_DUMP)
80
        if os.path.exists(dumppath):
81
            logger.debug(f'loading graph dump from {dumppath}')
82
            try:
83
                self._skos_vocab = annif.corpus.SubjectFileSKOS(dumppath)
84
            except ModuleNotFoundError:
85
                # Probably dump has been saved using a different rdflib version
86
                logger.debug('could not load graph dump, using turtle file')
87
            else:
88
                return self._skos_vocab
89
90
        # graph dump file not found - parse ttl file instead
91
        path = os.path.join(self.datadir, self.INDEX_FILENAME_TTL)
92
        if os.path.exists(path):
93
            logger.debug(f'loading graph from {path}')
94
            self._skos_vocab = annif.corpus.SubjectFileSKOS(path)
95
            # store the dump file so we can use it next time
96
            self._skos_vocab.save_skos(path)
97
            return self._skos_vocab
98
99
        raise NotInitializedException(f'graph file {path} not found')
100
101
    def load_vocabulary(self, subject_corpus, force=False):
102
        """Load subjects from a subject corpus and save them into one
103
        or more subject index files as well as a SKOS/Turtle file for later
104
        use. If force=True, replace the existing subject index completely."""
105
106
        if not force and os.path.exists(
107
                os.path.join(self.datadir, self.INDEX_FILENAME_CSV)):
108
            logger.info('updating existing vocabulary')
109
            self._subjects = self._update_subject_index(subject_corpus)
110
        else:
111
            self._subjects = self._create_subject_index(subject_corpus)
112
113
        subject_corpus.save_skos(
114
            os.path.join(self.datadir, self.INDEX_FILENAME_TTL))
115
116
    def as_graph(self):
117
        """return the vocabulary as an rdflib graph"""
118
        return self.skos.graph
119