ecodev-core 0.0.67__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.
@@ -0,0 +1,129 @@
1
+ """
2
+ Module listing all public method from the ecodev_core modules
3
+ """
4
+ from ecodev_core.app_activity import AppActivity
5
+ from ecodev_core.app_activity import dash_monitor
6
+ from ecodev_core.app_activity import fastapi_monitor
7
+ from ecodev_core.app_activity import get_method
8
+ from ecodev_core.app_activity import get_recent_activities
9
+ from ecodev_core.app_rights import AppRight
10
+ from ecodev_core.app_user import AppUser
11
+ from ecodev_core.app_user import select_user
12
+ from ecodev_core.app_user import upsert_app_users
13
+ from ecodev_core.auth_configuration import AUTH
14
+ from ecodev_core.authentication import attempt_to_log
15
+ from ecodev_core.authentication import ban_token
16
+ from ecodev_core.authentication import get_access_token
17
+ from ecodev_core.authentication import get_app_services
18
+ from ecodev_core.authentication import get_current_user
19
+ from ecodev_core.authentication import get_user
20
+ from ecodev_core.authentication import is_admin_user
21
+ from ecodev_core.authentication import is_authorized_user
22
+ from ecodev_core.authentication import is_banned
23
+ from ecodev_core.authentication import is_monitoring_user
24
+ from ecodev_core.authentication import JwtAuth
25
+ from ecodev_core.authentication import safe_get_user
26
+ from ecodev_core.authentication import SCHEME
27
+ from ecodev_core.authentication import Token
28
+ from ecodev_core.authentication import upsert_new_user
29
+ from ecodev_core.backup import backup
30
+ from ecodev_core.check_dependencies import check_dependencies
31
+ from ecodev_core.check_dependencies import compute_dependencies
32
+ from ecodev_core.custom_equal import custom_equal
33
+ from ecodev_core.db_connection import create_db_and_tables
34
+ from ecodev_core.db_connection import DB_URL
35
+ from ecodev_core.db_connection import delete_table
36
+ from ecodev_core.db_connection import engine
37
+ from ecodev_core.db_connection import get_session
38
+ from ecodev_core.db_connection import info_message
39
+ from ecodev_core.db_filters import ServerSideFilter
40
+ from ecodev_core.db_i18n import get_lang
41
+ from ecodev_core.db_i18n import I18nMixin
42
+ from ecodev_core.db_i18n import Lang
43
+ from ecodev_core.db_i18n import localized_col
44
+ from ecodev_core.db_i18n import set_lang
45
+ from ecodev_core.db_insertion import generic_insertion
46
+ from ecodev_core.db_insertion import get_raw_df
47
+ from ecodev_core.db_retrieval import count_rows
48
+ from ecodev_core.db_retrieval import get_rows
49
+ from ecodev_core.db_retrieval import ServerSideField
50
+ from ecodev_core.db_upsertion import add_missing_columns
51
+ from ecodev_core.db_upsertion import add_missing_enum_values
52
+ from ecodev_core.db_upsertion import field
53
+ from ecodev_core.db_upsertion import filter_to_sfield_dict
54
+ from ecodev_core.db_upsertion import get_sfield_columns
55
+ from ecodev_core.db_upsertion import sfield
56
+ from ecodev_core.db_upsertion import upsert_data
57
+ from ecodev_core.db_upsertion import upsert_deletor
58
+ from ecodev_core.db_upsertion import upsert_df_data
59
+ from ecodev_core.db_upsertion import upsert_selector
60
+ from ecodev_core.deployment import Deployment
61
+ from ecodev_core.email_sender import send_email
62
+ from ecodev_core.encryption import decrypt_value
63
+ from ecodev_core.encryption import encrypt_value
64
+ from ecodev_core.enum_utils import enum_converter
65
+ from ecodev_core.list_utils import first_func_or_default
66
+ from ecodev_core.list_utils import first_or_default
67
+ from ecodev_core.list_utils import first_transformed_or_default
68
+ from ecodev_core.list_utils import group_by
69
+ from ecodev_core.list_utils import group_by_value
70
+ from ecodev_core.list_utils import lselect
71
+ from ecodev_core.list_utils import lselectfirst
72
+ from ecodev_core.list_utils import sort_by_keys
73
+ from ecodev_core.list_utils import sort_by_values
74
+ from ecodev_core.logger import log_critical
75
+ from ecodev_core.logger import logger_get
76
+ from ecodev_core.pandas_utils import get_excelfile
77
+ from ecodev_core.pandas_utils import get_value
78
+ from ecodev_core.pandas_utils import is_null
79
+ from ecodev_core.pandas_utils import jsonify_series
80
+ from ecodev_core.pandas_utils import pd_equals
81
+ from ecodev_core.pandas_utils import safe_drop_columns
82
+ from ecodev_core.permissions import Permission
83
+ from ecodev_core.pydantic_utils import Basic
84
+ from ecodev_core.pydantic_utils import CustomFrozen
85
+ from ecodev_core.pydantic_utils import Frozen
86
+ from ecodev_core.pydantic_utils import OrmFrozen
87
+ from ecodev_core.read_write import load_json_file
88
+ from ecodev_core.read_write import load_yaml_file
89
+ from ecodev_core.read_write import make_dir
90
+ from ecodev_core.read_write import write_json_file
91
+ from ecodev_core.safe_utils import boolify
92
+ from ecodev_core.safe_utils import datify
93
+ from ecodev_core.safe_utils import floatify
94
+ from ecodev_core.safe_utils import intify
95
+ from ecodev_core.safe_utils import safe_clt
96
+ from ecodev_core.safe_utils import SafeTestCase
97
+ from ecodev_core.safe_utils import SimpleReturn
98
+ from ecodev_core.safe_utils import stringify
99
+ from ecodev_core.settings import SETTINGS
100
+ from ecodev_core.settings import Settings
101
+ from ecodev_core.token_banlist import TokenBanlist
102
+ from ecodev_core.version import db_to_value
103
+ from ecodev_core.version import get_row_versions
104
+ from ecodev_core.version import get_versions
105
+ from ecodev_core.version import Version
106
+ from ecodev_core.rest_api_client import get_rest_api_client
107
+ from ecodev_core.rest_api_client import RestApiClient
108
+ from ecodev_core.rest_api_client import handle_response
109
+ from ecodev_core.rest_api_configuration import API_AUTH
110
+ __all__ = [
111
+ 'AUTH', 'Token', 'get_app_services', 'attempt_to_log', 'get_current_user', 'is_admin_user',
112
+ 'write_json_file', 'load_json_file', 'make_dir', 'check_dependencies', 'compute_dependencies',
113
+ 'engine', 'create_db_and_tables', 'get_session', 'info_message', 'group_by_value', 'OrmFrozen',
114
+ 'first_or_default', 'lselect', 'lselectfirst', 'first_transformed_or_default', 'log_critical',
115
+ 'logger_get', 'Permission', 'AppUser', 'AppRight', 'Basic', 'Frozen', 'CustomFrozen', 'JwtAuth',
116
+ 'SafeTestCase', 'SimpleReturn', 'safe_clt', 'stringify', 'boolify', 'get_user', 'floatify',
117
+ 'delete_table', 'SCHEME', 'DB_URL', 'pd_equals', 'jsonify_series', 'upsert_app_users', 'intify',
118
+ 'enum_converter', 'ServerSideFilter', 'get_rows', 'count_rows', 'ServerSideField', 'get_raw_df',
119
+ 'generic_insertion', 'custom_equal', 'is_authorized_user', 'get_method', 'AppActivity',
120
+ 'fastapi_monitor', 'dash_monitor', 'is_monitoring_user', 'get_recent_activities', 'select_user',
121
+ 'get_access_token', 'safe_get_user', 'backup', 'group_by', 'get_excelfile', 'upsert_new_user',
122
+ 'datify', 'safe_drop_columns', 'get_value', 'is_null', 'send_email', 'first_func_or_default',
123
+ 'sort_by_keys', 'sort_by_values', 'Settings', 'load_yaml_file', 'Deployment', 'Version',
124
+ 'sfield', 'field', 'upsert_df_data', 'upsert_deletor', 'get_row_versions', 'get_versions',
125
+ 'db_to_value', 'upsert_data', 'upsert_selector', 'get_sfield_columns', 'filter_to_sfield_dict',
126
+ 'SETTINGS', 'add_missing_enum_values', 'ban_token', 'TokenBanlist', 'is_banned',
127
+ 'get_lang', 'set_lang', 'Lang', 'localized_col', 'I18nMixin', 'add_missing_columns',
128
+ 'encrypt_value', 'decrypt_value', 'get_rest_api_client', 'RestApiClient', 'handle_response',
129
+ 'API_AUTH']
@@ -0,0 +1,126 @@
1
+ """
2
+ Module implementing a simple monitoring table
3
+ """
4
+ import inspect
5
+ from datetime import datetime
6
+ from typing import Optional
7
+
8
+ from sqlmodel import cast
9
+ from sqlmodel import col
10
+ from sqlmodel import extract
11
+ from sqlmodel import Field
12
+ from sqlmodel import func
13
+ from sqlmodel import Integer
14
+ from sqlmodel import select
15
+ from sqlmodel import Session
16
+ from sqlmodel import SQLModel
17
+
18
+ from ecodev_core.app_user import AppUser
19
+ from ecodev_core.authentication import get_user
20
+ from ecodev_core.db_connection import engine
21
+
22
+
23
+ """
24
+ Simple helper to retrieve the method name in which this helper is called
25
+
26
+ NB: this is meant to stay a lambda, otherwise the name retrieved is get_method, not the caller
27
+ """
28
+
29
+
30
+ def get_method(): return inspect.stack()[1][3]
31
+
32
+
33
+ class AppActivityBase(SQLModel):
34
+ """
35
+ Simple monitoring class
36
+
37
+ Attributes are:
38
+ - user: the name of the user that triggered the monitoring log
39
+ - application: the application in which the user triggered the monitoring log
40
+ - method: the method called by the user that triggered the monitoring log
41
+ - relevant_option: if filled, complementary information on method (num of treated lines...)
42
+ """
43
+ user: str = Field(index=True)
44
+ application: str = Field(index=True)
45
+ method: str = Field(index=True)
46
+ relevant_option: Optional[str] = Field(index=True, default=None)
47
+
48
+
49
+ class AppActivity(AppActivityBase, table=True): # type: ignore
50
+ """
51
+ The table version of the AppActivityBase monitoring class
52
+ """
53
+ __tablename__ = 'app_activity'
54
+ id: Optional[int] = Field(default=None, primary_key=True)
55
+ created_at: datetime = Field(default_factory=datetime.utcnow)
56
+
57
+
58
+ def dash_monitor(method: str,
59
+ token: dict,
60
+ application: str,
61
+ relevant_option: Optional[str] = None):
62
+ """
63
+ Generic dash monitor.
64
+
65
+ Attributes are:
66
+ - method: the method called by the user that triggered the monitoring log
67
+ - token: contains the information on the user that triggered the monitoring log
68
+ - application: the application in which the user triggered the monitoring log
69
+ - relevant_option: if filled, complementary information on method (num of treated lines...)
70
+ """
71
+ with Session(engine) as session:
72
+ user = get_user(token.get('token', {}).get('access_token'))
73
+ add_activity_to_db(method, user, application, session, relevant_option)
74
+
75
+
76
+ def fastapi_monitor(method: str,
77
+ user: AppUser,
78
+ application: str,
79
+ session: Session,
80
+ relevant_option: Optional[str] = None):
81
+ """
82
+ Generic fastapi monitor.
83
+
84
+ Attributes are:
85
+ - method: the method called by the user that triggered the monitoring log
86
+ - user: the name of the user that triggered the monitoring log
87
+ - application: the application in which the user triggered the monitoring log
88
+ - session: db connection
89
+ - relevant_option: if filled, complementary information on method (num of treated lines...)
90
+ """
91
+ add_activity_to_db(method, user, application, session, relevant_option)
92
+
93
+
94
+ def add_activity_to_db(method: str,
95
+ user: AppUser,
96
+ application: str,
97
+ session: Session,
98
+ relevant_option: Optional[str] = None):
99
+ """
100
+ Add a new entry in AppActivity given the passed arguments
101
+ """
102
+ session.add(AppActivity(user=user.user, application=application, method=method,
103
+ relevant_option=relevant_option))
104
+ session.commit()
105
+
106
+
107
+ def get_recent_activities(last_date: str, session: Session) -> list[AppActivity]:
108
+ """
109
+ Returns all activities that happened after last_date
110
+ """
111
+ return session.exec(select(AppActivity).where(col(AppActivity.created_at) > last_date)).all()
112
+
113
+
114
+ def get_monthly_activities(last_date: str, session: Session) -> dict[tuple[int, int], int]:
115
+ """
116
+ Returns all activities that happened after last_date, grouped by year month.
117
+ """
118
+ query = (select(
119
+ cast(extract('year', AppActivity.created_at), Integer).label('year'),
120
+ cast(extract('month', AppActivity.created_at), Integer).label('month'),
121
+ func.count().label('count'))
122
+ .where(col(AppActivity.created_at) > last_date)
123
+ .group_by(extract('year', AppActivity.created_at), extract('month', AppActivity.created_at))
124
+ .order_by(extract('year', AppActivity.created_at), extract('month', AppActivity.created_at))
125
+ )
126
+ return dict(sorted(((year, month), value) for year, month, value in session.exec(query).all()))
@@ -0,0 +1,24 @@
1
+ """
2
+ Module implementing the sqlmodel orm part of the right table
3
+ """
4
+ from typing import Optional
5
+ from typing import TYPE_CHECKING
6
+
7
+ from sqlmodel import Field
8
+ from sqlmodel import Relationship
9
+ from sqlmodel import SQLModel
10
+
11
+
12
+ if TYPE_CHECKING: # pragma: no cover
13
+ from ecodev_core.app_user import AppUser
14
+
15
+
16
+ class AppRight(SQLModel, table=True): # type: ignore
17
+ """
18
+ Simple right class: listing all app_services that a particular user can access to
19
+ """
20
+ __tablename__ = 'app_right'
21
+ id: Optional[int] = Field(default=None, primary_key=True)
22
+ app_service: str
23
+ user_id: Optional[int] = Field(default=None, foreign_key='app_user.id')
24
+ user: Optional['AppUser'] = Relationship(back_populates='rights')
@@ -0,0 +1,92 @@
1
+ """
2
+ Module implementing the sqlmodel orm part of the user table
3
+ """
4
+ from pathlib import Path
5
+ from typing import Any
6
+ from typing import List
7
+ from typing import Optional
8
+ from typing import TYPE_CHECKING
9
+
10
+ import pandas as pd
11
+ from sqlmodel import col
12
+ from sqlmodel import Field
13
+ from sqlmodel import Relationship
14
+ from sqlmodel import select
15
+ from sqlmodel import Session
16
+ from sqlmodel import SQLModel
17
+ from sqlmodel.sql.expression import SelectOfScalar
18
+
19
+ from ecodev_core.db_insertion import create_or_update
20
+ from ecodev_core.db_insertion import Insertor
21
+ from ecodev_core.permissions import Permission
22
+ from ecodev_core.read_write import load_json_file
23
+
24
+
25
+ if TYPE_CHECKING: # pragma: no cover
26
+ from ecodev_core.app_rights import AppRight
27
+
28
+
29
+ class AppUser(SQLModel, table=True): # type: ignore
30
+ """
31
+ Simple user class: an id associate to a user with a password
32
+ """
33
+ __tablename__ = 'app_user'
34
+ id: Optional[int] = Field(default=None, primary_key=True)
35
+ user: str = Field(index=True)
36
+ password: str
37
+ permission: Permission = Field(default=Permission.ADMIN)
38
+ client: Optional[str] = Field(default=None)
39
+ rights: List['AppRight'] = Relationship(back_populates='user')
40
+
41
+
42
+ def user_convertor(df: pd.DataFrame) -> List[Any]:
43
+ """
44
+ Dummy user convertor
45
+ """
46
+ return [x for _, x in df.iterrows()]
47
+
48
+
49
+ def user_reductor(in_db_row: AppUser, db_row: AppUser) -> AppUser:
50
+ """
51
+ Update an existing in_db_row with new information coming from db_row
52
+
53
+ NB: in the future this will maybe handle the client as well
54
+ """
55
+ in_db_row.permission = db_row.permission
56
+ in_db_row.password = db_row.password
57
+ return in_db_row
58
+
59
+
60
+ def user_selector(db_row: AppUser) -> SelectOfScalar:
61
+ """
62
+ Criteria on which to decide whether creating a new row or updating an existing one in db
63
+ """
64
+ return select(AppUser).where(col(AppUser.user) == db_row.user)
65
+
66
+
67
+ USER_INSERTOR = Insertor(convertor=user_convertor, selector=user_selector,
68
+ reductor=user_reductor, db_schema=AppUser, read_excel_file=False)
69
+
70
+
71
+ def upsert_app_users(file_path: Path, session: Session) -> None:
72
+ """
73
+ Upsert db users with a list of users provided in the file_path (json format)
74
+ """
75
+ for user in load_json_file(file_path):
76
+ session.add(create_or_update(session, user, USER_INSERTOR))
77
+ session.commit()
78
+
79
+
80
+ def select_user(username: str, session: Session) -> AppUser:
81
+ """
82
+ Helper function to (attempt to) retrieve AppUser from username.
83
+
84
+ NB: Used to check whether user exists, before resetting its password.
85
+ I.e. User does not yet have a token - we are simply checking if
86
+ an active account exists under that username.
87
+
88
+ Raises:
89
+ sqlalchemy.exc.NoResultFound: Typical error is no users are found.
90
+ sqlalchemy.exc.MultipleResultsFound: Should normally never be an issue.
91
+ """
92
+ return session.exec(select(AppUser).where(col(AppUser.user) == username)).one()
@@ -0,0 +1,24 @@
1
+ """
2
+ Module implementing authentication configuration.
3
+ """
4
+ from pydantic_settings import BaseSettings
5
+ from pydantic_settings import SettingsConfigDict
6
+
7
+ from ecodev_core.settings import SETTINGS
8
+
9
+
10
+ class AuthenticationConfiguration(BaseSettings):
11
+ """
12
+ Simple authentication configuration class
13
+ """
14
+ secret_key: str = ''
15
+ algorithm: str = ''
16
+ access_token_expire_minutes: int = 0
17
+ model_config = SettingsConfigDict(env_file='.env')
18
+
19
+
20
+ AUTH = AuthenticationConfiguration()
21
+ SETTINGS_AUTH = SETTINGS.authentication # type: ignore[attr-defined]
22
+ SECRET_KEY = SETTINGS_AUTH.secret_key or AUTH.secret_key
23
+ ALGO = SETTINGS_AUTH.algorithm or AUTH.algorithm
24
+ EXPIRATION_LENGTH = SETTINGS_AUTH.access_token_expire_minutes or AUTH.access_token_expire_minutes