ecodev-core 0.0.34__py3-none-any.whl → 0.0.36__py3-none-any.whl

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.
ecodev_core/__init__.py CHANGED
@@ -40,6 +40,7 @@ from ecodev_core.db_insertion import get_raw_df
40
40
  from ecodev_core.db_retrieval import count_rows
41
41
  from ecodev_core.db_retrieval import get_rows
42
42
  from ecodev_core.db_retrieval import ServerSideField
43
+ from ecodev_core.deployment import Deployment
43
44
  from ecodev_core.email_sender import send_email
44
45
  from ecodev_core.enum_utils import enum_converter
45
46
  from ecodev_core.list_utils import first_func_or_default
@@ -65,6 +66,7 @@ from ecodev_core.pydantic_utils import CustomFrozen
65
66
  from ecodev_core.pydantic_utils import Frozen
66
67
  from ecodev_core.pydantic_utils import OrmFrozen
67
68
  from ecodev_core.read_write import load_json_file
69
+ from ecodev_core.read_write import load_yaml_file
68
70
  from ecodev_core.read_write import make_dir
69
71
  from ecodev_core.read_write import write_json_file
70
72
  from ecodev_core.safe_utils import boolify
@@ -75,6 +77,7 @@ from ecodev_core.safe_utils import safe_clt
75
77
  from ecodev_core.safe_utils import SafeTestCase
76
78
  from ecodev_core.safe_utils import SimpleReturn
77
79
  from ecodev_core.safe_utils import stringify
80
+ from ecodev_core.settings import Settings
78
81
 
79
82
  __all__ = [
80
83
  'AUTH', 'Token', 'get_app_services', 'attempt_to_log', 'get_current_user', 'is_admin_user',
@@ -89,4 +92,4 @@ __all__ = [
89
92
  'fastapi_monitor', 'dash_monitor', 'is_monitoring_user', 'get_recent_activities', 'select_user',
90
93
  'get_access_token', 'safe_get_user', 'backup', 'group_by', 'get_excelfile', 'upsert_new_user',
91
94
  'datify', 'safe_drop_columns', 'get_value', 'is_null', 'send_email', 'first_func_or_default',
92
- 'sort_by_keys', 'sort_by_values']
95
+ 'sort_by_keys', 'sort_by_values', 'Settings', 'load_yaml_file', 'Deployment']
@@ -0,0 +1,16 @@
1
+ """
2
+ Module implementing all types of deployment
3
+ """
4
+ from enum import Enum
5
+ from enum import unique
6
+
7
+
8
+ @unique
9
+ class Deployment(str, Enum):
10
+ """
11
+ Enum listing all types of deployment
12
+ """
13
+ LOCAL = 'local'
14
+ NON_PROD = 'nonprod'
15
+ PREPROD = 'preprod'
16
+ PROD = 'prod'
@@ -0,0 +1,76 @@
1
+ """
2
+ Module implementing a connection to an elastic search instance, and basic insertion/retrieval.
3
+ """
4
+ from typing import Any
5
+ from typing import Union
6
+
7
+ import progressbar
8
+ from elasticsearch import Elasticsearch
9
+ from elasticsearch import helpers
10
+ from pydantic_settings import BaseSettings
11
+ from pydantic_settings import SettingsConfigDict
12
+
13
+ from ecodev_core.logger import logger_get
14
+
15
+ ES_CLIENT: Union[Elasticsearch, None] = None
16
+ log = logger_get(__name__)
17
+ ES_BATCH_SIZE = 5000
18
+
19
+
20
+ class ESAuth(BaseSettings):
21
+ """
22
+ Simple ES authentication configuration class
23
+ """
24
+ host: str
25
+ user: str
26
+ password: str
27
+ port: int
28
+ index: str
29
+ model_config = SettingsConfigDict(env_file='.env', env_prefix='ES_')
30
+
31
+
32
+ ES_AUTH = ESAuth() # type: ignore
33
+
34
+
35
+ def get_es_client():
36
+ """
37
+ Get the elasticsearch client
38
+ """
39
+ global ES_CLIENT
40
+
41
+ if ES_CLIENT is None:
42
+ ES_CLIENT = Elasticsearch(f'http://{ES_AUTH.host}:{ES_AUTH.port}/',
43
+ basic_auth=[ES_AUTH.user, ES_AUTH.password])
44
+
45
+ return ES_CLIENT
46
+
47
+
48
+ def create_es_index(body: dict) -> None:
49
+ """
50
+ create an es index
51
+ """
52
+ client = get_es_client()
53
+ try:
54
+ client.indices.delete(index=ES_AUTH.index)
55
+ except Exception:
56
+ pass
57
+ client.indices.create(index=ES_AUTH.index, body=body)
58
+ log.info(f'index {ES_AUTH.index} created')
59
+
60
+
61
+ def insert_es_fields(operations: list[dict], batch_size: int = ES_BATCH_SIZE) -> None:
62
+ """
63
+ Generic es insertion
64
+ """
65
+ client = get_es_client()
66
+ batches = [list(operations)[i:i + batch_size] for i in range(0, len(operations), batch_size)]
67
+ log.info('indexing fields')
68
+ for batch in progressbar.progressbar(batches, redirect_stdout=False):
69
+ helpers.bulk(client, batch, index=ES_AUTH.index)
70
+
71
+
72
+ def retrieve_es_fields(body: dict[str, Any]) -> list[dict]:
73
+ """
74
+ Core call to the elasticsearch index
75
+ """
76
+ return get_es_client().search(index=ES_AUTH.index, body=body)
ecodev_core/list_utils.py CHANGED
@@ -118,3 +118,10 @@ def first_transformed_or_default(sequence: List[Any], transformation: Callable)
118
118
  or default value if no non-trivial transformed elements are found.
119
119
  """
120
120
  return next((fx for elt in sequence if (fx := transformation(elt)) is not None), None)
121
+
122
+
123
+ def dict_to_class(data: dict):
124
+ """
125
+ Convert a (possibly nested) dictionary to a class.
126
+ """
127
+ return {k: type(k, (), dict_to_class(v)) if isinstance(v, dict) else v for k, v in data.items()}
ecodev_core/logger.py CHANGED
@@ -5,6 +5,9 @@ import logging
5
5
  import sys
6
6
  import traceback
7
7
 
8
+ LIBS = ['azure', 'passlib', 'trimesh', 'fiona',
9
+ 'urllib3', 'botocore', 'boto', 'boto3', 's3transfer']
10
+
8
11
 
9
12
  def log_critical(message: str, logger):
10
13
  """
@@ -18,11 +21,24 @@ def logger_get(name, level=logging.DEBUG):
18
21
  """
19
22
  Main method called by all other modules to log
20
23
  """
24
+ logging.basicConfig(level=level, stream=sys.stdout)
25
+ for lib in LIBS:
26
+ _safe_log_setter(lib)
21
27
  logger = logging.getLogger(name)
22
28
  config_log(logger, level, MyFormatter())
23
29
  return logger
24
30
 
25
31
 
32
+ def _safe_log_setter(lib: str) -> None:
33
+ """
34
+ Safe logger. ERROR level not to be swamped by verbose library info.
35
+ """
36
+ try:
37
+ logging.getLogger(lib).setLevel(logging.ERROR)
38
+ except Exception:
39
+ pass
40
+
41
+
26
42
  class MyFormatter(logging.Formatter):
27
43
  """
28
44
  Formatter to print %(filename)s:%(funcName)s:%(lineno)d on 24 characters
ecodev_core/read_write.py CHANGED
@@ -8,6 +8,8 @@ from typing import Dict
8
8
  from typing import List
9
9
  from typing import Union
10
10
 
11
+ import yaml
12
+
11
13
 
12
14
  def write_json_file(json_data: Union[Dict, List], file_path: Path):
13
15
  """
@@ -28,6 +30,16 @@ def load_json_file(file_path: Path):
28
30
  return loaded_json
29
31
 
30
32
 
33
+ def load_yaml_file(file_path: Path):
34
+ """
35
+ Load a yaml file at file_path location
36
+ """
37
+ with open(file_path) as file:
38
+ loaded_yaml = yaml.safe_load(file)
39
+
40
+ return loaded_yaml
41
+
42
+
31
43
  def make_dir(directory: Path):
32
44
  """
33
45
  Helper that create the directory "directory" if it doesn't exist yet
@@ -0,0 +1,43 @@
1
+ """
2
+ Module defining a dynamic setting class
3
+ """
4
+ from pathlib import Path
5
+
6
+ from pydantic.utils import deep_update
7
+ from pydantic_settings import BaseSettings
8
+ from pydantic_settings import SettingsConfigDict
9
+
10
+ from ecodev_core.deployment import Deployment
11
+ from ecodev_core.list_utils import dict_to_class
12
+ from ecodev_core.read_write import load_yaml_file
13
+
14
+
15
+ class DeploymentSetting(BaseSettings):
16
+ """
17
+ Settings class used to load the deployment type from environment variables.
18
+ """
19
+ environment: str = 'local'
20
+ model_config = SettingsConfigDict(env_file='.env')
21
+
22
+
23
+ DEPLOYMENT = Deployment(DeploymentSetting().environment.lower())
24
+
25
+
26
+ class Settings:
27
+ """
28
+ Dynami setting class, loading yaml configuration from config file, possibly overwriting some of
29
+ this configuration with additional information coming from a secret file.
30
+ """
31
+
32
+ def __init__(self, base_path: Path = Path('/app'), deployment: Deployment = DEPLOYMENT):
33
+ """
34
+ Dynamically setting Settings attributes, doing so recursively. Attributes are loaded
35
+ from config file, possibly overwriting some of this configuration with additional
36
+ information coming from a secret file.
37
+ """
38
+ self.deployment = deployment
39
+ data = load_yaml_file(base_path / 'config' / f'{deployment.value}.yaml')
40
+ if (secrets_file := base_path / 'secrets' / f'{deployment.value}.yaml').exists():
41
+ data = deep_update(data, load_yaml_file(secrets_file))
42
+ for k, v in dict_to_class(data).items():
43
+ setattr(self, k, v)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ecodev-core
3
- Version: 0.0.34
3
+ Version: 0.0.36
4
4
  Summary: Low level sqlmodel/fastapi/pydantic building blocks
5
5
  License: MIT
6
6
  Author: Thomas Epelbaum
@@ -33,6 +33,7 @@ Classifier: Topic :: Software Development :: Libraries
33
33
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
34
34
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
35
35
  Classifier: Typing :: Typed
36
+ Requires-Dist: elasticsearch (>=8.14.0,<9.0.0)
36
37
  Requires-Dist: fastapi (>=0,<1)
37
38
  Requires-Dist: httpx (>=0,<1)
38
39
  Requires-Dist: numpy (>=1,<2)
@@ -40,8 +41,9 @@ Requires-Dist: openpyxl (>=3,<4)
40
41
  Requires-Dist: orjson (>=3.10.5,<4.0.0)
41
42
  Requires-Dist: pandas (>=2,<3)
42
43
  Requires-Dist: passlib[bcyrypt] (>=1,<2)
44
+ Requires-Dist: progressbar2 (>=4.4.2,<5.0.0)
43
45
  Requires-Dist: psycopg2-binary (>=2,<3)
44
- Requires-Dist: pydantic (>=2,<3)
46
+ Requires-Dist: pydantic (==2.9.2)
45
47
  Requires-Dist: pydantic-settings (>=2,<3)
46
48
  Requires-Dist: python-jose[cryptography] (>=3,<4)
47
49
  Requires-Dist: sqladmin (==0.15.2)
@@ -1,4 +1,4 @@
1
- ecodev_core/__init__.py,sha256=BcZLnkIVxOlVVXubWqKAAMRbUyFQt8v6JgfawC_6cPY,4970
1
+ ecodev_core/__init__.py,sha256=gIm_-wiXh8YyM-7xSQ07uNFNlPyAngmw45Sd062CT-k,5152
2
2
  ecodev_core/app_activity.py,sha256=_rU5uPfttHxXX5IaCuTA7K9We5w2qluJ3Xpf6i12HhY,3763
3
3
  ecodev_core/app_rights.py,sha256=RZPdDtydFqc_nFj96huKAc56BS0qS6ScKv4Kghqd6lc,726
4
4
  ecodev_core/app_user.py,sha256=r1bqA4H08x53XmxmjwyGKl_PFjYQazzBbVErdkztqeE,2947
@@ -11,17 +11,20 @@ ecodev_core/db_connection.py,sha256=bc5MujZ57f204wTsuNVdn1JdP-zBzkDJxHmdxBDTiNs,
11
11
  ecodev_core/db_filters.py,sha256=T_5JVF27UEu7sC6NOm7-W3_Y0GLfbWQO_EeTXcD2cv8,5041
12
12
  ecodev_core/db_insertion.py,sha256=RSCyAlUObbBlWJuMRX-YFY4VgtWqYLdwRqMWw--x95Y,3646
13
13
  ecodev_core/db_retrieval.py,sha256=IxyF3ZtKgACLiNFggK7boKggvMRKYDRD2IimxU4dap4,7345
14
+ ecodev_core/deployment.py,sha256=z8ACI00EtKknXOB8xyPwYIXTvPjIDOH9z9cBGEU0YrA,281
14
15
  ecodev_core/email_sender.py,sha256=XD7jAVXhGzvbiHqMhK9_aTEIS70Lw_CmPeAxRZGji-Y,1610
15
16
  ecodev_core/enum_utils.py,sha256=BkQ4YQ97tXBYmMcQiSIi0mbioD5CgVU79myg1BBAXuA,556
16
- ecodev_core/list_utils.py,sha256=s56vvhylMFzi6A76YvOIi4d5QYml73n_TmYNq1wM_P4,4427
17
- ecodev_core/logger.py,sha256=AWGGNZz12Ql0JDq1wCJQxwxK2syiczEBftmuJkjbAPw,3149
17
+ ecodev_core/es_connection.py,sha256=3z8KWF9yUzCW4xC9nhLlIcoW3Gw6m19MvH4Z6nxy7R4,1967
18
+ ecodev_core/list_utils.py,sha256=OltBlhp0COYPD8tc5eruqQq5nlVA7vp7fPYhTwXu6Ww,4632
19
+ ecodev_core/logger.py,sha256=_v63vq7jh72RRkzajHB9QkRfEPcwCMNcLh7maLeKwRg,3596
18
20
  ecodev_core/pandas_utils.py,sha256=Juc6gvPnoBiSVF2SR6_vfMi5W-QEkY3fnpo5ROB1L9s,2191
19
21
  ecodev_core/permissions.py,sha256=WAx-ilMu8LlQp2sjJVdkhNQieytEaEm8577ZF1HWeTY,502
20
22
  ecodev_core/pydantic_utils.py,sha256=e3GH50JmcpTmd2UgrB94QSwWOlOCW3WIlVdyX9C4T-U,741
21
- ecodev_core/read_write.py,sha256=auJ5bBJTVGkLRkiP_vZxVCX64B0Y-9qpsaDhovHmbas,996
23
+ ecodev_core/read_write.py,sha256=YIGRERvFHU7vy-JIaCWAza4CPMysLRUHKJxN-ZgFmu0,1208
22
24
  ecodev_core/safe_utils.py,sha256=JCfxo6fcznjsL-XHNJ1TKo1UvfJB83WT5jpTFmtJwsE,6160
25
+ ecodev_core/settings.py,sha256=lgC0GFi3nH9FxUlf2azzzKNT9-i2thl4UMKLxQHL9Xg,1559
23
26
  ecodev_core/sqlmodel_utils.py,sha256=t57H3QPtKRy4ujic1clMK_2L4p0yjGJLZbDjHPZ8M94,453
24
- ecodev_core-0.0.34.dist-info/LICENSE.md,sha256=jebQDe1ib9LAODuNvcSoo2CoqS6P0_q8--mMTICh_kI,1074
25
- ecodev_core-0.0.34.dist-info/METADATA,sha256=2wRebyF6ttEEeXjJtRrcu6sYsMbUWcBObY8wahQ0Ncw,3385
26
- ecodev_core-0.0.34.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
27
- ecodev_core-0.0.34.dist-info/RECORD,,
27
+ ecodev_core-0.0.36.dist-info/LICENSE.md,sha256=jebQDe1ib9LAODuNvcSoo2CoqS6P0_q8--mMTICh_kI,1074
28
+ ecodev_core-0.0.36.dist-info/METADATA,sha256=9OpeiKjWbClyXKtL2jirg96XdzVZRH9wiOs_QL5SEvc,3478
29
+ ecodev_core-0.0.36.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
30
+ ecodev_core-0.0.36.dist-info/RECORD,,