Loglan-Core 0.2.5__tar.gz → 0.3.0__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.
Files changed (39) hide show
  1. {Loglan-Core-0.2.5 → loglan_core-0.3.0/Loglan_Core.egg-info}/PKG-INFO +8 -5
  2. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/Loglan_Core.egg-info/SOURCES.txt +1 -1
  3. {Loglan-Core-0.2.5/Loglan_Core.egg-info → loglan_core-0.3.0}/PKG-INFO +8 -5
  4. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/README.md +5 -2
  5. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/__init__.py +3 -1
  6. loglan_core-0.3.0/loglan_core/addons/base_selector.py +202 -0
  7. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/addons/definition_selector.py +16 -12
  8. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/addons/export_word_converter.py +9 -2
  9. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/addons/exporter.py +13 -10
  10. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/addons/key_selector.py +37 -5
  11. loglan_core-0.3.0/loglan_core/addons/utils.py +61 -0
  12. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/addons/word_linker.py +6 -25
  13. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/addons/word_selector.py +127 -38
  14. loglan_core-0.3.0/loglan_core/addons/word_sourcer.py +264 -0
  15. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/author.py +3 -12
  16. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/base.py +19 -6
  17. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/definition.py +7 -50
  18. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/event.py +5 -58
  19. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/key.py +4 -52
  20. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/setting.py +1 -0
  21. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/syllable.py +7 -6
  22. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/table_names.py +0 -3
  23. loglan_core-0.3.0/loglan_core/type.py +66 -0
  24. loglan_core-0.3.0/loglan_core/word.py +155 -0
  25. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/word_spell.py +1 -0
  26. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/setup.py +2 -3
  27. Loglan-Core-0.2.5/loglan_core/addons/base_selector.py +0 -79
  28. Loglan-Core-0.2.5/loglan_core/addons/word_sourcer.py +0 -174
  29. Loglan-Core-0.2.5/loglan_core/type.py +0 -109
  30. Loglan-Core-0.2.5/loglan_core/word.py +0 -340
  31. Loglan-Core-0.2.5/loglan_core/word_source.py +0 -71
  32. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/LICENSE +0 -0
  33. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/Loglan_Core.egg-info/dependency_links.txt +0 -0
  34. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/Loglan_Core.egg-info/requires.txt +0 -0
  35. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/Loglan_Core.egg-info/top_level.txt +0 -0
  36. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/addons/__init__.py +0 -0
  37. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/loglan_core/connect_tables.py +0 -0
  38. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/pyproject.toml +0 -0
  39. {Loglan-Core-0.2.5 → loglan_core-0.3.0}/setup.cfg +0 -0
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Loglan-Core
3
- Version: 0.2.5
3
+ Version: 0.3.0
4
4
  Summary: Loglan Dictionary Database Model for SQLAlchemy
5
5
  Home-page: https://github.com/torrua/loglan_core
6
- Download-URL: https://github.com/torrua/loglan_core/archive/0.2.5.tar.gz
6
+ Download-URL: https://github.com/torrua/loglan_core/archive/0.3.0.tar.gz
7
7
  Author: torrua
8
8
  Author-email: torrua@gmail.com
9
9
  License: MIT
10
10
  Keywords: Loglan,Dictionary,Database,Model,LOD
11
- Classifier: Development Status :: 4 - Beta
11
+ Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Intended Audience :: Science/Research
14
14
  Classifier: Topic :: Database
@@ -26,14 +26,17 @@ Requires-Dist: sqlalchemy>=2.0.21
26
26
  ![Codecov](https://img.shields.io/codecov/c/github/torrua/loglan_core?logo=Codecov&logoColor=%23F01F7A&label=codecov)
27
27
  ![Scrutinizer code quality (GitHub/Bitbucket)](https://img.shields.io/scrutinizer/quality/g/torrua/loglan_core/main?logo=Scrutinizer%20CI&logoColor=%238A9296&label=Scrutinizer%20CC&link=https%3A%2F%2Fscrutinizer-ci.com%2Fg%2Ftorrua%2Floglan_core%2F%3Fbranch%3Dmain)
28
28
  ![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability-percentage/torrua/loglan_core?logo=Code%20Climate)
29
- [![Pylint](https://github.com/torrua/loglan_core/actions/workflows/pylint.yml/badge.svg)](https://github.com/torrua/loglan_core/actions/workflows/pylint.yml)
29
+ ![pylint](https://img.shields.io/badge/PyLint-10.00-brightgreen?logo=python&logoColor=white)
30
30
  [![Pytest](https://github.com/torrua/loglan_core/actions/workflows/pytest.yml/badge.svg)](https://github.com/torrua/loglan_core/actions/workflows/pytest.yml)
31
- ![pylint](https://img.shields.io/badge/pylint-9.95-yellow?logo=python&logoColor=white)
31
+ ![Bandit Status](https://img.shields.io/github/actions/workflow/status/torrua/loglan_core/bandit.yml?label=bandit)
32
+ ![Black Status](https://img.shields.io/github/actions/workflow/status/torrua/loglan_core/black.yml?label=Black&labelColor=black)
33
+
32
34
  ![PyPI - Downloads](https://img.shields.io/pypi/dm/loglan_core?color=yellow)
33
35
  ![PyPI - Version](https://img.shields.io/pypi/v/loglan-core?logo=PyPi&logoColor=%23FFFFFF)
34
36
  ![GitHub License](https://img.shields.io/github/license/torrua/loglan_core)
35
37
 
36
38
 
39
+
37
40
  # SQLAlchemy Database Model for Loglan Dictionary
38
41
 
39
42
  This project represent a SQLAlchemy database model for the dictionary of [constructed language Loglan](http://www.loglan.org/).
@@ -20,7 +20,6 @@ loglan_core/syllable.py
20
20
  loglan_core/table_names.py
21
21
  loglan_core/type.py
22
22
  loglan_core/word.py
23
- loglan_core/word_source.py
24
23
  loglan_core/word_spell.py
25
24
  loglan_core/addons/__init__.py
26
25
  loglan_core/addons/base_selector.py
@@ -28,6 +27,7 @@ loglan_core/addons/definition_selector.py
28
27
  loglan_core/addons/export_word_converter.py
29
28
  loglan_core/addons/exporter.py
30
29
  loglan_core/addons/key_selector.py
30
+ loglan_core/addons/utils.py
31
31
  loglan_core/addons/word_linker.py
32
32
  loglan_core/addons/word_selector.py
33
33
  loglan_core/addons/word_sourcer.py
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Loglan-Core
3
- Version: 0.2.5
3
+ Version: 0.3.0
4
4
  Summary: Loglan Dictionary Database Model for SQLAlchemy
5
5
  Home-page: https://github.com/torrua/loglan_core
6
- Download-URL: https://github.com/torrua/loglan_core/archive/0.2.5.tar.gz
6
+ Download-URL: https://github.com/torrua/loglan_core/archive/0.3.0.tar.gz
7
7
  Author: torrua
8
8
  Author-email: torrua@gmail.com
9
9
  License: MIT
10
10
  Keywords: Loglan,Dictionary,Database,Model,LOD
11
- Classifier: Development Status :: 4 - Beta
11
+ Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Intended Audience :: Science/Research
14
14
  Classifier: Topic :: Database
@@ -26,14 +26,17 @@ Requires-Dist: sqlalchemy>=2.0.21
26
26
  ![Codecov](https://img.shields.io/codecov/c/github/torrua/loglan_core?logo=Codecov&logoColor=%23F01F7A&label=codecov)
27
27
  ![Scrutinizer code quality (GitHub/Bitbucket)](https://img.shields.io/scrutinizer/quality/g/torrua/loglan_core/main?logo=Scrutinizer%20CI&logoColor=%238A9296&label=Scrutinizer%20CC&link=https%3A%2F%2Fscrutinizer-ci.com%2Fg%2Ftorrua%2Floglan_core%2F%3Fbranch%3Dmain)
28
28
  ![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability-percentage/torrua/loglan_core?logo=Code%20Climate)
29
- [![Pylint](https://github.com/torrua/loglan_core/actions/workflows/pylint.yml/badge.svg)](https://github.com/torrua/loglan_core/actions/workflows/pylint.yml)
29
+ ![pylint](https://img.shields.io/badge/PyLint-10.00-brightgreen?logo=python&logoColor=white)
30
30
  [![Pytest](https://github.com/torrua/loglan_core/actions/workflows/pytest.yml/badge.svg)](https://github.com/torrua/loglan_core/actions/workflows/pytest.yml)
31
- ![pylint](https://img.shields.io/badge/pylint-9.95-yellow?logo=python&logoColor=white)
31
+ ![Bandit Status](https://img.shields.io/github/actions/workflow/status/torrua/loglan_core/bandit.yml?label=bandit)
32
+ ![Black Status](https://img.shields.io/github/actions/workflow/status/torrua/loglan_core/black.yml?label=Black&labelColor=black)
33
+
32
34
  ![PyPI - Downloads](https://img.shields.io/pypi/dm/loglan_core?color=yellow)
33
35
  ![PyPI - Version](https://img.shields.io/pypi/v/loglan-core?logo=PyPi&logoColor=%23FFFFFF)
34
36
  ![GitHub License](https://img.shields.io/github/license/torrua/loglan_core)
35
37
 
36
38
 
39
+
37
40
  # SQLAlchemy Database Model for Loglan Dictionary
38
41
 
39
42
  This project represent a SQLAlchemy database model for the dictionary of [constructed language Loglan](http://www.loglan.org/).
@@ -3,14 +3,17 @@
3
3
  ![Codecov](https://img.shields.io/codecov/c/github/torrua/loglan_core?logo=Codecov&logoColor=%23F01F7A&label=codecov)
4
4
  ![Scrutinizer code quality (GitHub/Bitbucket)](https://img.shields.io/scrutinizer/quality/g/torrua/loglan_core/main?logo=Scrutinizer%20CI&logoColor=%238A9296&label=Scrutinizer%20CC&link=https%3A%2F%2Fscrutinizer-ci.com%2Fg%2Ftorrua%2Floglan_core%2F%3Fbranch%3Dmain)
5
5
  ![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability-percentage/torrua/loglan_core?logo=Code%20Climate)
6
- [![Pylint](https://github.com/torrua/loglan_core/actions/workflows/pylint.yml/badge.svg)](https://github.com/torrua/loglan_core/actions/workflows/pylint.yml)
6
+ ![pylint](https://img.shields.io/badge/PyLint-10.00-brightgreen?logo=python&logoColor=white)
7
7
  [![Pytest](https://github.com/torrua/loglan_core/actions/workflows/pytest.yml/badge.svg)](https://github.com/torrua/loglan_core/actions/workflows/pytest.yml)
8
- ![pylint](https://img.shields.io/badge/pylint-9.95-yellow?logo=python&logoColor=white)
8
+ ![Bandit Status](https://img.shields.io/github/actions/workflow/status/torrua/loglan_core/bandit.yml?label=bandit)
9
+ ![Black Status](https://img.shields.io/github/actions/workflow/status/torrua/loglan_core/black.yml?label=Black&labelColor=black)
10
+
9
11
  ![PyPI - Downloads](https://img.shields.io/pypi/dm/loglan_core?color=yellow)
10
12
  ![PyPI - Version](https://img.shields.io/pypi/v/loglan-core?logo=PyPi&logoColor=%23FFFFFF)
11
13
  ![GitHub License](https://img.shields.io/github/license/torrua/loglan_core)
12
14
 
13
15
 
16
+
14
17
  # SQLAlchemy Database Model for Loglan Dictionary
15
18
 
16
19
  This project represent a SQLAlchemy database model for the dictionary of [constructed language Loglan](http://www.loglan.org/).
@@ -2,6 +2,8 @@
2
2
  Main point for export DB models
3
3
  """
4
4
 
5
+ # fmt: off
6
+
5
7
  __author__ = "torrua"
6
8
  __copyright__ = "Copyright 2024, loglan_core project"
7
9
  __email__ = "torrua@gmail.com"
@@ -19,7 +21,7 @@ from loglan_core.setting import BaseSetting as Setting
19
21
  from loglan_core.syllable import BaseSyllable as Syllable
20
22
  from loglan_core.type import BaseType as Type
21
23
  from loglan_core.word import BaseWord as Word
22
- from loglan_core.word_source import BaseWordSource as WordSource
23
24
  from loglan_core.word_spell import BaseWordSpell as WordSpell
25
+
24
26
  from loglan_core.base import BaseModel as Base
25
27
  from loglan_core.addons.word_selector import WordSelector
@@ -0,0 +1,202 @@
1
+ """
2
+ This module provides a base selector for SQLAlchemy
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any, Type
8
+ from typing_extensions import Self
9
+
10
+ from sqlalchemy import Select, BinaryExpression
11
+ from sqlalchemy.orm import Session, InstrumentedAttribute
12
+
13
+ from loglan_core.base import BaseModel
14
+
15
+
16
+ class BaseSelector(Select): # pylint: disable=too-many-ancestors
17
+ """
18
+ A custom base selector that inherits from SQLAlchemy's Select class.
19
+ This class provides methods to execute a session and fetch results
20
+ in different ways. It also provides a way to fetch many results.
21
+
22
+ Methods:
23
+ --------
24
+ execute(session: Session) -> ResultProxy:
25
+ Executes the session and returns the result.
26
+
27
+ all(session: Session) -> List[ResultRow]:
28
+ Executes the session and returns all the results.
29
+
30
+ scalar(session: Session) -> Any:
31
+ Executes the session and returns a scalar result.
32
+
33
+ fetchmany(session: Session, size: int | None = None) -> List[ResultRow]:
34
+ Executes the session and fetches a specified number of results.
35
+
36
+ condition_by_attribute(
37
+ class_: Type[BaseModel],
38
+ attr: InstrumentedAttribute | str,
39
+ value: Any,
40
+ is_sqlite: bool = False,
41
+ case_sensitive: bool = False,
42
+ ) -> BinaryExpression:
43
+ Creates a filter to select items by a specific attribute value.
44
+ Support wildcard and case-sensitive search.
45
+ """
46
+
47
+ def execute(self, session: Session):
48
+ """
49
+ Executes the given session and returns the result.
50
+
51
+ Args:
52
+ session (Session): SQLAlchemy Session object.
53
+
54
+ Returns:
55
+ ResultProxy: The result of the executed session.
56
+ """
57
+ return session.execute(self)
58
+
59
+ def all(self, session: Session):
60
+ """
61
+ Executes the given session and returns all the results as a list.
62
+
63
+ Args:
64
+ session (Session): SQLAlchemy Session object.
65
+
66
+ Returns:
67
+ List[ResultRow]: All the results of the executed session.
68
+ """
69
+ return self.execute(session).scalars().all()
70
+
71
+ def scalar(self, session: Session):
72
+ """
73
+ Executes the given session and returns a scalar result.
74
+
75
+ Args:
76
+ session (Session): SQLAlchemy Session object.
77
+
78
+ Returns:
79
+ Any: The scalar result of the executed session.
80
+ """
81
+ return self.execute(session).scalar()
82
+
83
+ def fetchmany(self, session: Session, size: int | None = None):
84
+ """
85
+ Executes the given session and fetches a specified number of results.
86
+
87
+ Args:
88
+ session (Session): SQLAlchemy Session object.
89
+ size (int, optional): Number of results to fetch. If None, fetches all results.
90
+
91
+ Returns:
92
+ List[ResultRow]: The fetched results.
93
+ """
94
+ return self.execute(session).scalars().fetchmany(size)
95
+
96
+ @classmethod
97
+ def condition_by_attribute(
98
+ cls,
99
+ class_: Type[BaseModel],
100
+ attr: InstrumentedAttribute | str,
101
+ value: Any,
102
+ is_sqlite: bool = False,
103
+ case_sensitive: bool = False,
104
+ ) -> BinaryExpression:
105
+ """
106
+ Applies a filter to select words by a specific attribute value.
107
+
108
+ Args:
109
+ class_ (BaseModel): The class to select from.
110
+ attr (str): The attribute to filter by.
111
+ value (Any): The value of the attribute to filter by.
112
+ is_sqlite (bool): If SQLite is being used. Defaults to False.
113
+ case_sensitive (bool): Whether the search should be case-sensitive.
114
+ Defaults to False.
115
+ Returns:
116
+ BaseSelector: A query with the filter applied.
117
+ """
118
+
119
+ cls._is_class_acceptable(class_)
120
+ attr = cls._get_attr(class_, attr)
121
+
122
+ value = str(value).replace("*", "%")
123
+
124
+ if case_sensitive:
125
+ return attr.op("GLOB")(value) if is_sqlite else attr.like(value)
126
+ return attr.ilike(value)
127
+
128
+ @staticmethod
129
+ def _get_attr(
130
+ class_: Type[BaseModel],
131
+ attr: InstrumentedAttribute | str,
132
+ ) -> InstrumentedAttribute:
133
+ """
134
+ Gets the attribute from the class.
135
+
136
+ Args:
137
+ class_ (BaseModel): The class to get the attribute from.
138
+ attr (str | InstrumentedAttribute): The attribute to get.
139
+
140
+ Raises:
141
+ AttributeError: If the attribute is not found in the class.
142
+
143
+ Returns:
144
+ InstrumentedAttribute: The attribute from the class.
145
+ """
146
+ if isinstance(attr, str):
147
+ try:
148
+ return getattr(class_, attr)
149
+ except AttributeError as exc:
150
+ raise AttributeError(
151
+ f"Provided attribute={attr} is not an attribute of {class_}"
152
+ ) from exc
153
+ return attr
154
+
155
+ @staticmethod
156
+ def _is_class_acceptable(class_: Type[BaseModel]):
157
+ """
158
+ Checks if the class is an instance of BaseModel or its child.
159
+
160
+ Args:
161
+ class_ (Type[BaseModel]): The class to check.
162
+
163
+ Raises:
164
+ ValueError: If the class is not an instance of BaseModel or its child.
165
+ """
166
+ if not issubclass(class_, BaseModel):
167
+ raise ValueError(
168
+ f"Provided class_={class_} is not a {BaseModel} or its child"
169
+ )
170
+
171
+ def by_attrs(
172
+ self,
173
+ class_: Type[BaseModel],
174
+ is_sqlite: bool = False,
175
+ case_sensitive: bool = False,
176
+ **kwargs,
177
+ ) -> Self:
178
+ """
179
+ Selects all words by a set of attributes.
180
+
181
+ Args:
182
+ class_ (Type[BaseModel]): The class to select from.
183
+ is_sqlite (bool): If SQLite is being used. Defaults to False.
184
+ case_sensitive (bool): Whether the search should be case-sensitive.
185
+ Defaults to False.
186
+ **kwargs: A set of attributes to filter by.
187
+
188
+ Returns:
189
+ BaseSelector: A query with the filter applied.
190
+ """
191
+ return self.where(
192
+ *{
193
+ self.condition_by_attribute(
194
+ class_,
195
+ k,
196
+ v,
197
+ is_sqlite=is_sqlite,
198
+ case_sensitive=case_sensitive,
199
+ )
200
+ for k, v in kwargs.items()
201
+ }
202
+ )
@@ -11,9 +11,16 @@ Classes:
11
11
 
12
12
  from __future__ import annotations
13
13
 
14
- from sqlalchemy import select
14
+ from typing import cast
15
+
16
+ from sqlalchemy import select, true
15
17
 
16
18
  from loglan_core.addons.base_selector import BaseSelector
19
+ from loglan_core.addons.utils import (
20
+ filter_word_by_event_id,
21
+ filter_key_by_word_cs,
22
+ filter_key_by_language,
23
+ )
17
24
  from loglan_core.connect_tables import t_connect_keys
18
25
  from loglan_core.definition import BaseDefinition
19
26
  from loglan_core.key import BaseKey
@@ -86,10 +93,10 @@ class DefinitionSelector(BaseSelector): # pylint: disable=too-many-ancestors
86
93
  select(self.class_.id)
87
94
  .join(t_connect_keys)
88
95
  .join(BaseWord)
89
- .where(BaseWord.filter_by_event_id(event_id))
96
+ .where(filter_word_by_event_id(event_id))
90
97
  .scalar_subquery()
91
98
  )
92
- return self.where(self.class_.id.in_(subquery))
99
+ return cast(DefinitionSelector, self.where(self.class_.id.in_(subquery)))
93
100
 
94
101
  def by_key(
95
102
  self,
@@ -111,17 +118,13 @@ class DefinitionSelector(BaseSelector): # pylint: disable=too-many-ancestors
111
118
  """
112
119
 
113
120
  search_key = key.word if isinstance(key, BaseKey) else str(key)
114
- filter_key = BaseKey.filter_by_key_cs(
115
- search_key, case_sensitive, self.is_sqlite
116
- )
117
- filter_language = BaseKey.filter_by_language(
121
+ filter_key = filter_key_by_word_cs(search_key, case_sensitive, self.is_sqlite)
122
+ filter_language = filter_key_by_language(
118
123
  key.language if isinstance(key, BaseKey) else language
119
124
  )
120
125
 
121
- statement = self.join(self.class_.relationship_keys).filter(
122
- filter_key, filter_language
123
- )
124
- return statement.distinct()
126
+ statement = self.join(self.class_.keys).filter(filter_key, filter_language)
127
+ return cast(DefinitionSelector, statement.distinct())
125
128
 
126
129
  def by_language(self, language: str | None = None) -> DefinitionSelector:
127
130
  """
@@ -134,4 +137,5 @@ class DefinitionSelector(BaseSelector): # pylint: disable=too-many-ancestors
134
137
  Returns:
135
138
  DefinitionSelector: The filtered DefinitionSelector instance.
136
139
  """
137
- return self.filter(self.class_.filter_language(language))
140
+ filter_language = self.class_.language == language if language else true()
141
+ return cast(DefinitionSelector, self.filter(filter_language))
@@ -1,6 +1,7 @@
1
1
  """
2
2
  This module contains ExportWordConverter for Word model of LOD.
3
3
  """
4
+
4
5
  from loglan_core.word import BaseWord
5
6
 
6
7
 
@@ -59,7 +60,9 @@ class ExportWordConverter:
59
60
  Returns:
60
61
  str: A string with the names of the complexes separated by a vertical bar.
61
62
  """
62
- return " | ".join(cpx.name for cpx in self.word.complexes)
63
+ return " | ".join(
64
+ cpx.name for cpx in self.word.derivatives if cpx.type.group == "Cpx"
65
+ )
63
66
 
64
67
  @property
65
68
  def e_affixes(self) -> str:
@@ -69,7 +72,11 @@ class ExportWordConverter:
69
72
  Returns:
70
73
  str: A string containing all affixes of the word with hyphens removed.
71
74
  """
72
- return " ".join(afx.name.replace("-", "") for afx in self.word.affixes).strip()
75
+ return " ".join(
76
+ afx.name.replace("-", "")
77
+ for afx in self.word.derivatives
78
+ if afx.type.type_x == "Affix"
79
+ ).strip()
73
80
 
74
81
  @property
75
82
  def e_djifoa(self) -> str:
@@ -2,8 +2,8 @@
2
2
  This module contains an "Export extensions" for LOD dictionary SQL model.
3
3
  Add export() function to db object for returning its text string presentation.
4
4
  """
5
- from typing import Iterable, Callable, Type
6
5
 
6
+ from typing import Iterable, Callable, Type
7
7
 
8
8
  from loglan_core.addons.export_word_converter import ExportWordConverter
9
9
  from loglan_core.author import BaseAuthor
@@ -42,6 +42,9 @@ class Exporter:
42
42
  ValueError: If the object type is not supported for export.
43
43
  """
44
44
 
45
+ FORMAT_DATE_EVENT = "%m/%d/%Y"
46
+ FORMAT_DATE_SETTING = "%d.%m.%Y %H:%M:%S"
47
+
45
48
  @classmethod
46
49
  def export(cls, obj, separator: str = DEFAULT_SEPARATOR) -> str:
47
50
  """
@@ -102,8 +105,8 @@ class Exporter:
102
105
  """
103
106
  return obj.abbreviation, obj.full_name, obj.notes
104
107
 
105
- @staticmethod
106
- def export_event(obj: BaseEvent) -> tuple:
108
+ @classmethod
109
+ def export_event(cls, obj: BaseEvent) -> tuple:
107
110
  """
108
111
  Prepare Event data for exporting to text file
109
112
 
@@ -113,7 +116,7 @@ class Exporter:
113
116
  return (
114
117
  obj.event_id,
115
118
  obj.name,
116
- obj.date.strftime("%m/%d/%Y"), # TODO Add format as class constant
119
+ obj.date.strftime(cls.FORMAT_DATE_EVENT),
117
120
  obj.definition,
118
121
  obj.annotation,
119
122
  obj.suffix,
@@ -127,10 +130,10 @@ class Exporter:
127
130
  Returns:
128
131
  tuple: elements for export
129
132
  """
130
- return obj.name, obj.type, str(obj.allowed)
133
+ return obj.name, obj.type_, str(obj.allowed)
131
134
 
132
- @staticmethod
133
- def export_setting(obj: BaseSetting) -> tuple:
135
+ @classmethod
136
+ def export_setting(cls, obj: BaseSetting) -> tuple:
134
137
  """
135
138
  Prepare Setting data for exporting to text file
136
139
 
@@ -138,7 +141,7 @@ class Exporter:
138
141
  tuple: elements for export
139
142
  """
140
143
  return (
141
- obj.date.strftime("%d.%m.%Y %H:%M:%S"), # TODO Add format as class constant
144
+ obj.date.strftime(cls.FORMAT_DATE_SETTING),
142
145
  obj.db_version,
143
146
  obj.last_word_id,
144
147
  obj.db_release,
@@ -153,7 +156,7 @@ class Exporter:
153
156
  tuple: elements for export
154
157
  """
155
158
  return (
156
- obj.type,
159
+ obj.type_,
157
160
  obj.type_x,
158
161
  obj.group,
159
162
  str(obj.parentable),
@@ -175,7 +178,7 @@ class Exporter:
175
178
  origin = ewc.stringer(obj.origin)
176
179
  return (
177
180
  obj.id_old,
178
- obj.type.type,
181
+ obj.type.type_,
179
182
  obj.type.type_x,
180
183
  ewc.e_affixes,
181
184
  match,
@@ -23,9 +23,16 @@ This allows for flexible and powerful querying of keys in a codebase.
23
23
 
24
24
  from __future__ import annotations
25
25
 
26
+ from typing import cast
27
+
26
28
  from sqlalchemy import select
27
29
 
28
30
  from loglan_core.addons.base_selector import BaseSelector
31
+ from loglan_core.addons.utils import (
32
+ filter_word_by_event_id,
33
+ filter_key_by_word_cs,
34
+ filter_key_by_language,
35
+ )
29
36
  from loglan_core.connect_tables import t_connect_keys
30
37
  from loglan_core.definition import BaseDefinition
31
38
  from loglan_core.key import BaseKey
@@ -87,10 +94,10 @@ class KeySelector(BaseSelector): # pylint: disable=too-many-ancestors
87
94
  .join(t_connect_keys)
88
95
  .join(BaseDefinition)
89
96
  .join(BaseWord)
90
- .where(BaseWord.filter_by_event_id(event_id))
97
+ .where(filter_word_by_event_id(event_id))
91
98
  .scalar_subquery()
92
99
  )
93
- return self.where(self.class_.id.in_(subquery))
100
+ return cast(KeySelector, self.where(self.class_.id.in_(subquery)))
94
101
 
95
102
  def by_key(self, key: str, case_sensitive: bool = False) -> KeySelector:
96
103
  """
@@ -104,8 +111,9 @@ class KeySelector(BaseSelector): # pylint: disable=too-many-ancestors
104
111
  Returns:
105
112
  KeySelector: The filtered KeySelector instance.
106
113
  """
107
- return self.where(
108
- self.class_.filter_by_key_cs(key, case_sensitive, self.is_sqlite)
114
+ return cast(
115
+ KeySelector,
116
+ self.where(filter_key_by_word_cs(key, case_sensitive, self.is_sqlite)),
109
117
  )
110
118
 
111
119
  def by_language(self, language: str | None = None) -> KeySelector:
@@ -119,4 +127,28 @@ class KeySelector(BaseSelector): # pylint: disable=too-many-ancestors
119
127
  Returns:
120
128
  KeySelector: The filtered KeySelector instance.
121
129
  """
122
- return self.where(self.class_.filter_by_language(language))
130
+ return cast(KeySelector, self.where(filter_key_by_language(language)))
131
+
132
+ def by_word_id(self, word_id: int) -> KeySelector:
133
+ """
134
+ Filters the keys by the given word ID.
135
+
136
+ Args:
137
+ word_id (int): The identifier of the word to filter by.
138
+
139
+ Returns:
140
+ KeySelector: The filtered KeySelector instance.
141
+
142
+ Keep in mind that duplicated keys from related definitions
143
+ will be counted with ```.count()``` but excluded from ```.all()``` request
144
+
145
+ """
146
+ return cast(
147
+ KeySelector,
148
+ self.distinct()
149
+ .join(t_connect_keys)
150
+ .join(BaseDefinition, BaseDefinition.id == t_connect_keys.c.DID)
151
+ .join(BaseWord, BaseWord.id == BaseDefinition.word_id)
152
+ .filter(BaseWord.id == word_id)
153
+ .order_by(BaseKey.word.asc()),
154
+ )
@@ -0,0 +1,61 @@
1
+ """
2
+ This module provides utility functions for the loglan_core package.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from sqlalchemy import select, true, func
8
+ from sqlalchemy.sql.elements import BinaryExpression
9
+ from sqlalchemy.sql.elements import BooleanClauseList
10
+ from sqlalchemy.sql.expression import ColumnElement
11
+
12
+ from loglan_core.event import BaseEvent
13
+ from loglan_core.key import BaseKey
14
+ from loglan_core.word import BaseWord
15
+
16
+
17
+ def filter_word_by_event_id(event_id: int | None) -> BooleanClauseList:
18
+ """
19
+ Returns a filter condition to select words associated with a specific event.
20
+
21
+ Args:
22
+ event_id: The id of the event to filter by. Defaults to None.
23
+
24
+ Returns:
25
+ BooleanClauseList: A filter condition to select words associated with a specific event.
26
+ """
27
+ latest_id = select(func.max(BaseEvent.event_id)).scalar_subquery()
28
+ event_id_filter = event_id or latest_id
29
+ start_id_condition = BaseWord.event_start_id <= event_id_filter
30
+ end_id_condition = (
31
+ BaseWord.event_end_id > event_id_filter
32
+ ) | BaseWord.event_end_id.is_(None)
33
+ return start_id_condition & end_id_condition
34
+
35
+
36
+ def filter_key_by_word_cs(
37
+ key: str,
38
+ case_sensitive: bool = False,
39
+ is_sqlite: bool = False,
40
+ ) -> BinaryExpression:
41
+ """case sensitive name filter"""
42
+ key = str(key).replace("*", "%")
43
+ return (
44
+ (BaseKey.word.op("GLOB")(key) if is_sqlite else BaseKey.word.like(key))
45
+ if case_sensitive
46
+ else BaseKey.word.ilike(key)
47
+ )
48
+
49
+
50
+ def filter_key_by_language(language: str | None = None) -> ColumnElement[bool]:
51
+ """
52
+ Filter the language of the base key.
53
+
54
+ Args:
55
+ language (str or None): The language to filter by.
56
+ If None, no language filter will be applied.
57
+
58
+ Returns:
59
+ ColumnElement[bool]: A filter condition for the base key's language.
60
+ """
61
+ return (BaseKey.language == language) if language else true()