arpakitlib 1.7.250__py3-none-any.whl → 1.7.252__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.
- arpakitlib/_arpakit_project_template/alembic/README +1 -0
 - arpakitlib/_arpakit_project_template/alembic/env.py +83 -0
 - arpakitlib/_arpakit_project_template/alembic/script.py.mako +26 -0
 - arpakitlib/_arpakit_project_template/alembic.ini +119 -0
 - arpakitlib/_arpakit_project_template/example.env +5 -14
 - arpakitlib/_arpakit_project_template/manage/docker_run_postgres.sh +4 -3
 - arpakitlib/_arpakit_project_template/manage/docker_start_postgres.sh +2 -1
 - arpakitlib/_arpakit_project_template/manage/docker_stop_postgres.sh +2 -1
 - arpakitlib/_arpakit_project_template/manage/git_set_arpakit_company_origin.sh +4 -2
 - arpakitlib/_arpakit_project_template/manage/git_set_arpakit_origin.sh +4 -2
 - arpakitlib/_arpakit_project_template/src/additional_model/additional_model.py +0 -7
 - arpakitlib/_arpakit_project_template/src/api/auth.py +52 -0
 - arpakitlib/_arpakit_project_template/src/api/create_api_app.py +21 -14
 - arpakitlib/_arpakit_project_template/src/api/create_handle_exception_.py +13 -13
 - arpakitlib/_arpakit_project_template/src/api/event.py +24 -2
 - arpakitlib/_arpakit_project_template/src/api/transmitted_api_data.py +3 -11
 - arpakitlib/_arpakit_project_template/src/core/settings.py +3 -111
 - arpakitlib/_arpakit_project_template/src/db/util.py +1 -3
 - arpakitlib/_arpakit_project_template/src/just_script/__init__.py +0 -0
 - arpakitlib/_arpakit_project_template/src/just_script/example.py +16 -0
 - arpakitlib/ar_arpakit_project_template_util.py +8 -18
 - arpakitlib/ar_arpakit_schedule_uust_api_client_util.py +5 -4
 - arpakitlib/ar_arpakitlib_cli_util.py +10 -22
 - arpakitlib/ar_class_util.py +0 -1
 - arpakitlib/ar_cryptomus_api_client_util.py +21 -0
 - arpakitlib/ar_fastapi_util.py +101 -70
 - arpakitlib/ar_schedule_uust_api_client_util.py +24 -24
 - arpakitlib/ar_settings_util.py +166 -14
 - arpakitlib/ar_sqlalchemy_model_util.py +1 -1
 - arpakitlib/ar_steam_payment_api_client_util.py +21 -0
 - arpakitlib/ar_wata_api_client.py +21 -0
 - {arpakitlib-1.7.250.dist-info → arpakitlib-1.7.252.dist-info}/METADATA +1 -1
 - {arpakitlib-1.7.250.dist-info → arpakitlib-1.7.252.dist-info}/RECORD +37 -28
 - /arpakitlib/_arpakit_project_template/src/core/{_check_settings.py → _show_settings.py} +0 -0
 - {arpakitlib-1.7.250.dist-info → arpakitlib-1.7.252.dist-info}/LICENSE +0 -0
 - {arpakitlib-1.7.250.dist-info → arpakitlib-1.7.252.dist-info}/WHEEL +0 -0
 - {arpakitlib-1.7.250.dist-info → arpakitlib-1.7.252.dist-info}/entry_points.txt +0 -0
 
    
        arpakitlib/ar_fastapi_util.py
    CHANGED
    
    | 
         @@ -4,6 +4,7 @@ from __future__ import annotations 
     | 
|
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            import asyncio
         
     | 
| 
       6 
6 
     | 
    
         
             
            import datetime as dt
         
     | 
| 
      
 7 
     | 
    
         
            +
            import inspect
         
     | 
| 
       7 
8 
     | 
    
         
             
            import logging
         
     | 
| 
       8 
9 
     | 
    
         
             
            import os.path
         
     | 
| 
       9 
10 
     | 
    
         
             
            import pathlib
         
     | 
| 
         @@ -24,12 +25,14 @@ from starlette import status 
     | 
|
| 
       24 
25 
     | 
    
         
             
            from starlette.middleware.cors import CORSMiddleware
         
     | 
| 
       25 
26 
     | 
    
         
             
            from starlette.staticfiles import StaticFiles
         
     | 
| 
       26 
27 
     | 
    
         | 
| 
      
 28 
     | 
    
         
            +
            from arpakitlib.ar_datetime_util import now_utc_dt
         
     | 
| 
       27 
29 
     | 
    
         
             
            from arpakitlib.ar_dict_util import combine_dicts
         
     | 
| 
       28 
30 
     | 
    
         
             
            from arpakitlib.ar_enumeration_util import Enumeration
         
     | 
| 
       29 
31 
     | 
    
         
             
            from arpakitlib.ar_exception_util import exception_to_traceback_str
         
     | 
| 
      
 32 
     | 
    
         
            +
            from arpakitlib.ar_file_storage_in_dir_util import FileStorageInDir
         
     | 
| 
       30 
33 
     | 
    
         
             
            from arpakitlib.ar_func_util import raise_if_not_async_func, is_async_object
         
     | 
| 
       31 
34 
     | 
    
         
             
            from arpakitlib.ar_json_util import safely_transfer_obj_to_json_str_to_json_obj, safely_transfer_obj_to_json_str
         
     | 
| 
       32 
     | 
    
         
            -
            from arpakitlib. 
     | 
| 
      
 35 
     | 
    
         
            +
            from arpakitlib.ar_settings_util import SimpleSettings
         
     | 
| 
       33 
36 
     | 
    
         
             
            from arpakitlib.ar_sqlalchemy_model_util import StoryLogDBM, OperationDBM
         
     | 
| 
       34 
37 
     | 
    
         
             
            from arpakitlib.ar_sqlalchemy_util import SQLAlchemyDB
         
     | 
| 
       35 
38 
     | 
    
         
             
            from arpakitlib.ar_type_util import raise_for_type, raise_if_none
         
     | 
| 
         @@ -212,19 +215,20 @@ class APIException(fastapi.exceptions.HTTPException): 
     | 
|
| 
       212 
215 
     | 
    
         | 
| 
       213 
216 
     | 
    
         
             
            def create_handle_exception(
         
     | 
| 
       214 
217 
     | 
    
         
             
                    *,
         
     | 
| 
       215 
     | 
    
         
            -
                     
     | 
| 
       216 
     | 
    
         
            -
                     
     | 
| 
      
 218 
     | 
    
         
            +
                    funcs_before: list[Callable | None] | None = None,
         
     | 
| 
      
 219 
     | 
    
         
            +
                    async_funcs_after: list[Callable | None] | None = None,
         
     | 
| 
       217 
220 
     | 
    
         
             
            ) -> Callable:
         
     | 
| 
       218 
     | 
    
         
            -
                if  
     | 
| 
       219 
     | 
    
         
            -
                     
     | 
| 
       220 
     | 
    
         
            -
                 
     | 
| 
      
 221 
     | 
    
         
            +
                if funcs_before is None:
         
     | 
| 
      
 222 
     | 
    
         
            +
                    funcs_before = []
         
     | 
| 
      
 223 
     | 
    
         
            +
                funcs_before = [v for v in funcs_before if v is not None]
         
     | 
| 
       221 
224 
     | 
    
         | 
| 
       222 
     | 
    
         
            -
                if  
     | 
| 
       223 
     | 
    
         
            -
                     
     | 
| 
       224 
     | 
    
         
            -
                 
     | 
| 
      
 225 
     | 
    
         
            +
                if async_funcs_after is None:
         
     | 
| 
      
 226 
     | 
    
         
            +
                    async_funcs_after = []
         
     | 
| 
      
 227 
     | 
    
         
            +
                async_funcs_after = [v for v in async_funcs_after if v is not None]
         
     | 
| 
       225 
228 
     | 
    
         | 
| 
       226 
     | 
    
         
            -
                async def  
     | 
| 
       227 
     | 
    
         
            -
                        request: starlette.requests.Request, 
     | 
| 
      
 229 
     | 
    
         
            +
                async def func(
         
     | 
| 
      
 230 
     | 
    
         
            +
                        request: starlette.requests.Request,
         
     | 
| 
      
 231 
     | 
    
         
            +
                        exception: Exception
         
     | 
| 
       228 
232 
     | 
    
         
             
                ) -> APIJSONResponse:
         
     | 
| 
       229 
233 
     | 
    
         
             
                    status_code = starlette.status.HTTP_500_INTERNAL_SERVER_ERROR
         
     | 
| 
       230 
234 
     | 
    
         | 
| 
         @@ -266,10 +270,10 @@ def create_handle_exception( 
     | 
|
| 
       266 
270 
     | 
    
         
             
                        status_code = starlette.status.HTTP_500_INTERNAL_SERVER_ERROR
         
     | 
| 
       267 
271 
     | 
    
         
             
                        error_so.error_code = BaseAPIErrorCodes.unknown_error
         
     | 
| 
       268 
272 
     | 
    
         | 
| 
       269 
     | 
    
         
            -
                    if error_so.error_code:
         
     | 
| 
      
 273 
     | 
    
         
            +
                    if error_so.error_code is not None:
         
     | 
| 
       270 
274 
     | 
    
         
             
                        error_so.error_code = error_so.error_code.upper().replace(" ", "_").strip()
         
     | 
| 
       271 
275 
     | 
    
         | 
| 
       272 
     | 
    
         
            -
                    if error_so.error_specification_code:
         
     | 
| 
      
 276 
     | 
    
         
            +
                    if error_so.error_specification_code is not None:
         
     | 
| 
       273 
277 
     | 
    
         
             
                        error_so.error_specification_code = (
         
     | 
| 
       274 
278 
     | 
    
         
             
                            error_so.error_specification_code.upper().replace(" ", "_").strip()
         
     | 
| 
       275 
279 
     | 
    
         
             
                        )
         
     | 
| 
         @@ -280,23 +284,29 @@ def create_handle_exception( 
     | 
|
| 
       280 
284 
     | 
    
         
             
                    if error_so.error_code == BaseAPIErrorCodes.cannot_authorize:
         
     | 
| 
       281 
285 
     | 
    
         
             
                        status_code = status.HTTP_401_UNAUTHORIZED
         
     | 
| 
       282 
286 
     | 
    
         | 
| 
       283 
     | 
    
         
            -
                     
     | 
| 
       284 
     | 
    
         
            -
             
     | 
| 
       285 
     | 
    
         
            -
             
     | 
| 
       286 
     | 
    
         
            -
             
     | 
| 
      
 287 
     | 
    
         
            +
                    error_so.error_data["status_code"] = status_code
         
     | 
| 
      
 288 
     | 
    
         
            +
             
     | 
| 
      
 289 
     | 
    
         
            +
                    # funcs_before
         
     | 
| 
      
 290 
     | 
    
         
            +
             
     | 
| 
      
 291 
     | 
    
         
            +
                    _transmitted_kwargs = {}
         
     | 
| 
      
 292 
     | 
    
         
            +
                    for func_before in funcs_before:
         
     | 
| 
      
 293 
     | 
    
         
            +
                        _func_data = func_before(
         
     | 
| 
      
 294 
     | 
    
         
            +
                            request=request, status_code=status_code, error_so=error_so, exception=exception,
         
     | 
| 
      
 295 
     | 
    
         
            +
                            transmitted_kwargs=_transmitted_kwargs
         
     | 
| 
       287 
296 
     | 
    
         
             
                        )
         
     | 
| 
       288 
297 
     | 
    
         
             
                        if is_async_object(_func_data):
         
     | 
| 
       289 
298 
     | 
    
         
             
                            _func_data = await _func_data
         
     | 
| 
       290 
299 
     | 
    
         
             
                        if _func_data is not None:
         
     | 
| 
       291 
     | 
    
         
            -
                             
     | 
| 
       292 
     | 
    
         
            -
                            raise_for_type(status_code, int)
         
     | 
| 
      
 300 
     | 
    
         
            +
                            error_so, _transmitted_kwargs = _func_data[0], _func_data[1]
         
     | 
| 
       293 
301 
     | 
    
         
             
                            raise_for_type(error_so, ErrorSO)
         
     | 
| 
       294 
     | 
    
         
            -
                            raise_for_type( 
     | 
| 
      
 302 
     | 
    
         
            +
                            raise_for_type(_transmitted_kwargs, dict)
         
     | 
| 
      
 303 
     | 
    
         
            +
             
     | 
| 
      
 304 
     | 
    
         
            +
                    # async_funcs_after
         
     | 
| 
       295 
305 
     | 
    
         | 
| 
       296 
     | 
    
         
            -
                    for  
     | 
| 
       297 
     | 
    
         
            -
                        raise_if_not_async_func( 
     | 
| 
       298 
     | 
    
         
            -
                        _ = asyncio.create_task( 
     | 
| 
       299 
     | 
    
         
            -
                             
     | 
| 
      
 306 
     | 
    
         
            +
                    for async_func_after in async_funcs_after:
         
     | 
| 
      
 307 
     | 
    
         
            +
                        raise_if_not_async_func(async_func_after)
         
     | 
| 
      
 308 
     | 
    
         
            +
                        _ = asyncio.create_task(async_func_after(
         
     | 
| 
      
 309 
     | 
    
         
            +
                            request=request, status_code=status_code, error_so=error_so, exception=exception
         
     | 
| 
       300 
310 
     | 
    
         
             
                        ))
         
     | 
| 
       301 
311 
     | 
    
         | 
| 
       302 
312 
     | 
    
         
             
                    return APIJSONResponse(
         
     | 
| 
         @@ -304,43 +314,46 @@ def create_handle_exception( 
     | 
|
| 
       304 
314 
     | 
    
         
             
                        status_code=status_code
         
     | 
| 
       305 
315 
     | 
    
         
             
                    )
         
     | 
| 
       306 
316 
     | 
    
         | 
| 
       307 
     | 
    
         
            -
                return  
     | 
| 
      
 317 
     | 
    
         
            +
                return func
         
     | 
| 
       308 
318 
     | 
    
         | 
| 
       309 
319 
     | 
    
         | 
| 
       310 
     | 
    
         
            -
            def  
     | 
| 
      
 320 
     | 
    
         
            +
            def logging__api_func_before_in_handle_exception(
         
     | 
| 
       311 
321 
     | 
    
         
             
                    *,
         
     | 
| 
       312 
322 
     | 
    
         
             
                    ignore_api_error_codes: list[str] | None = None,
         
     | 
| 
       313 
323 
     | 
    
         
             
                    ignore_status_codes: list[int] | None = None,
         
     | 
| 
       314 
324 
     | 
    
         
             
                    ignore_exception_types: list[type[Exception]] | None = None,
         
     | 
| 
       315 
325 
     | 
    
         
             
                    need_exc_info: bool = False
         
     | 
| 
       316 
     | 
    
         
            -
            ):
         
     | 
| 
      
 326 
     | 
    
         
            +
            ) -> Callable:
         
     | 
| 
      
 327 
     | 
    
         
            +
                current_func_name = inspect.currentframe().f_code.co_name
         
     | 
| 
      
 328 
     | 
    
         
            +
             
     | 
| 
       317 
329 
     | 
    
         
             
                def func(
         
     | 
| 
       318 
330 
     | 
    
         
             
                        *,
         
     | 
| 
      
 331 
     | 
    
         
            +
                        request: starlette.requests.Request,
         
     | 
| 
       319 
332 
     | 
    
         
             
                        status_code: int,
         
     | 
| 
       320 
333 
     | 
    
         
             
                        error_so: ErrorSO,
         
     | 
| 
       321 
     | 
    
         
            -
                        request: starlette.requests.Request,
         
     | 
| 
       322 
334 
     | 
    
         
             
                        exception: Exception,
         
     | 
| 
      
 335 
     | 
    
         
            +
                        transmitted_kwargs: dict[str, Any],
         
     | 
| 
       323 
336 
     | 
    
         
             
                        **kwargs
         
     | 
| 
       324 
     | 
    
         
            -
                ) -> ( 
     | 
| 
       325 
     | 
    
         
            -
                     
     | 
| 
      
 337 
     | 
    
         
            +
                ) -> (ErrorSO, dict[str, Any]):
         
     | 
| 
      
 338 
     | 
    
         
            +
                    transmitted_kwargs[current_func_name] = now_utc_dt()
         
     | 
| 
       326 
339 
     | 
    
         | 
| 
       327 
340 
     | 
    
         
             
                    if ignore_api_error_codes and error_so.error_code in ignore_api_error_codes:
         
     | 
| 
       328 
     | 
    
         
            -
                        return  
     | 
| 
      
 341 
     | 
    
         
            +
                        return error_so, transmitted_kwargs
         
     | 
| 
       329 
342 
     | 
    
         | 
| 
       330 
343 
     | 
    
         
             
                    if ignore_status_codes and status_code in ignore_status_codes:
         
     | 
| 
       331 
     | 
    
         
            -
                        return  
     | 
| 
      
 344 
     | 
    
         
            +
                        return error_so, transmitted_kwargs
         
     | 
| 
       332 
345 
     | 
    
         | 
| 
       333 
346 
     | 
    
         
             
                    if ignore_exception_types and (
         
     | 
| 
       334 
347 
     | 
    
         
             
                            exception in ignore_exception_types or type(exception) in ignore_exception_types
         
     | 
| 
       335 
348 
     | 
    
         
             
                    ):
         
     | 
| 
       336 
     | 
    
         
            -
                        return  
     | 
| 
      
 349 
     | 
    
         
            +
                        return error_so, transmitted_kwargs
         
     | 
| 
       337 
350 
     | 
    
         | 
| 
       338 
351 
     | 
    
         
             
                    _logger.error(safely_transfer_obj_to_json_str(error_so.model_dump()), exc_info=need_exc_info)
         
     | 
| 
       339 
352 
     | 
    
         | 
| 
       340 
353 
     | 
    
         
             
                return func
         
     | 
| 
       341 
354 
     | 
    
         | 
| 
       342 
355 
     | 
    
         | 
| 
       343 
     | 
    
         
            -
            def  
     | 
| 
      
 356 
     | 
    
         
            +
            def story_log__api_func_before_in_handle_exception(
         
     | 
| 
       344 
357 
     | 
    
         
             
                    *,
         
     | 
| 
       345 
358 
     | 
    
         
             
                    sqlalchemy_db: SQLAlchemyDB,
         
     | 
| 
       346 
359 
     | 
    
         
             
                    ignore_api_error_codes: list[str] | None = None,
         
     | 
| 
         @@ -349,26 +362,29 @@ def story_log_func_before_response( 
     | 
|
| 
       349 
362 
     | 
    
         
             
            ) -> Callable:
         
     | 
| 
       350 
363 
     | 
    
         
             
                raise_for_type(sqlalchemy_db, SQLAlchemyDB)
         
     | 
| 
       351 
364 
     | 
    
         | 
| 
      
 365 
     | 
    
         
            +
                current_func_name = inspect.currentframe().f_code.co_name
         
     | 
| 
      
 366 
     | 
    
         
            +
             
     | 
| 
       352 
367 
     | 
    
         
             
                async def async_func(
         
     | 
| 
       353 
368 
     | 
    
         
             
                        *,
         
     | 
| 
      
 369 
     | 
    
         
            +
                        request: starlette.requests.Request,
         
     | 
| 
       354 
370 
     | 
    
         
             
                        status_code: int,
         
     | 
| 
       355 
371 
     | 
    
         
             
                        error_so: ErrorSO,
         
     | 
| 
       356 
     | 
    
         
            -
                        request: starlette.requests.Request,
         
     | 
| 
       357 
372 
     | 
    
         
             
                        exception: Exception,
         
     | 
| 
      
 373 
     | 
    
         
            +
                        transmitted_kwargs: dict[str, Any],
         
     | 
| 
       358 
374 
     | 
    
         
             
                        **kwargs
         
     | 
| 
       359 
     | 
    
         
            -
                ) -> ( 
     | 
| 
       360 
     | 
    
         
            -
                     
     | 
| 
      
 375 
     | 
    
         
            +
                ) -> (ErrorSO, dict[str, Any]):
         
     | 
| 
      
 376 
     | 
    
         
            +
                    transmitted_kwargs[current_func_name] = now_utc_dt()
         
     | 
| 
       361 
377 
     | 
    
         | 
| 
       362 
378 
     | 
    
         
             
                    if ignore_api_error_codes and error_so.error_code in ignore_api_error_codes:
         
     | 
| 
       363 
     | 
    
         
            -
                        return  
     | 
| 
      
 379 
     | 
    
         
            +
                        return error_so, transmitted_kwargs
         
     | 
| 
       364 
380 
     | 
    
         | 
| 
       365 
381 
     | 
    
         
             
                    if ignore_status_codes and status_code in ignore_status_codes:
         
     | 
| 
       366 
     | 
    
         
            -
                        return  
     | 
| 
      
 382 
     | 
    
         
            +
                        return error_so, transmitted_kwargs
         
     | 
| 
       367 
383 
     | 
    
         | 
| 
       368 
384 
     | 
    
         
             
                    if ignore_exception_types and (
         
     | 
| 
       369 
385 
     | 
    
         
             
                            exception in ignore_exception_types or type(exception) in ignore_exception_types
         
     | 
| 
       370 
386 
     | 
    
         
             
                    ):
         
     | 
| 
       371 
     | 
    
         
            -
                        return  
     | 
| 
      
 387 
     | 
    
         
            +
                        return error_so, transmitted_kwargs
         
     | 
| 
       372 
388 
     | 
    
         | 
| 
       373 
389 
     | 
    
         
             
                    async with sqlalchemy_db.new_async_session() as session:
         
     | 
| 
       374 
390 
     | 
    
         
             
                        story_log_dbm = StoryLogDBM(
         
     | 
| 
         @@ -384,9 +400,9 @@ def story_log_func_before_response( 
     | 
|
| 
       384 
400 
     | 
    
         
             
                        await session.refresh(story_log_dbm)
         
     | 
| 
       385 
401 
     | 
    
         | 
| 
       386 
402 
     | 
    
         
             
                    error_so.error_data.update({"story_log_long_id": story_log_dbm.long_id})
         
     | 
| 
       387 
     | 
    
         
            -
                     
     | 
| 
      
 403 
     | 
    
         
            +
                    transmitted_kwargs["story_log_id"] = story_log_dbm.id
         
     | 
| 
       388 
404 
     | 
    
         | 
| 
       389 
     | 
    
         
            -
                    return  
     | 
| 
      
 405 
     | 
    
         
            +
                    return error_so, transmitted_kwargs
         
     | 
| 
       390 
406 
     | 
    
         | 
| 
       391 
407 
     | 
    
         
             
                return async_func
         
     | 
| 
       392 
408 
     | 
    
         | 
| 
         @@ -463,7 +479,7 @@ class ARPAKITLibSO(BaseSO): 
     | 
|
| 
       463 
479 
     | 
    
         
             
                arpakitlib: bool = True
         
     | 
| 
       464 
480 
     | 
    
         | 
| 
       465 
481 
     | 
    
         | 
| 
       466 
     | 
    
         
            -
            def  
     | 
| 
      
 482 
     | 
    
         
            +
            def create_needed_api_router():
         
     | 
| 
       467 
483 
     | 
    
         
             
                api_router = APIRouter()
         
     | 
| 
       468 
484 
     | 
    
         | 
| 
       469 
485 
     | 
    
         
             
                @api_router.get(
         
     | 
| 
         @@ -490,9 +506,7 @@ def add_needed_api_router_to_app(*, app: FastAPI): 
     | 
|
| 
       490 
506 
     | 
    
         
             
                        content=ARPAKITLibSO(arpakitlib=True)
         
     | 
| 
       491 
507 
     | 
    
         
             
                    )
         
     | 
| 
       492 
508 
     | 
    
         | 
| 
       493 
     | 
    
         
            -
                 
     | 
| 
       494 
     | 
    
         
            -
             
     | 
| 
       495 
     | 
    
         
            -
                return app
         
     | 
| 
      
 509 
     | 
    
         
            +
                return api_router
         
     | 
| 
       496 
510 
     | 
    
         | 
| 
       497 
511 
     | 
    
         | 
| 
       498 
512 
     | 
    
         
             
            class BaseStartupAPIEvent:
         
     | 
| 
         @@ -517,6 +531,17 @@ class BaseTransmittedAPIData(BaseModel): 
     | 
|
| 
       517 
531 
     | 
    
         
             
                model_config = ConfigDict(extra="ignore", arbitrary_types_allowed=True, from_attributes=True)
         
     | 
| 
       518 
532 
     | 
    
         | 
| 
       519 
533 
     | 
    
         | 
| 
      
 534 
     | 
    
         
            +
            class SimpleTransmittedAPIData(BaseTransmittedAPIData):
         
     | 
| 
      
 535 
     | 
    
         
            +
                settings: SimpleSettings | None = None
         
     | 
| 
      
 536 
     | 
    
         
            +
             
     | 
| 
      
 537 
     | 
    
         
            +
             
     | 
| 
      
 538 
     | 
    
         
            +
            class AdvancedTransmittedAPIData(SimpleTransmittedAPIData):
         
     | 
| 
      
 539 
     | 
    
         
            +
                sqlalchemy_db: SQLAlchemyDB | None = None
         
     | 
| 
      
 540 
     | 
    
         
            +
                media_file_storage_in_dir: FileStorageInDir | None = None
         
     | 
| 
      
 541 
     | 
    
         
            +
                cache_file_storage_in_dir: FileStorageInDir | None = None
         
     | 
| 
      
 542 
     | 
    
         
            +
                dump_file_storage_in_dir: FileStorageInDir | None = None
         
     | 
| 
      
 543 
     | 
    
         
            +
             
     | 
| 
      
 544 
     | 
    
         
            +
             
     | 
| 
       520 
545 
     | 
    
         
             
            def get_transmitted_api_data(request: starlette.requests.Request) -> BaseTransmittedAPIData:
         
     | 
| 
       521 
546 
     | 
    
         
             
                return request.app.state.transmitted_api_data
         
     | 
| 
       522 
547 
     | 
    
         | 
| 
         @@ -574,7 +599,9 @@ def base_api_auth( 
     | 
|
| 
       574 
599 
     | 
    
         
             
                        ac: fastapi.security.HTTPAuthorizationCredentials | None = fastapi.Security(
         
     | 
| 
       575 
600 
     | 
    
         
             
                            fastapi.security.HTTPBearer(auto_error=False)
         
     | 
| 
       576 
601 
     | 
    
         
             
                        ),
         
     | 
| 
       577 
     | 
    
         
            -
                        api_key_string: str | None = Security( 
     | 
| 
      
 602 
     | 
    
         
            +
                        api_key_string: str | None = Security(
         
     | 
| 
      
 603 
     | 
    
         
            +
                            APIKeyHeader(name="apikey", auto_error=False)
         
     | 
| 
      
 604 
     | 
    
         
            +
                        ),
         
     | 
| 
       578 
605 
     | 
    
         
             
                        request: starlette.requests.Request,
         
     | 
| 
       579 
606 
     | 
    
         
             
                        transmitted_api_data: BaseTransmittedAPIData = Depends(get_transmitted_api_data)
         
     | 
| 
       580 
607 
     | 
    
         
             
                ) -> BaseAPIAuthData:
         
     | 
| 
         @@ -586,7 +613,7 @@ def base_api_auth( 
     | 
|
| 
       586 
613 
     | 
    
         
             
                        require_correct_token=require_correct_token
         
     | 
| 
       587 
614 
     | 
    
         
             
                    )
         
     | 
| 
       588 
615 
     | 
    
         | 
| 
       589 
     | 
    
         
            -
                    # api_key
         
     | 
| 
      
 616 
     | 
    
         
            +
                    # parse api_key
         
     | 
| 
       590 
617 
     | 
    
         | 
| 
       591 
618 
     | 
    
         
             
                    api_auth_data.api_key_string = api_key_string
         
     | 
| 
       592 
619 
     | 
    
         | 
| 
         @@ -604,7 +631,7 @@ def base_api_auth( 
     | 
|
| 
       604 
631 
     | 
    
         
             
                    if not api_auth_data.api_key_string and "apikey" in request.query_params.keys():
         
     | 
| 
       605 
632 
     | 
    
         
             
                        api_auth_data.api_key_string = request.query_params["apikey"]
         
     | 
| 
       606 
633 
     | 
    
         | 
| 
       607 
     | 
    
         
            -
                    # token
         
     | 
| 
      
 634 
     | 
    
         
            +
                    # parse token
         
     | 
| 
       608 
635 
     | 
    
         | 
| 
       609 
636 
     | 
    
         
             
                    api_auth_data.token_string = ac.credentials if ac and ac.credentials and ac.credentials.strip() else None
         
     | 
| 
       610 
637 
     | 
    
         | 
| 
         @@ -633,7 +660,7 @@ def base_api_auth( 
     | 
|
| 
       633 
660 
     | 
    
         
             
                    if not api_auth_data.token_string:
         
     | 
| 
       634 
661 
     | 
    
         
             
                        api_auth_data.token_string = None
         
     | 
| 
       635 
662 
     | 
    
         | 
| 
       636 
     | 
    
         
            -
                    #  
     | 
| 
      
 663 
     | 
    
         
            +
                    # require_api_key_string
         
     | 
| 
       637 
664 
     | 
    
         | 
| 
       638 
665 
     | 
    
         
             
                    if require_api_key_string and not api_auth_data.api_key_string:
         
     | 
| 
       639 
666 
     | 
    
         
             
                        raise APIException(
         
     | 
| 
         @@ -642,7 +669,7 @@ def base_api_auth( 
     | 
|
| 
       642 
669 
     | 
    
         
             
                            error_data=safely_transfer_obj_to_json_str_to_json_obj(api_auth_data.model_dump())
         
     | 
| 
       643 
670 
     | 
    
         
             
                        )
         
     | 
| 
       644 
671 
     | 
    
         | 
| 
       645 
     | 
    
         
            -
                    #  
     | 
| 
      
 672 
     | 
    
         
            +
                    # require_token_string
         
     | 
| 
       646 
673 
     | 
    
         | 
| 
       647 
674 
     | 
    
         
             
                    if require_token_string and not api_auth_data.token_string:
         
     | 
| 
       648 
675 
     | 
    
         
             
                        raise APIException(
         
     | 
| 
         @@ -651,10 +678,10 @@ def base_api_auth( 
     | 
|
| 
       651 
678 
     | 
    
         
             
                            error_data=safely_transfer_obj_to_json_str_to_json_obj(api_auth_data.model_dump())
         
     | 
| 
       652 
679 
     | 
    
         
             
                        )
         
     | 
| 
       653 
680 
     | 
    
         | 
| 
       654 
     | 
    
         
            -
                    #  
     | 
| 
      
 681 
     | 
    
         
            +
                    # validate_api_key_func
         
     | 
| 
       655 
682 
     | 
    
         | 
| 
       656 
683 
     | 
    
         
             
                    if validate_api_key_func is not None:
         
     | 
| 
       657 
     | 
    
         
            -
                         
     | 
| 
      
 684 
     | 
    
         
            +
                        validate_api_key_func_res = validate_api_key_func(
         
     | 
| 
       658 
685 
     | 
    
         
             
                            api_key_string=api_auth_data.api_key_string,
         
     | 
| 
       659 
686 
     | 
    
         
             
                            token_string=api_auth_data.token_string,
         
     | 
| 
       660 
687 
     | 
    
         
             
                            base_api_auth_data=api_auth_data,
         
     | 
| 
         @@ -662,14 +689,14 @@ def base_api_auth( 
     | 
|
| 
       662 
689 
     | 
    
         
             
                            request=request,
         
     | 
| 
       663 
690 
     | 
    
         
             
                            **kwargs
         
     | 
| 
       664 
691 
     | 
    
         
             
                        )
         
     | 
| 
       665 
     | 
    
         
            -
                        if is_async_object( 
     | 
| 
       666 
     | 
    
         
            -
                             
     | 
| 
       667 
     | 
    
         
            -
                        api_auth_data.is_api_key_correct =  
     | 
| 
      
 692 
     | 
    
         
            +
                        if is_async_object(validate_api_key_func_res):
         
     | 
| 
      
 693 
     | 
    
         
            +
                            validate_api_key_func_res = await validate_api_key_func_res
         
     | 
| 
      
 694 
     | 
    
         
            +
                        api_auth_data.is_api_key_correct = validate_api_key_func_res
         
     | 
| 
       668 
695 
     | 
    
         | 
| 
       669 
     | 
    
         
            -
                    #  
     | 
| 
      
 696 
     | 
    
         
            +
                    # validate_token_func
         
     | 
| 
       670 
697 
     | 
    
         | 
| 
       671 
698 
     | 
    
         
             
                    if validate_token_func is not None:
         
     | 
| 
       672 
     | 
    
         
            -
                         
     | 
| 
      
 699 
     | 
    
         
            +
                        validate_token_func_res = validate_token_func(
         
     | 
| 
       673 
700 
     | 
    
         
             
                            api_key_string=api_auth_data.api_key_string,
         
     | 
| 
       674 
701 
     | 
    
         
             
                            token_string=api_auth_data.token_string,
         
     | 
| 
       675 
702 
     | 
    
         
             
                            base_api_auth_data=api_auth_data,
         
     | 
| 
         @@ -677,11 +704,11 @@ def base_api_auth( 
     | 
|
| 
       677 
704 
     | 
    
         
             
                            request=request,
         
     | 
| 
       678 
705 
     | 
    
         
             
                            **kwargs
         
     | 
| 
       679 
706 
     | 
    
         
             
                        )
         
     | 
| 
       680 
     | 
    
         
            -
                        if is_async_object( 
     | 
| 
       681 
     | 
    
         
            -
                             
     | 
| 
       682 
     | 
    
         
            -
                        api_auth_data.is_token_correct =  
     | 
| 
      
 707 
     | 
    
         
            +
                        if is_async_object(validate_token_func_res):
         
     | 
| 
      
 708 
     | 
    
         
            +
                            validate_token_func_res = await validate_token_func_res
         
     | 
| 
      
 709 
     | 
    
         
            +
                        api_auth_data.is_token_correct = validate_token_func_res
         
     | 
| 
       683 
710 
     | 
    
         | 
| 
       684 
     | 
    
         
            -
                    #  
     | 
| 
      
 711 
     | 
    
         
            +
                    # require_correct_api_key
         
     | 
| 
       685 
712 
     | 
    
         | 
| 
       686 
713 
     | 
    
         
             
                    if require_correct_api_key:
         
     | 
| 
       687 
714 
     | 
    
         
             
                        if not api_auth_data.is_api_key_correct:
         
     | 
| 
         @@ -692,7 +719,7 @@ def base_api_auth( 
     | 
|
| 
       692 
719 
     | 
    
         
             
                                error_data=safely_transfer_obj_to_json_str_to_json_obj(api_auth_data.model_dump()),
         
     | 
| 
       693 
720 
     | 
    
         
             
                            )
         
     | 
| 
       694 
721 
     | 
    
         | 
| 
       695 
     | 
    
         
            -
                    #  
     | 
| 
      
 722 
     | 
    
         
            +
                    # require_correct_token
         
     | 
| 
       696 
723 
     | 
    
         | 
| 
       697 
724 
     | 
    
         
             
                    if require_correct_token:
         
     | 
| 
       698 
725 
     | 
    
         
             
                        if not api_auth_data.is_token_correct:
         
     | 
| 
         @@ -750,7 +777,6 @@ def create_fastapi_app( 
     | 
|
| 
       750 
777 
     | 
    
         
             
                    *,
         
     | 
| 
       751 
778 
     | 
    
         
             
                    title: str = "arpakitlib FastAPI",
         
     | 
| 
       752 
779 
     | 
    
         
             
                    description: str | None = "arpakitlib FastAPI",
         
     | 
| 
       753 
     | 
    
         
            -
                    log_filepath: str | None = "./story.log",
         
     | 
| 
       754 
780 
     | 
    
         
             
                    handle_exception_: Callable | None = None,
         
     | 
| 
       755 
781 
     | 
    
         
             
                    startup_api_events: list[BaseStartupAPIEvent | None] | None = None,
         
     | 
| 
       756 
782 
     | 
    
         
             
                    shutdown_api_events: list[BaseShutdownAPIEvent | None] | None = None,
         
     | 
| 
         @@ -760,12 +786,14 @@ def create_fastapi_app( 
     | 
|
| 
       760 
786 
     | 
    
         
             
                    media_dirpath: str | None = None,
         
     | 
| 
       761 
787 
     | 
    
         
             
                    static_dirpath: str | None = None
         
     | 
| 
       762 
788 
     | 
    
         
             
            ):
         
     | 
| 
       763 
     | 
    
         
            -
                _logger.info("start 
     | 
| 
       764 
     | 
    
         
            -
             
     | 
| 
       765 
     | 
    
         
            -
                setup_normal_logging(log_filepath=log_filepath)
         
     | 
| 
      
 789 
     | 
    
         
            +
                _logger.info("start")
         
     | 
| 
       766 
790 
     | 
    
         | 
| 
       767 
791 
     | 
    
         
             
                if handle_exception_ is None:
         
     | 
| 
       768 
     | 
    
         
            -
                    handle_exception_ = create_handle_exception( 
     | 
| 
      
 792 
     | 
    
         
            +
                    handle_exception_ = create_handle_exception(
         
     | 
| 
      
 793 
     | 
    
         
            +
                        funcs_before=[
         
     | 
| 
      
 794 
     | 
    
         
            +
                            logging__api_func_before_in_handle_exception()
         
     | 
| 
      
 795 
     | 
    
         
            +
                        ]
         
     | 
| 
      
 796 
     | 
    
         
            +
                    )
         
     | 
| 
       769 
797 
     | 
    
         | 
| 
       770 
798 
     | 
    
         
             
                if not startup_api_events:
         
     | 
| 
       771 
799 
     | 
    
         
             
                    startup_api_events = [BaseStartupAPIEvent()]
         
     | 
| 
         @@ -807,11 +835,14 @@ def create_fastapi_app( 
     | 
|
| 
       807 
835 
     | 
    
         
             
                    handle_exception=handle_exception_
         
     | 
| 
       808 
836 
     | 
    
         
             
                )
         
     | 
| 
       809 
837 
     | 
    
         | 
| 
       810 
     | 
    
         
            -
                 
     | 
| 
      
 838 
     | 
    
         
            +
                app.include_router(
         
     | 
| 
      
 839 
     | 
    
         
            +
                    router=create_needed_api_router(),
         
     | 
| 
      
 840 
     | 
    
         
            +
                    prefix=""
         
     | 
| 
      
 841 
     | 
    
         
            +
                )
         
     | 
| 
       811 
842 
     | 
    
         | 
| 
       812 
843 
     | 
    
         
             
                app.include_router(router=main_api_router)
         
     | 
| 
       813 
844 
     | 
    
         | 
| 
       814 
     | 
    
         
            -
                _logger.info("finish 
     | 
| 
      
 845 
     | 
    
         
            +
                _logger.info("finish")
         
     | 
| 
       815 
846 
     | 
    
         | 
| 
       816 
847 
     | 
    
         
             
                return app
         
     | 
| 
       817 
848 
     | 
    
         | 
| 
         @@ -43,6 +43,30 @@ class ScheduleUUSTAPIClient: 
     | 
|
| 
       43 
43 
     | 
    
         
             
                        )
         
     | 
| 
       44 
44 
     | 
    
         
             
                    }
         
     | 
| 
       45 
45 
     | 
    
         | 
| 
      
 46 
     | 
    
         
            +
                async def _async_make_http_request(
         
     | 
| 
      
 47 
     | 
    
         
            +
                        self,
         
     | 
| 
      
 48 
     | 
    
         
            +
                        *,
         
     | 
| 
      
 49 
     | 
    
         
            +
                        method: str = "GET",
         
     | 
| 
      
 50 
     | 
    
         
            +
                        url: str,
         
     | 
| 
      
 51 
     | 
    
         
            +
                        params: dict[str, Any] | None = None,
         
     | 
| 
      
 52 
     | 
    
         
            +
                        **kwargs
         
     | 
| 
      
 53 
     | 
    
         
            +
                ) -> ClientResponse:
         
     | 
| 
      
 54 
     | 
    
         
            +
                    response = await async_make_http_request(
         
     | 
| 
      
 55 
     | 
    
         
            +
                        method=method,
         
     | 
| 
      
 56 
     | 
    
         
            +
                        url=url,
         
     | 
| 
      
 57 
     | 
    
         
            +
                        headers=self.headers,
         
     | 
| 
      
 58 
     | 
    
         
            +
                        params=combine_dicts(params, self.auth_params()),
         
     | 
| 
      
 59 
     | 
    
         
            +
                        max_tries_=9,
         
     | 
| 
      
 60 
     | 
    
         
            +
                        proxy_url_=self.api_proxy_url,
         
     | 
| 
      
 61 
     | 
    
         
            +
                        raise_for_status_=True,
         
     | 
| 
      
 62 
     | 
    
         
            +
                        timeout_=timedelta(seconds=15),
         
     | 
| 
      
 63 
     | 
    
         
            +
                        **kwargs
         
     | 
| 
      
 64 
     | 
    
         
            +
                    )
         
     | 
| 
      
 65 
     | 
    
         
            +
                    json_data = await response.json()
         
     | 
| 
      
 66 
     | 
    
         
            +
                    if "error" in json_data.keys():
         
     | 
| 
      
 67 
     | 
    
         
            +
                        raise Exception(f"error in json_data, {json_data}")
         
     | 
| 
      
 68 
     | 
    
         
            +
                    return response
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
       46 
70 
     | 
    
         
             
                def auth_params(self) -> dict[str, Any]:
         
     | 
| 
       47 
71 
     | 
    
         
             
                    if self.api_password:
         
     | 
| 
       48 
72 
     | 
    
         
             
                        return {
         
     | 
| 
         @@ -72,30 +96,6 @@ class ScheduleUUSTAPIClient: 
     | 
|
| 
       72 
96 
     | 
    
         
             
                def generate_v2_token(self) -> str:
         
     | 
| 
       73 
97 
     | 
    
         
             
                    return self.generate_new_v2_token(password_first_part=self.api_password_first_part)
         
     | 
| 
       74 
98 
     | 
    
         | 
| 
       75 
     | 
    
         
            -
                async def _async_make_http_request(
         
     | 
| 
       76 
     | 
    
         
            -
                        self,
         
     | 
| 
       77 
     | 
    
         
            -
                        *,
         
     | 
| 
       78 
     | 
    
         
            -
                        method: str = "GET",
         
     | 
| 
       79 
     | 
    
         
            -
                        url: str,
         
     | 
| 
       80 
     | 
    
         
            -
                        params: dict[str, Any] | None = None,
         
     | 
| 
       81 
     | 
    
         
            -
                        **kwargs
         
     | 
| 
       82 
     | 
    
         
            -
                ) -> ClientResponse:
         
     | 
| 
       83 
     | 
    
         
            -
                    response = await async_make_http_request(
         
     | 
| 
       84 
     | 
    
         
            -
                        method=method,
         
     | 
| 
       85 
     | 
    
         
            -
                        url=url,
         
     | 
| 
       86 
     | 
    
         
            -
                        headers=self.headers,
         
     | 
| 
       87 
     | 
    
         
            -
                        params=combine_dicts(params, self.auth_params()),
         
     | 
| 
       88 
     | 
    
         
            -
                        max_tries_=9,
         
     | 
| 
       89 
     | 
    
         
            -
                        proxy_url_=self.api_proxy_url,
         
     | 
| 
       90 
     | 
    
         
            -
                        raise_for_status_=True,
         
     | 
| 
       91 
     | 
    
         
            -
                        timeout_=timedelta(seconds=15),
         
     | 
| 
       92 
     | 
    
         
            -
                        **kwargs
         
     | 
| 
       93 
     | 
    
         
            -
                    )
         
     | 
| 
       94 
     | 
    
         
            -
                    json_data = await response.json()
         
     | 
| 
       95 
     | 
    
         
            -
                    if "error" in json_data.keys():
         
     | 
| 
       96 
     | 
    
         
            -
                        raise Exception(f"error in json_data, {json_data}")
         
     | 
| 
       97 
     | 
    
         
            -
                    return response
         
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
99 
     | 
    
         
             
                async def get_current_week(self) -> int:
         
     | 
| 
       100 
100 
     | 
    
         
             
                    """
         
     | 
| 
       101 
101 
     | 
    
         
             
                    response.json example
         
     | 
    
        arpakitlib/ar_settings_util.py
    CHANGED
    
    | 
         @@ -1,12 +1,16 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # arpakit
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
      
 2 
     | 
    
         
            +
            import os
         
     | 
| 
       3 
3 
     | 
    
         
             
            from typing import Union, Any
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
      
 5 
     | 
    
         
            +
            import pytz
         
     | 
| 
       5 
6 
     | 
    
         
             
            from pydantic import ConfigDict, field_validator, model_validator
         
     | 
| 
       6 
7 
     | 
    
         
             
            from pydantic_core import PydanticUndefined
         
     | 
| 
      
 8 
     | 
    
         
            +
            from pydantic_core.core_schema import ValidationInfo
         
     | 
| 
       7 
9 
     | 
    
         
             
            from pydantic_settings import BaseSettings
         
     | 
| 
       8 
10 
     | 
    
         | 
| 
       9 
11 
     | 
    
         
             
            from arpakitlib.ar_enumeration_util import Enumeration
         
     | 
| 
      
 12 
     | 
    
         
            +
            from arpakitlib.ar_json_util import safely_transfer_obj_to_json_str
         
     | 
| 
      
 13 
     | 
    
         
            +
            from arpakitlib.ar_sqlalchemy_util import generate_sqlalchemy_url
         
     | 
| 
       10 
14 
     | 
    
         | 
| 
       11 
15 
     | 
    
         
             
            _ARPAKIT_LIB_MODULE_VERSION = "3.0"
         
     | 
| 
       12 
16 
     | 
    
         | 
| 
         @@ -26,11 +30,9 @@ class ModeTypes(Enumeration): 
     | 
|
| 
       26 
30 
     | 
    
         
             
                prod: str = "prod"
         
     | 
| 
       27 
31 
     | 
    
         | 
| 
       28 
32 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
            class  
     | 
| 
      
 33 
     | 
    
         
            +
            class BaseSettings2(BaseSettings):
         
     | 
| 
       30 
34 
     | 
    
         
             
                model_config = ConfigDict(extra="ignore")
         
     | 
| 
       31 
35 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
                mode_type: str = ModeTypes.not_prod
         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
36 
     | 
    
         
             
                @model_validator(mode="before")
         
     | 
| 
       35 
37 
     | 
    
         
             
                @classmethod
         
     | 
| 
       36 
38 
     | 
    
         
             
                def validate_all_fields(cls, values: dict[str, Any]) -> dict[str, Any]:
         
     | 
| 
         @@ -39,6 +41,21 @@ class SimpleSettings(BaseSettings): 
     | 
|
| 
       39 
41 
     | 
    
         
             
                            values[key] = None
         
     | 
| 
       40 
42 
     | 
    
         
             
                    return values
         
     | 
| 
       41 
43 
     | 
    
         | 
| 
      
 44 
     | 
    
         
            +
                @classmethod
         
     | 
| 
      
 45 
     | 
    
         
            +
                def generate_env_example(cls) -> str:
         
     | 
| 
      
 46 
     | 
    
         
            +
                    return generate_env_example(settings_class=cls)
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                @classmethod
         
     | 
| 
      
 49 
     | 
    
         
            +
                def save_env_example_to_file(cls, filepath: str) -> str:
         
     | 
| 
      
 50 
     | 
    
         
            +
                    env_example = cls.generate_env_example()
         
     | 
| 
      
 51 
     | 
    
         
            +
                    with open(filepath, mode="w") as f:
         
     | 
| 
      
 52 
     | 
    
         
            +
                        f.write(env_example)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    return env_example
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            class SimpleSettings(BaseSettings2):
         
     | 
| 
      
 57 
     | 
    
         
            +
                mode_type: str = ModeTypes.not_prod
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
       42 
59 
     | 
    
         
             
                @field_validator("mode_type")
         
     | 
| 
       43 
60 
     | 
    
         
             
                @classmethod
         
     | 
| 
       44 
61 
     | 
    
         
             
                def validate_mode_type(cls, v: str):
         
     | 
| 
         @@ -61,20 +78,155 @@ class SimpleSettings(BaseSettings): 
     | 
|
| 
       61 
78 
     | 
    
         
             
                    if self.is_mode_type_prod:
         
     | 
| 
       62 
79 
     | 
    
         
             
                        raise ValueError(f"mode type = {self.mode_type}")
         
     | 
| 
       63 
80 
     | 
    
         | 
| 
       64 
     | 
    
         
            -
                @classmethod
         
     | 
| 
       65 
     | 
    
         
            -
                def generate_env_example(cls) -> str:
         
     | 
| 
       66 
     | 
    
         
            -
                    return generate_env_example(settings_class=cls)
         
     | 
| 
       67 
81 
     | 
    
         | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
                 
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
      
 82 
     | 
    
         
            +
            class AdvancedSettings(SimpleSettings):
         
     | 
| 
      
 83 
     | 
    
         
            +
                project_name: str | None = None
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                sql_db_user: str | None = None
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                @field_validator("sql_db_user", mode="after")
         
     | 
| 
      
 88 
     | 
    
         
            +
                def validate_sql_db_user(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
         
     | 
| 
      
 89 
     | 
    
         
            +
                    if v is not None:
         
     | 
| 
      
 90 
     | 
    
         
            +
                        return v
         
     | 
| 
      
 91 
     | 
    
         
            +
                    res = validation_info.data.get("project_name")
         
     | 
| 
      
 92 
     | 
    
         
            +
                    if res is not None:
         
     | 
| 
      
 93 
     | 
    
         
            +
                        return res
         
     | 
| 
      
 94 
     | 
    
         
            +
                    return res
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                sql_db_password: str | None = None
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                @field_validator("sql_db_password", mode="after")
         
     | 
| 
      
 99 
     | 
    
         
            +
                def validate_sql_db_password(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
         
     | 
| 
      
 100 
     | 
    
         
            +
                    if v is not None:
         
     | 
| 
      
 101 
     | 
    
         
            +
                        return v
         
     | 
| 
      
 102 
     | 
    
         
            +
                    res = validation_info.data.get("project_name")
         
     | 
| 
      
 103 
     | 
    
         
            +
                    if res is not None:
         
     | 
| 
      
 104 
     | 
    
         
            +
                        return res
         
     | 
| 
      
 105 
     | 
    
         
            +
                    return res
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                sql_db_port: int | None = int("50506") if "50506".strip().isdigit() else None
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                sql_db_database: str | None = None
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                @field_validator("sql_db_database", mode="after")
         
     | 
| 
      
 112 
     | 
    
         
            +
                def validate_sql_db_database(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
         
     | 
| 
      
 113 
     | 
    
         
            +
                    if v is not None:
         
     | 
| 
      
 114 
     | 
    
         
            +
                        return v
         
     | 
| 
      
 115 
     | 
    
         
            +
                    res = validation_info.data.get("project_name")
         
     | 
| 
      
 116 
     | 
    
         
            +
                    if res is not None:
         
     | 
| 
      
 117 
     | 
    
         
            +
                        return res
         
     | 
| 
      
 118 
     | 
    
         
            +
                    return res
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                sync_sql_db_url: str | None = None
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                @field_validator("sync_sql_db_url", mode="after")
         
     | 
| 
      
 123 
     | 
    
         
            +
                def validate_sync_sql_db_url(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
         
     | 
| 
      
 124 
     | 
    
         
            +
                    if v is not None:
         
     | 
| 
      
 125 
     | 
    
         
            +
                        return v
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                    return generate_sqlalchemy_url(
         
     | 
| 
      
 128 
     | 
    
         
            +
                        base="postgresql+asyncpg",
         
     | 
| 
      
 129 
     | 
    
         
            +
                        user=validation_info.data.get("sql_db_user"),
         
     | 
| 
      
 130 
     | 
    
         
            +
                        password=validation_info.data.get("sql_db_password"),
         
     | 
| 
      
 131 
     | 
    
         
            +
                        port=validation_info.data.get("sql_db_port"),
         
     | 
| 
      
 132 
     | 
    
         
            +
                        database=validation_info.data.get("sql_db_database")
         
     | 
| 
      
 133 
     | 
    
         
            +
                    )
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                async_sql_db_url: str | None = None
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                @field_validator("async_sql_db_url", mode="after")
         
     | 
| 
      
 138 
     | 
    
         
            +
                def validate_async_sql_db_url(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
         
     | 
| 
      
 139 
     | 
    
         
            +
                    if v is not None:
         
     | 
| 
      
 140 
     | 
    
         
            +
                        return v
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                    return generate_sqlalchemy_url(
         
     | 
| 
      
 143 
     | 
    
         
            +
                        base="postgresql+asyncpg",
         
     | 
| 
      
 144 
     | 
    
         
            +
                        user=validation_info.data.get("sql_db_user"),
         
     | 
| 
      
 145 
     | 
    
         
            +
                        password=validation_info.data.get("sql_db_password"),
         
     | 
| 
      
 146 
     | 
    
         
            +
                        port=validation_info.data.get("sql_db_port"),
         
     | 
| 
      
 147 
     | 
    
         
            +
                        database=validation_info.data.get("sql_db_database")
         
     | 
| 
      
 148 
     | 
    
         
            +
                    )
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                sql_db_echo: bool = False
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                api_port: int | None = None
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                @field_validator("api_port", mode="before")
         
     | 
| 
      
 155 
     | 
    
         
            +
                def validate_api_port(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> int | None:
         
     | 
| 
      
 156 
     | 
    
         
            +
                    if isinstance(v, str):
         
     | 
| 
      
 157 
     | 
    
         
            +
                        if v.isdigit():
         
     | 
| 
      
 158 
     | 
    
         
            +
                            return int(v)
         
     | 
| 
      
 159 
     | 
    
         
            +
                    return None
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                api_init_sql_db_at_start: bool = True
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                api_logging__api_func_before_in_handle_exception: bool = True
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                api_story_log__api_func_before_in_handle_exception: bool = True
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                api_correct_api_key: str | None = "1"
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                api_correct_token: str | None = "1"
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                api_enable_admin1: bool = True
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
                admin1_secret_key: str | None = "85a9583cb91c4de7a78d7eb1e5306a04418c9c43014c447ea8ec8dd5deb4cf71"
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                var_dirpath: str | None = "./var"
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                log_filepath: str | None = None
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
                @field_validator("log_filepath", mode="after")
         
     | 
| 
      
 180 
     | 
    
         
            +
                def validate_log_filepath(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
         
     | 
| 
      
 181 
     | 
    
         
            +
                    if v is not None:
         
     | 
| 
      
 182 
     | 
    
         
            +
                        return v
         
     | 
| 
      
 183 
     | 
    
         
            +
                    var_dirpath = validation_info.data.get("var_dirpath")
         
     | 
| 
      
 184 
     | 
    
         
            +
                    if var_dirpath is None:
         
     | 
| 
      
 185 
     | 
    
         
            +
                        return None
         
     | 
| 
      
 186 
     | 
    
         
            +
                    return os.path.join(var_dirpath, "story.log")
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
                cache_dirpath: str | None = None
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
      
 190 
     | 
    
         
            +
                @field_validator("cache_dirpath", mode="after")
         
     | 
| 
      
 191 
     | 
    
         
            +
                def validate_cache_dirpath(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
         
     | 
| 
      
 192 
     | 
    
         
            +
                    if v is not None:
         
     | 
| 
      
 193 
     | 
    
         
            +
                        return v
         
     | 
| 
      
 194 
     | 
    
         
            +
                    var_dirpath = validation_info.data.get("var_dirpath")
         
     | 
| 
      
 195 
     | 
    
         
            +
                    if var_dirpath is None:
         
     | 
| 
      
 196 
     | 
    
         
            +
                        return None
         
     | 
| 
      
 197 
     | 
    
         
            +
                    return os.path.join(var_dirpath, "cache")
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
                media_dirpath: str | None = None
         
     | 
| 
      
 200 
     | 
    
         
            +
             
     | 
| 
      
 201 
     | 
    
         
            +
                @field_validator("media_dirpath", mode="after")
         
     | 
| 
      
 202 
     | 
    
         
            +
                def validate_media_dirpath(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
         
     | 
| 
      
 203 
     | 
    
         
            +
                    if v is not None:
         
     | 
| 
      
 204 
     | 
    
         
            +
                        return v
         
     | 
| 
      
 205 
     | 
    
         
            +
                    var_dirpath = validation_info.data.get("var_dirpath")
         
     | 
| 
      
 206 
     | 
    
         
            +
                    if var_dirpath is None:
         
     | 
| 
      
 207 
     | 
    
         
            +
                        return None
         
     | 
| 
      
 208 
     | 
    
         
            +
                    return os.path.join(var_dirpath, "media")
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
                dump_dirpath: str | None = None
         
     | 
| 
      
 211 
     | 
    
         
            +
             
     | 
| 
      
 212 
     | 
    
         
            +
                @field_validator("dump_dirpath", mode="after")
         
     | 
| 
      
 213 
     | 
    
         
            +
                def validate_dump_dirpath(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
         
     | 
| 
      
 214 
     | 
    
         
            +
                    if v is not None:
         
     | 
| 
      
 215 
     | 
    
         
            +
                        return v
         
     | 
| 
      
 216 
     | 
    
         
            +
                    var_dirpath = validation_info.data.get("var_dirpath")
         
     | 
| 
      
 217 
     | 
    
         
            +
                    if var_dirpath is None:
         
     | 
| 
      
 218 
     | 
    
         
            +
                        return None
         
     | 
| 
      
 219 
     | 
    
         
            +
                    return os.path.join(var_dirpath, "dump")
         
     | 
| 
      
 220 
     | 
    
         
            +
             
     | 
| 
      
 221 
     | 
    
         
            +
                local_timezone: str | None = None
         
     | 
| 
      
 222 
     | 
    
         
            +
             
     | 
| 
      
 223 
     | 
    
         
            +
                @property
         
     | 
| 
      
 224 
     | 
    
         
            +
                def local_timezone_as_pytz(self) -> Any:
         
     | 
| 
      
 225 
     | 
    
         
            +
                    return pytz.timezone(self.local_timezone)
         
     | 
| 
       74 
226 
     | 
    
         | 
| 
       75 
227 
     | 
    
         | 
| 
       76 
228 
     | 
    
         
             
            def __example():
         
     | 
| 
       77 
     | 
    
         
            -
                 
     | 
| 
      
 229 
     | 
    
         
            +
                print(safely_transfer_obj_to_json_str(AdvancedSettings(var_dirpath="./var").model_dump(mode="json")))
         
     | 
| 
       78 
230 
     | 
    
         | 
| 
       79 
231 
     | 
    
         | 
| 
       80 
232 
     | 
    
         
             
            if __name__ == '__main__':
         
     | 
| 
         @@ -67,7 +67,7 @@ class BaseDBM(DeclarativeBase): 
     | 
|
| 
       67 
67 
     | 
    
         | 
| 
       68 
68 
     | 
    
         
             
                    return res
         
     | 
| 
       69 
69 
     | 
    
         | 
| 
       70 
     | 
    
         
            -
                def simple_dict_with_sd_properties(self) -> dict[str  
     | 
| 
      
 70 
     | 
    
         
            +
                def simple_dict_with_sd_properties(self) -> dict[str, Any]:
         
     | 
| 
       71 
71 
     | 
    
         
             
                    return self.simple_dict(include_sd_properties=True)
         
     | 
| 
       72 
72 
     | 
    
         | 
| 
       73 
73 
     | 
    
         
             
                def simple_json(self, *, include_sd_properties: bool = True) -> str:
         
     |