cyg 0.0.dev1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cyg might be problematic. Click here for more details.

cyg-0.0.dev1/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+
2
+
3
+ Copyright (c) 2026 CultureLab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the " Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
cyg-0.0.dev1/PKG-INFO ADDED
@@ -0,0 +1,44 @@
1
+ Metadata-Version: 2.5
2
+ Name: cyg
3
+ Version: 0.0.dev1
4
+ Summary: A package to query Cygnet, a reformulation of the Open Multilingual Wordnet
5
+ Project-URL: Homepage, https://github.com/smpouli/cygnet_python_package
6
+ Project-URL: Documentation, https://github.com/smpouli/cygnet_python_package/blob/main/documentation.txt
7
+ Project-URL: Repository, https://github.com/smpouli/cygnet_python_package
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: Wordnet,language,linguistics,multilingual
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Information Technology
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Topic :: Database
19
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
20
+ Classifier: Topic :: Text Processing
21
+ Requires-Python: >=3.9
22
+ Requires-Dist: urllib3
23
+ Description-Content-Type: text/markdown
24
+
25
+ # Product Name
26
+ >Cyg is a Python library conceived to query Cygnet, a reformulation of the Open Multilingual WordNet.
27
+
28
+
29
+ Cyg makes it possible to return ilis of concepts in different languages and also to compare senses, definitions and relationships between concepts. The database can also be queried online: https://cygnet.maudslay.eu/.
30
+
31
+
32
+
33
+ ## Release History
34
+ 0.0.1b1
35
+ * Work in progress
36
+
37
+ ## Meta
38
+
39
+ Distributed under the MIT license. See ``LICENSE`` for more information.
40
+
41
+ [https://github.com/smpouli/cygnet_python_package]
42
+
43
+
44
+
cyg-0.0.dev1/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # Product Name
2
+ >Cyg is a Python library conceived to query Cygnet, a reformulation of the Open Multilingual WordNet.
3
+
4
+
5
+ Cyg makes it possible to return ilis of concepts in different languages and also to compare senses, definitions and relationships between concepts. The database can also be queried online: https://cygnet.maudslay.eu/.
6
+
7
+
8
+
9
+ ## Release History
10
+ 0.0.1b1
11
+ * Work in progress
12
+
13
+ ## Meta
14
+
15
+ Distributed under the MIT license. See ``LICENSE`` for more information.
16
+
17
+ [https://github.com/smpouli/cygnet_python_package]
18
+
19
+
20
+
@@ -0,0 +1,199 @@
1
+
2
+ First step:
3
+
4
+ import cygnet
5
+
6
+
7
+ Use cases
8
+
9
+ 1/ Count all concepts
10
+
11
+ c = cygnet.Cygnet().concepts()
12
+ print (len(c))
13
+
14
+ * Filter by POS only
15
+
16
+ c = cygnet.Cygnet().concepts(pos="noun")
17
+
18
+ Note: only four POS currently present (noun, verb, adj and adv) in the table used
19
+
20
+ * Filter by language(s)
21
+
22
+ c = cygnet.Cygnet().concepts(langs="it")
23
+
24
+ c = cygnet.Cygnet().concepts(pos="adv", langs="it")
25
+
26
+ * Filter by by form
27
+
28
+ c = cygnet.Cygnet().concepts(form="bal")
29
+
30
+ c = cygnet.Cygnet().concepts(form="bal", pos="noun", langs="fr")
31
+
32
+
33
+
34
+
35
+ 2/ Working with concepts
36
+
37
+ => find the POS and index of a concept
38
+ for a in c:
39
+ print (a.pos())
40
+
41
+ => find the index of a concept
42
+ for a in c:
43
+ print (a.index())
44
+
45
+ => find the definition of a concept
46
+ # the offset is not implemented because the data is lacking
47
+ for a in c:
48
+ f = a.definition("es")
49
+ print (f.text())
50
+
51
+
52
+ => find semantically related concepts:
53
+
54
+ c =cygnet.Cygnet().concepts(form="cheek", langs="en")
55
+ for a in c:
56
+ rel=a.holonyms()
57
+
58
+ Available relationships:
59
+ - hypernymy: a.hypernyms()
60
+ - hyponyny: a.hyponyms()
61
+ - meronymy: a.meronyms ()
62
+ - holonyms: a.holonyms()
63
+
64
+ => Retrieve senses from concepts:
65
+
66
+ c =cygnet.Cygnet().concepts(form="cheek", langs="en")
67
+ for a in c:
68
+ senses=a.senses("en")
69
+ print (a, senses)
70
+
71
+
72
+ => Retrieve lexemes from concepts:
73
+
74
+
75
+ c =cygnet.Cygnet().concepts(form="cheek", langs="en")
76
+ for a in c:
77
+ words=a.lexemes("en")
78
+ print (a, words)
79
+
80
+
81
+ 3/ Count all lexemes
82
+
83
+ c = cygnet.Cygnet().lexemes()
84
+ print (len(c))
85
+
86
+
87
+ * Filter by form
88
+
89
+ c = cygnet.Cygnet().lexemes(form="but")
90
+ print (c)
91
+
92
+ * Filter from language(s)
93
+
94
+ c = cygnet.Cygnet().lexemes(form="but", langs=["en", "fr"])
95
+
96
+ print (c)
97
+
98
+
99
+
100
+ 4/ Working with lexemes
101
+
102
+ => find the index of the lexeme
103
+
104
+ c =cygnet.Cygnet().concepts(form="cheek", langs="en")
105
+ for a in c:
106
+ words=a.lexemes()
107
+ for w in words:
108
+ print (w.index())
109
+
110
+ => find the language of the lexeme:
111
+ c =cygnet.Cygnet().concepts(form="sudden", langs="en")
112
+ print (c)
113
+ for a in c:
114
+ words=a.lexemes()
115
+ for w in words:
116
+ print (w.lang())
117
+
118
+
119
+ => find the lemma
120
+ c =cygnet.Cygnet().concepts(form="been")
121
+ for a in c:
122
+ words=a.lexemes("en")
123
+ for w in words:
124
+ print (w.lemma())
125
+
126
+
127
+ => print all forms of the lemma:
128
+ c =cygnet.Cygnet().concepts(form="been")
129
+ for a in c:
130
+ words=a.lexemes("en")
131
+ for w in words:
132
+ print (w.all_forms())
133
+
134
+
135
+ => find the concepts related to the lexeme
136
+
137
+ c =cygnet.Cygnet().concepts(form="pray")
138
+ for a in c:
139
+ words=a.lexemes("en")
140
+ for w in words:
141
+ print (w.concepts())
142
+
143
+
144
+ => find the senses related to the lexeme
145
+ c =cygnet.Cygnet().concepts(form="pray")
146
+ for a in c:
147
+ words=a.lexemes("en")
148
+ for w in words:
149
+ print (w.senses())
150
+
151
+ 5/ Count all senses
152
+
153
+ c = cygnet.Cygnet().senses()
154
+ print (len(c))
155
+
156
+ * Filter by form
157
+
158
+ c = cygnet.Cygnet().senses(form="chaos")
159
+ print (c)
160
+
161
+ * Filter from language(s)
162
+
163
+ c = cygnet.Cygnet().senses(form="chaos", langs=["en", "fr"])
164
+ print (c)
165
+
166
+
167
+ 6/ Working with senses
168
+
169
+ => find the index of the sense
170
+
171
+ c = cygnet.Cygnet().senses(form="chaos", langs=["en", "fr"])
172
+
173
+ for b in c:
174
+ print (b, b.index())
175
+
176
+
177
+
178
+ => find the concepts related to the sense
179
+ c = cygnet.Cygnet().senses(form="chaos", langs=["en"])
180
+
181
+ for b in c:
182
+ print (b, b.concept())
183
+
184
+
185
+ => find the lexeme related to the sense:
186
+
187
+ c = cygnet.Cygnet().senses(form="chaos")
188
+
189
+ for b in c:
190
+ print (b, b.lexeme(), b.lang())
191
+
192
+ => display examples of the sense and offsets
193
+
194
+ c =cygnet.Cygnet().concepts(form="sordid")
195
+ for a in c:
196
+ words=a.lexemes("en")
197
+ for w in words:
198
+ for d in w.senses():
199
+ print (d, d.examples().text(), d.examples().sense_offsets())
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [tool.hatch.build.targets.wheel]
6
+ packages = ["src/cyg"]
7
+ sources = ["src/cyg"]
8
+
9
+
10
+ [project]
11
+ name = "cyg"
12
+ version = "0.0.dev1"
13
+ description = "A package to query Cygnet, a reformulation of the Open Multilingual Wordnet"
14
+ keywords = ["Wordnet", "multilingual", "linguistics", "language"]
15
+ readme = "README.md"
16
+ requires-python = ">=3.9"
17
+ dependencies = [
18
+ "urllib3",
19
+ ]
20
+ classifiers = [
21
+ "Programming Language :: Python :: 3",
22
+ "Operating System :: OS Independent",
23
+ "Development Status :: 4 - Beta",
24
+ "Intended Audience :: Developers",
25
+ "Intended Audience :: Information Technology",
26
+ "Intended Audience :: Science/Research",
27
+ "License :: OSI Approved :: MIT License",
28
+ "Topic :: Text Processing",
29
+ "Topic :: Database",
30
+ "Topic :: Scientific/Engineering :: Information Analysis"
31
+ ]
32
+
33
+ license = "MIT"
34
+ license-files = ["LICEN[CS]E*"]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/smpouli/cygnet_python_package"
38
+ Documentation = "https://github.com/smpouli/cygnet_python_package/blob/main/documentation.txt"
39
+ Repository = "https://github.com/smpouli/cygnet_python_package"
File without changes
@@ -0,0 +1,178 @@
1
+ from __future__ import annotations
2
+ from abc import ABC, abstractmethod
3
+ from typing import List, Tuple, Literal, Optional
4
+
5
+ POS = Literal["noun", "verb", "adj", "adv", "adp", "unk", "conj", "nref"]
6
+
7
+ class Cygnet(ABC):
8
+ """Top-level access point for querying the Cygnet database."""
9
+
10
+ @abstractmethod
11
+ def concepts(self,
12
+ form: Optional[str] = None,
13
+ langs: List[str] | str | None = None,
14
+ pos : Optional[POS] = None
15
+ ) -> List[AbstractConcept]:
16
+ """Returns all concepts, optionally filtered by wordform, language(s), and/or part of speech."""
17
+ pass
18
+
19
+ @abstractmethod
20
+ def concept(self,
21
+ ili: str
22
+ ) -> Optional[AbstractConcept]:
23
+ """Returns the concept identified by the given ILI (Interlingual Index) string, or None if not found."""
24
+ pass
25
+
26
+ @abstractmethod
27
+ def senses(self,
28
+ form: Optional[str] = None,
29
+ langs: List[str] | str | None = None
30
+ ) -> List[AbstractSense]:
31
+ """Returns all senses, optionally filtered by wordform and/or language(s)."""
32
+ pass
33
+
34
+ @abstractmethod
35
+ def lexemes(self,
36
+ form: Optional[str] = None,
37
+ langs: List[str] | str | None = None
38
+ ) -> List[AbstractLexeme]:
39
+ """Returns all lexemes, optionally filtered by wordform and/or language(s)."""
40
+ pass
41
+
42
+ @abstractmethod
43
+ def langs(self) -> List[str]:
44
+ """Returns the list of language codes available in the database."""
45
+ pass
46
+
47
+ class AbstractConcept(ABC):
48
+ """A language-independent concept, corresponding to a synset in traditional Wordnet releases."""
49
+
50
+ @abstractmethod
51
+ def definition(self,
52
+ lang: str = "en"
53
+ ) -> Optional[AbstractAnnotatedString]:
54
+ """Returns the definition of this concept in the given language, or None if unavailable."""
55
+ pass
56
+
57
+ @abstractmethod
58
+ def pos(self) -> POS:
59
+ """Returns the part of speech (ontological category) of this concept."""
60
+ pass
61
+
62
+ @abstractmethod
63
+ def index(self) -> str:
64
+ """Returns the unique identifier (ILI) for this concept."""
65
+ pass
66
+
67
+ @abstractmethod
68
+ def senses(self,
69
+ lang: Optional[str] = None
70
+ ) -> List[AbstractSense]:
71
+ """Returns all senses linked to this concept, optionally filtered by language."""
72
+ pass
73
+
74
+ @abstractmethod
75
+ def lexemes(self,
76
+ lang: Optional[str] = None
77
+ ) -> List[AbstractLexeme]:
78
+ """Returns all lexemes linked to senses of this concept, optionally filtered by language."""
79
+ pass
80
+
81
+ @abstractmethod
82
+ def hypernyms(self) -> List[AbstractConcept]:
83
+ """Returns concepts that are connected to this concept by a hypernym relation."""
84
+ pass
85
+
86
+ @abstractmethod
87
+ def hyponyms(self) -> List[AbstractConcept]:
88
+ """Returns concepts that are connected to this concept by a hyponymy relation."""
89
+ pass
90
+
91
+ @abstractmethod
92
+ def meronyms(self) -> List[AbstractConcept]:
93
+ """Returns concepts that are connected to this concept by a meronymy relation."""
94
+ pass
95
+
96
+ @abstractmethod
97
+ def holonyms(self) -> List[AbstractConcept]:
98
+ """Returns concepts that are connected to this concept by a holonymy relation."""
99
+ pass
100
+
101
+ class AbstractSense(ABC):
102
+ """A pairing of a lexeme with a concept, representing one meaning of a word."""
103
+
104
+ @abstractmethod
105
+ def index(self) -> str:
106
+ """Returns the unique identifier for this sense."""
107
+ pass
108
+
109
+ @abstractmethod
110
+ def examples(self) -> List[AbstractAnnotatedString]:
111
+ """Returns usage examples illustrating this sense."""
112
+ pass
113
+
114
+ @abstractmethod
115
+ def concept(self) -> AbstractConcept:
116
+ """Returns the concept associated with this sense (the signified)."""
117
+ pass
118
+
119
+ @abstractmethod
120
+ def lexeme(self) -> AbstractLexeme:
121
+ """Returns the lexeme associated with this sense (the signifier)."""
122
+ pass
123
+
124
+ @abstractmethod
125
+ def lang(self) -> str:
126
+ """Returns the language code of this sense's lexeme."""
127
+ pass
128
+
129
+ class AbstractLexeme(ABC):
130
+ """A wordform in a specific language, with all possible inflections."""
131
+
132
+ @abstractmethod
133
+ def index(self) -> str:
134
+ """Returns the unique identifier for this lexeme."""
135
+ pass
136
+
137
+ @abstractmethod
138
+ def lang(self) -> str:
139
+ """Returns the language code of this lexeme."""
140
+ pass
141
+
142
+ @abstractmethod
143
+ def lemma(self) -> str:
144
+ """Returns the canonical form of this lexeme."""
145
+ pass
146
+
147
+ @abstractmethod
148
+ def all_forms(self) -> List[str]:
149
+ """Returns all inflected forms of this lexeme (including the lemma)."""
150
+ pass
151
+
152
+ @abstractmethod
153
+ def senses(self) -> List[AbstractSense]:
154
+ """Returns every sense that references this lexeme."""
155
+ pass
156
+
157
+ @abstractmethod
158
+ def concepts(self) -> List[AbstractConcept]:
159
+ """Returns every concept linked to this lexeme via a sense."""
160
+ pass
161
+
162
+ class AbstractAnnotatedString(ABC):
163
+ """A string of text with sense annotations."""
164
+
165
+ @abstractmethod
166
+ def text(self) -> str:
167
+ """Return the plain text content of this string."""
168
+ pass
169
+
170
+ @abstractmethod
171
+ def lang(self) -> str:
172
+ """Return the language code of the language this string is in."""
173
+ pass
174
+
175
+ @abstractmethod
176
+ def sense_offsets(self) -> List[Tuple[AbstractSense, int, int]]:
177
+ """Return a list of (sense, start, end) tuples marking annotated spans within the text."""
178
+ pass
@@ -0,0 +1,47 @@
1
+ from typing import List, Optional
2
+ import queries
3
+ from api import AbstractConcept, AbstractSense, AbstractLexeme, POS
4
+ from subclasses import*
5
+
6
+
7
+ class Cygnet:
8
+ """
9
+ main class of the module
10
+ """
11
+ def __init__(self):
12
+ pass
13
+
14
+ def concepts(self,
15
+ form: Optional[str] = None,
16
+ langs=None,
17
+ pos: Optional[POS] = None
18
+ ) -> List[AbstractConcept]:
19
+ rows = queries.fetch_concepts(form=form, langs=langs, pos=pos)
20
+ return [Concept(row) for row in rows]
21
+
22
+ def concept(self, ili: str) -> Optional[AbstractConcept]:
23
+ row = queries.fetch_concept_by_ili(ili)
24
+ if row is None:
25
+ return None
26
+ else:
27
+ return Concept(row)
28
+
29
+ def senses(self,
30
+ form: Optional[str] = None,
31
+ langs=None
32
+ ) -> List[AbstractSense]:
33
+ rows = queries.fetch_senses(form=form, langs=langs)
34
+ return [Sense(row) for row in rows]
35
+
36
+ def lexemes(self,
37
+ form: Optional[str] = None,
38
+ langs=None
39
+ ) -> List[AbstractLexeme]:
40
+ rows = queries.fetch_lexemes(form=form, langs=langs)
41
+ return [Lexeme(row) for row in rows]
42
+
43
+
44
+ def langs(self) -> List[str]:
45
+ languages = queries.print_all_languages()
46
+ return [l[0] for l in languages]
47
+
@@ -0,0 +1,4 @@
1
+ If for whatever reason, the database cannot be downloaded automatically:
2
+
3
+ 2- Download the database here:https://github.com/rowanhm/cygnet/releases/latest/download/cygnet.db.gz
4
+ 3- Unzip it and store it in this folder.
@@ -0,0 +1,43 @@
1
+
2
+ from pathlib import Path
3
+ import urllib.request
4
+ import os, gzip, shutil
5
+ import sqlite3
6
+
7
+ # database checking and downloading
8
+ path_to_installed_database=Path('src/cyg/data/cygnet.db')
9
+
10
+ if path_to_installed_database.exists():
11
+ # connexion to the database
12
+ conn = sqlite3.connect('src/cyg/data/cygnet.db')
13
+ Database = conn.cursor()
14
+ else:
15
+ download_url = "https://github.com/rowanhm/cygnet/releases/latest/download/cygnet.db.gz"
16
+
17
+ gz_file = Path("src/cyg/data/cygnet.db.gz")
18
+ output_file = Path("src/cyg/data/cygnet.db")
19
+
20
+
21
+ try:
22
+ # Download
23
+ print ("The Cygnet database needs to be downloaded. This will happen only once.")
24
+ print ("Downloading Cygnet database...")
25
+ urllib.request.urlretrieve(download_url, gz_file)
26
+ print ("Cygnet database successfully downloaded.")
27
+ # Decompress
28
+ with gzip.open(gz_file, "rb") as gz:
29
+ with open(output_file, "wb") as output:
30
+ shutil.copyfileobj(gz, output)
31
+
32
+ os.remove(gz_file)
33
+ print("Cygnet database successfully extracted and stored.")
34
+
35
+ # connexion to the database
36
+ conn = sqlite3.connect('src/cyg/data/cygnet.db')
37
+ Database = conn.cursor()
38
+ except:
39
+ print ("An error occurred, please try again or look at the help file in the data folder.")
40
+
41
+
42
+
43
+
@@ -0,0 +1,284 @@
1
+ from typing import Optional
2
+ from db import Database
3
+
4
+
5
+ #-------- queries related to cygnet.py
6
+
7
+ def fetch_concepts(form: Optional[str] = None,
8
+ langs=None,
9
+ pos: Optional[str] = None):
10
+ """
11
+ Retrieve all concepts optionally filtered by form, languages and/or POS
12
+ """
13
+ sql_concept = """SELECT DISTINCT synsets.rowid, LOWER(synsets.pos), ili FROM synsets
14
+ JOIN senses ON synsets.rowid = senses.synset_rowid
15
+ JOIN entries ON entries.rowid = senses.entry_rowid
16
+ JOIN forms ON entries.rowid = forms.entry_rowid
17
+ JOIN languages ON languages.rowid = entries.language_rowid"""
18
+ params: list = []
19
+ conditions = []
20
+ if form:
21
+ conditions.append(" form=?")
22
+ params += [form]
23
+ if langs:
24
+ lang_list = [langs] if isinstance(langs, str) else langs
25
+ placeholders = ",".join("?" * len(lang_list))
26
+ conditions.append(f" languages.code IN ({placeholders})")
27
+ params += lang_list
28
+ if pos:
29
+ conditions.append(" LOWER(synsets.pos)=?")
30
+ params.append(pos)
31
+
32
+ if len(conditions)==1:
33
+ sql_concept= sql_concept + "\n WHERE " + conditions[0] + "\n ORDER BY synsets.rowid ASC"
34
+
35
+ elif len(conditions)>1:
36
+ condition = "\n WHERE " + " AND ".join(conditions)
37
+ sql_concept +=f""" {condition}
38
+ ORDER BY synsets.rowid ASC"""
39
+
40
+ return Database.execute(sql_concept, tuple(params)).fetchall()
41
+
42
+
43
+
44
+
45
+
46
+ def fetch_concept_by_ili ( ili: str):
47
+ """
48
+ Retrieve the specific concept related to an ILI
49
+ """
50
+ return Database.execute(
51
+ """SELECT rowid, LOWER(pos), ili FROM synsets
52
+ WHERE ili=?""", (ili,)
53
+ ).fetchone()
54
+
55
+
56
+ def fetch_senses(form: Optional[str] = None, langs=None):
57
+ """
58
+ Retrieve all concepts optionnally filtered by form and/or languages
59
+ """
60
+ sql_sense= "SELECT DISTINCT senses.rowid, senses.synset_rowid, senses.entry_rowid FROM senses"
61
+ params: list = []
62
+ conditions = []
63
+ if form:
64
+ conditions.append(" form=?")
65
+ params += [form]
66
+ if langs:
67
+ lang_list = [langs] if isinstance(langs, str) else langs
68
+ placeholders = ",".join("?" * len(lang_list))
69
+ conditions.append(f" languages.code IN ({placeholders})")
70
+ params += lang_list
71
+
72
+ if len(conditions)==1:
73
+ sql_sense += """ \n JOIN entries ON entries.rowid = senses.entry_rowid
74
+ JOIN forms ON entries.rowid = forms.entry_rowid
75
+ JOIN languages ON languages.rowid = entries.language_rowid""" + "\n WHERE " + conditions[0] + """\n ORDER BY senses.sense_index, senses.rowid"""
76
+
77
+ elif len(conditions)>1:
78
+ condition = "\n WHERE " + " AND ".join(conditions)
79
+ sql_sense +=f""" \n JOIN entries ON entries.rowid = senses.entry_rowid
80
+ JOIN forms ON entries.rowid = forms.entry_rowid
81
+ JOIN languages ON languages.rowid = entries.language_rowid
82
+ {condition}
83
+ ORDER BY senses.sense_index, senses.rowid"""
84
+
85
+
86
+ return Database.execute(sql_sense, tuple(params)).fetchall()
87
+
88
+
89
+
90
+
91
+ def fetch_lexemes(form: Optional[str] = None, langs=None):
92
+ """
93
+ Retrieve all lexemes optionnally filtered by form and/or languages
94
+ """
95
+ sql_lexeme= "SELECT DISTINCT entries.rowid from entries"
96
+ params: list = []
97
+ conditions = []
98
+ if form:
99
+ conditions.append(" form=?")
100
+ params += [form]
101
+ if langs:
102
+ lang_list = [langs] if isinstance(langs, str) else langs
103
+ placeholders = ",".join("?" * len(lang_list))
104
+ conditions.append(f" languages.code IN ({placeholders})")
105
+ params += lang_list
106
+
107
+ if len(conditions)==1:
108
+ sql_lexeme += """ \n JOIN senses ON entries.rowid = senses.entry_rowid
109
+ JOIN forms ON entries.rowid = forms.entry_rowid
110
+ JOIN languages ON languages.rowid = entries.language_rowid""" + "\n WHERE " + conditions[0] + """\n ORDER BY entries.rowid ASC"""
111
+
112
+ elif len(conditions)>1:
113
+ condition = "\n WHERE " + " AND ".join(conditions)
114
+ sql_lexeme +=f""" \n JOIN forms ON entries.rowid = forms.entry_rowid
115
+ JOIN languages ON languages.rowid = entries.language_rowid
116
+ {condition}
117
+ ORDER BY entries.rowid ASC"""
118
+
119
+
120
+ return Database.execute(sql_lexeme, tuple(params)).fetchall()
121
+
122
+
123
+ def print_all_languages ():
124
+ """
125
+ Display the codes of all languages available in the database
126
+ """
127
+ return Database.execute("""SELECT code FROM languages ORDER BY code""" ).fetchall()
128
+
129
+
130
+ #-------- queries related to the class concept
131
+
132
+
133
+ def fetch_definition(rowid, lang: str):
134
+ """
135
+ Retrieve the definition of a concept in a given language
136
+ """
137
+ sql_def = """SELECT definitions.definition, languages.code FROM definitions
138
+ JOIN languages ON languages.rowid=definitions.language_rowid
139
+ WHERE definitions.synset_rowid=? AND languages.code=?"""
140
+
141
+
142
+ return Database.execute(sql_def, (rowid, lang)).fetchone()
143
+
144
+
145
+ def fetch_senses_by_concept(rowid,lang: Optional[str] = None):
146
+ """
147
+ Retrieve the senses of a concept optionally filtered by language
148
+ """
149
+ sql_senses_concept=""" SELECT DISTINCT senses.rowid, senses.synset_rowid, senses.entry_rowid FROM senses"""
150
+ params=[rowid]
151
+
152
+ if lang:
153
+ sql_senses_concept += """\n JOIN entries ON entries.rowid=senses.entry_rowid
154
+ JOIN languages on entries.language_rowid=languages.rowid
155
+ WHERE synset_rowid = ? AND languages.code=?
156
+ ORDER BY senses.sense_index, senses.rowid"""
157
+ params.append(lang)
158
+ else:
159
+ sql_senses_concept += """ \n WHERE synset_rowid = ?
160
+ ORDER BY senses.sense_index, senses.rowid"""
161
+
162
+ return Database.execute(sql_senses_concept, tuple(params)).fetchall()
163
+
164
+
165
+
166
+
167
+ def fetch_lexemes_by_concept(rowid,lang: Optional[str] = None):
168
+ """
169
+ Retrieve the lexemes related to a concept optionally filtered by language
170
+ """
171
+ sql_lexemes_concept=""" SELECT DISTINCT senses.entry_rowid FROM senses"""
172
+ params=[rowid]
173
+
174
+ if lang:
175
+ sql_lexemes_concept += """\n JOIN entries ON entries.rowid=senses.entry_rowid
176
+ JOIN languages on entries.language_rowid=languages.rowid
177
+ WHERE senses.synset_rowid = ? AND languages.code=?
178
+ ORDER BY senses.entry_rowid ASC"""
179
+ params.append(lang)
180
+ else:
181
+ sql_lexemes_concept += """ \n WHERE senses.synset_rowid = ?
182
+ ORDER BY senses.entry_rowid ASC"""
183
+
184
+ return Database.execute(sql_lexemes_concept, tuple(params)).fetchall()
185
+
186
+
187
+
188
+
189
+ def fetch_related_synsets(rowid,relation):
190
+ """
191
+ Retrieve the hyperonyms, hyponyms, meronyms or holonyms of a concept
192
+ """
193
+ sql_rel="""SELECT target_rowid, LOWER(synsets.pos), synsets.ili FROM synset_relations
194
+ JOIN synsets ON target_rowid=synsets.rowid
195
+ WHERE source_rowid=? AND type_rowid=?
196
+ ORDER BY target_rowid ASC"""
197
+
198
+ return Database.execute(sql_rel, (rowid,relation)).fetchall()
199
+
200
+
201
+
202
+
203
+ #-------- queries related to the class Sense
204
+
205
+ def fetch_examples(sense_rowid):
206
+ """
207
+ Retrieve all examples of a particular sense
208
+ """
209
+ sql_examples = """ SELECT example, languages.code, example_annotations.sense_rowid, senses.synset_rowid, senses.entry_rowid, example_annotations.start_offset, example_annotations.end_offset FROM examples
210
+ JOIN sense_examples ON examples.rowid= sense_examples.example_rowid
211
+ JOIN example_annotations ON example_annotations.rowid=examples.rowid
212
+ JOIN senses ON senses.rowid=sense_examples.sense_rowid
213
+ JOIN entries ON entries.rowid=senses.entry_rowid
214
+ JOIN languages on entries.language_rowid=languages.rowid
215
+ WHERE sense_examples.sense_rowid = ?
216
+ ORDER BY example ASC"""
217
+ return Database.execute(sql_examples, (sense_rowid, )).fetchall()
218
+
219
+
220
+
221
+ def fetch_concept_by_sense(sense_rowid):
222
+ """
223
+ Retrieve the concept related to a sense
224
+ """
225
+ sql_sense_concept=""" SELECT rowid, LOWER(pos), ili FROM synsets
226
+ WHERE rowid = ?"""
227
+ return Database.execute(sql_sense_concept, (sense_rowid, )).fetchone()
228
+
229
+
230
+ #-------- queries related to the class Lexeme
231
+
232
+
233
+ def find_language_lexeme (lex_rowid):
234
+ """
235
+ Retrieve the language of a lexeme
236
+ """
237
+ sql_lex_lang=""" SELECT code FROM languages
238
+ JOIN entries ON entries.language_rowid = languages.rowid
239
+ WHERE entries.rowid = ?"""
240
+ return Database.execute(sql_lex_lang, (lex_rowid, )).fetchone()
241
+
242
+
243
+ def find_lemma(lex_rowid):
244
+ """
245
+ Retrieve the lemma of a lexeme
246
+ """
247
+ sql_lex_lemma=""" SELECT normalized_form FROM forms
248
+ WHERE rank=0 and entry_rowid= ?"""
249
+ return Database.execute(sql_lex_lemma, (lex_rowid, )).fetchone()
250
+
251
+
252
+ def fetch_forms_by_lexeme(lex_rowid):
253
+ """
254
+ Retrieve all the forms of a lexeme
255
+ """
256
+ sql_lex_forms=""" SELECT form, entry_rowid, rank FROM forms
257
+ WHERE entry_rowid= ?
258
+ ORDER BY form ASC"""
259
+ return Database.execute(sql_lex_forms, (lex_rowid, )).fetchall()
260
+
261
+
262
+ def fetch_senses_by_lexeme(lex_rowid):
263
+ """
264
+ Retrieve all the senses of a lexeme
265
+ """
266
+ sql_lex_senses="""SELECT rowid, synset_rowid, entry_rowid FROM senses
267
+ WHERE entry_rowid= ?
268
+ ORDER BY rowid ASC"""
269
+ return Database.execute(sql_lex_senses, (lex_rowid, )).fetchall()
270
+
271
+
272
+ def fetch_concepts_by_lexeme(lex_rowid):
273
+ """
274
+ Retrieve all the concepts related to a lexeme
275
+ """
276
+ sql_lex_concepts="""SELECT synset_rowid, LOWER(synsets.pos), synsets.ili FROM senses
277
+ JOIN synsets ON synsets.rowid=senses.synset_rowid
278
+ WHERE senses.entry_rowid= ?
279
+ ORDER BY synsets.rowid ASC"""
280
+ return Database.execute(sql_lex_concepts, (lex_rowid, )).fetchall()
281
+
282
+
283
+
284
+
@@ -0,0 +1,200 @@
1
+ from typing import List, Optional,Tuple
2
+ from api import AbstractConcept, AbstractSense, AbstractLexeme, POS, AbstractAnnotatedString
3
+ import queries
4
+
5
+
6
+ class Concept(AbstractConcept):
7
+ """
8
+ Implementation of the object Concept
9
+ """
10
+ def __init__(self, data):
11
+ self.integer= data[0]
12
+ self.gram_cat = data[1]
13
+ self.identifier= data[2]
14
+
15
+
16
+ def __repr__(self):
17
+ return "Concept(id="+ str(self.integer)+')'
18
+
19
+ def index(self) -> str:
20
+ return self.identifier
21
+
22
+ def pos(self) -> POS:
23
+ return self.gram_cat
24
+
25
+ def definition(self, lang: str) -> Optional[AbstractAnnotatedString]:
26
+ row = queries.fetch_definition(self.integer, lang)
27
+ if row is None:
28
+ return None
29
+ else:
30
+ return AnnotatedString_Temp(row)
31
+
32
+ def senses(self, lang: Optional[str] = None) -> List[AbstractSense]:
33
+ rows= queries.fetch_senses_by_concept(self.integer,lang)
34
+ return [Sense(row) for row in rows]
35
+
36
+ def lexemes(self, lang: Optional[str] = None) -> List[AbstractLexeme]:
37
+ rows= queries.fetch_lexemes_by_concept(self.integer,lang)
38
+ return [Lexeme(row) for row in rows]
39
+
40
+ def hypernyms(self) -> List[AbstractConcept]:
41
+ rel_hyper= queries.fetch_related_synsets(self.integer,"5")
42
+ return [Concept(row) for row in rel_hyper]
43
+
44
+ def hyponyms(self) -> List[AbstractConcept]:
45
+ rel_hypo= queries.fetch_related_synsets(self.integer,"6")
46
+ return [Concept(row) for row in rel_hypo]
47
+
48
+ def meronyms(self) -> List[AbstractConcept]:
49
+ rel_mero=queries.fetch_related_synsets(self.integer,"7")
50
+ return [Concept(row) for row in rel_mero]
51
+
52
+ def holonyms(self) -> List[AbstractConcept]:
53
+ rel_holo=queries.fetch_related_synsets(self.integer,"8")
54
+ return [Concept(row) for row in rel_holo]
55
+
56
+
57
+
58
+
59
+
60
+ class Lexeme(AbstractLexeme):
61
+ """
62
+ Implementation of the object Lexeme
63
+ """
64
+
65
+ def __init__(self, data):
66
+ self.integer= data[0]
67
+
68
+
69
+ def __repr__(self):
70
+ return "Lexeme(id="+ str(self.integer)+")"
71
+
72
+ def index(self) -> str:
73
+ return str(self.integer)
74
+
75
+ def lang(self) -> str:
76
+ row = queries.find_language_lexeme (self.integer)
77
+ return (row[0])
78
+
79
+ def lemma(self) -> str:
80
+ row = queries.find_lemma (self.integer)
81
+ return (row[0])
82
+
83
+ def all_forms(self) -> List[str]:
84
+ rows = queries.fetch_forms_by_lexeme(self.integer)
85
+ results =[]
86
+ for row in rows:
87
+ results.append(row[0])
88
+ return results
89
+
90
+ def senses(self) -> List[AbstractSense]:
91
+ rows = queries.fetch_senses_by_lexeme(self.integer)
92
+ return [Sense(row) for row in rows]
93
+
94
+
95
+ def concepts(self) -> List[AbstractConcept]:
96
+ rows = queries.fetch_concepts_by_lexeme(self.integer)
97
+ return [Concept(row) for row in rows]
98
+
99
+
100
+
101
+
102
+
103
+ class Sense(AbstractSense):
104
+ """
105
+ Implementation of the object Sense
106
+ """
107
+ def __init__(self, data):
108
+ self.integer= data[0]
109
+ self.linked_concept = data[1]
110
+ self.linked_entry = data[2]
111
+
112
+
113
+ def __repr__(self):
114
+ return "Sense(id="+ str(self.integer)+")"
115
+
116
+ def index(self) -> str:
117
+ return str(self.integer)
118
+
119
+
120
+ def examples(self) -> List[AbstractAnnotatedString]:
121
+ row = queries.fetch_examples(self.integer)
122
+ if row is None:
123
+ return None
124
+ else:
125
+ return AnnotatedString(row)
126
+
127
+
128
+ def concept(self) -> AbstractConcept:
129
+ row = queries.fetch_concept_by_sense(self.linked_concept)
130
+ return Concept(row)
131
+
132
+
133
+ def lexeme(self) -> AbstractLexeme:
134
+ return Lexeme([self.linked_entry])
135
+
136
+
137
+ def lang(self) -> str:
138
+ row = queries.find_language_lexeme (self.linked_entry)
139
+ return (row[0])
140
+
141
+
142
+
143
+
144
+ class AnnotatedString_Temp(AbstractAnnotatedString):
145
+ """
146
+ Temporary class to deal with definitions
147
+ """
148
+ def __init__(self, data):
149
+ self._text = data[0]
150
+ self._lang = data[1]
151
+
152
+
153
+ def text(self) -> str:
154
+ return self._text
155
+
156
+ def lang(self) -> str:
157
+ return self._lang
158
+
159
+ def sense_offsets(self) -> List[Tuple[AbstractSense, int, int]]:
160
+ pass
161
+
162
+
163
+ class AnnotatedString(AbstractAnnotatedString):
164
+ """
165
+ Print examples and definitions as well as their annotations
166
+ """
167
+
168
+ def __init__(self, data):
169
+
170
+ self._text=[]
171
+ self._lang=[]
172
+ self._dic={}
173
+ for elem in data:
174
+ self._lang = elem[1]
175
+ self._text.append(elem[0])
176
+ if (elem[2], elem[3], elem[4]) not in self._dic:
177
+ self._dic[(elem[2], elem[3], elem[4])]=[[elem[-2], elem[-1]]]
178
+ else:
179
+ self._dic[(elem[2], elem[3], elem[4])].append([elem[-2], elem[-1]])
180
+
181
+
182
+
183
+ def text(self) -> str:
184
+ return self._text
185
+
186
+ def lang(self) -> str:
187
+ return self._lang
188
+
189
+ def sense_offsets(self) -> List[Tuple[AbstractSense, int, int]]:
190
+
191
+ list_offsets=[]
192
+ for sense_index in self._dic:
193
+ for list_numbers in self._dic[sense_index]:
194
+ list_offsets.append(tuple([Sense(sense_index), list_numbers[0], list_numbers[1]]))
195
+
196
+ return list_offsets
197
+
198
+
199
+
200
+