squirrels 0.4.0__py3-none-any.whl → 0.5.0__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.

Potentially problematic release.


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

Files changed (125) hide show
  1. dateutils/__init__.py +6 -0
  2. dateutils/_enums.py +25 -0
  3. squirrels/dateutils.py → dateutils/_implementation.py +58 -111
  4. dateutils/types.py +6 -0
  5. squirrels/__init__.py +13 -11
  6. squirrels/_api_routes/__init__.py +5 -0
  7. squirrels/_api_routes/auth.py +271 -0
  8. squirrels/_api_routes/base.py +165 -0
  9. squirrels/_api_routes/dashboards.py +150 -0
  10. squirrels/_api_routes/data_management.py +145 -0
  11. squirrels/_api_routes/datasets.py +257 -0
  12. squirrels/_api_routes/oauth2.py +298 -0
  13. squirrels/_api_routes/project.py +252 -0
  14. squirrels/_api_server.py +256 -450
  15. squirrels/_arguments/__init__.py +0 -0
  16. squirrels/_arguments/init_time_args.py +108 -0
  17. squirrels/_arguments/run_time_args.py +147 -0
  18. squirrels/_auth.py +960 -0
  19. squirrels/_command_line.py +126 -45
  20. squirrels/_compile_prompts.py +147 -0
  21. squirrels/_connection_set.py +48 -26
  22. squirrels/_constants.py +68 -38
  23. squirrels/_dashboards.py +160 -0
  24. squirrels/_data_sources.py +570 -0
  25. squirrels/_dataset_types.py +84 -0
  26. squirrels/_exceptions.py +29 -0
  27. squirrels/_initializer.py +177 -80
  28. squirrels/_logging.py +115 -0
  29. squirrels/_manifest.py +208 -79
  30. squirrels/_model_builder.py +69 -0
  31. squirrels/_model_configs.py +74 -0
  32. squirrels/_model_queries.py +52 -0
  33. squirrels/_models.py +926 -367
  34. squirrels/_package_data/base_project/.env +42 -0
  35. squirrels/_package_data/base_project/.env.example +42 -0
  36. squirrels/_package_data/base_project/assets/expenses.db +0 -0
  37. squirrels/_package_data/base_project/connections.yml +16 -0
  38. squirrels/_package_data/base_project/dashboards/dashboard_example.py +34 -0
  39. squirrels/_package_data/base_project/dashboards/dashboard_example.yml +22 -0
  40. squirrels/{package_data → _package_data}/base_project/docker/.dockerignore +5 -2
  41. squirrels/{package_data → _package_data}/base_project/docker/Dockerfile +3 -3
  42. squirrels/{package_data → _package_data}/base_project/docker/compose.yml +1 -1
  43. squirrels/_package_data/base_project/duckdb_init.sql +10 -0
  44. squirrels/{package_data/base_project/.gitignore → _package_data/base_project/gitignore} +3 -2
  45. squirrels/_package_data/base_project/macros/macros_example.sql +17 -0
  46. squirrels/_package_data/base_project/models/builds/build_example.py +26 -0
  47. squirrels/_package_data/base_project/models/builds/build_example.sql +16 -0
  48. squirrels/_package_data/base_project/models/builds/build_example.yml +57 -0
  49. squirrels/_package_data/base_project/models/dbviews/dbview_example.sql +12 -0
  50. squirrels/_package_data/base_project/models/dbviews/dbview_example.yml +26 -0
  51. squirrels/_package_data/base_project/models/federates/federate_example.py +37 -0
  52. squirrels/_package_data/base_project/models/federates/federate_example.sql +19 -0
  53. squirrels/_package_data/base_project/models/federates/federate_example.yml +65 -0
  54. squirrels/_package_data/base_project/models/sources.yml +38 -0
  55. squirrels/{package_data → _package_data}/base_project/parameters.yml +56 -40
  56. squirrels/_package_data/base_project/pyconfigs/connections.py +14 -0
  57. squirrels/{package_data → _package_data}/base_project/pyconfigs/context.py +21 -40
  58. squirrels/_package_data/base_project/pyconfigs/parameters.py +141 -0
  59. squirrels/_package_data/base_project/pyconfigs/user.py +44 -0
  60. squirrels/_package_data/base_project/seeds/seed_categories.yml +15 -0
  61. squirrels/_package_data/base_project/seeds/seed_subcategories.csv +15 -0
  62. squirrels/_package_data/base_project/seeds/seed_subcategories.yml +21 -0
  63. squirrels/_package_data/base_project/squirrels.yml.j2 +61 -0
  64. squirrels/_package_data/templates/dataset_results.html +112 -0
  65. squirrels/_package_data/templates/oauth_login.html +271 -0
  66. squirrels/_package_data/templates/squirrels_studio.html +20 -0
  67. squirrels/_package_loader.py +8 -4
  68. squirrels/_parameter_configs.py +104 -103
  69. squirrels/_parameter_options.py +348 -0
  70. squirrels/_parameter_sets.py +57 -47
  71. squirrels/_parameters.py +1664 -0
  72. squirrels/_project.py +721 -0
  73. squirrels/_py_module.py +7 -5
  74. squirrels/_schemas/__init__.py +0 -0
  75. squirrels/_schemas/auth_models.py +167 -0
  76. squirrels/_schemas/query_param_models.py +75 -0
  77. squirrels/{_api_response_models.py → _schemas/response_models.py} +126 -47
  78. squirrels/_seeds.py +35 -16
  79. squirrels/_sources.py +110 -0
  80. squirrels/_utils.py +248 -73
  81. squirrels/_version.py +1 -1
  82. squirrels/arguments.py +7 -0
  83. squirrels/auth.py +4 -0
  84. squirrels/connections.py +3 -0
  85. squirrels/dashboards.py +2 -81
  86. squirrels/data_sources.py +14 -631
  87. squirrels/parameter_options.py +13 -348
  88. squirrels/parameters.py +14 -1266
  89. squirrels/types.py +16 -0
  90. squirrels-0.5.0.dist-info/METADATA +113 -0
  91. squirrels-0.5.0.dist-info/RECORD +97 -0
  92. {squirrels-0.4.0.dist-info → squirrels-0.5.0.dist-info}/WHEEL +1 -1
  93. squirrels-0.5.0.dist-info/entry_points.txt +3 -0
  94. {squirrels-0.4.0.dist-info → squirrels-0.5.0.dist-info/licenses}/LICENSE +1 -1
  95. squirrels/_authenticator.py +0 -85
  96. squirrels/_dashboards_io.py +0 -61
  97. squirrels/_environcfg.py +0 -84
  98. squirrels/arguments/init_time_args.py +0 -40
  99. squirrels/arguments/run_time_args.py +0 -208
  100. squirrels/package_data/assets/favicon.ico +0 -0
  101. squirrels/package_data/assets/index.css +0 -1
  102. squirrels/package_data/assets/index.js +0 -58
  103. squirrels/package_data/base_project/assets/expenses.db +0 -0
  104. squirrels/package_data/base_project/connections.yml +0 -7
  105. squirrels/package_data/base_project/dashboards/dashboard_example.py +0 -32
  106. squirrels/package_data/base_project/dashboards.yml +0 -10
  107. squirrels/package_data/base_project/env.yml +0 -29
  108. squirrels/package_data/base_project/models/dbviews/dbview_example.py +0 -47
  109. squirrels/package_data/base_project/models/dbviews/dbview_example.sql +0 -22
  110. squirrels/package_data/base_project/models/federates/federate_example.py +0 -21
  111. squirrels/package_data/base_project/models/federates/federate_example.sql +0 -3
  112. squirrels/package_data/base_project/pyconfigs/auth.py +0 -45
  113. squirrels/package_data/base_project/pyconfigs/connections.py +0 -19
  114. squirrels/package_data/base_project/pyconfigs/parameters.py +0 -95
  115. squirrels/package_data/base_project/seeds/seed_subcategories.csv +0 -15
  116. squirrels/package_data/base_project/squirrels.yml.j2 +0 -94
  117. squirrels/package_data/templates/index.html +0 -18
  118. squirrels/project.py +0 -378
  119. squirrels/user_base.py +0 -55
  120. squirrels-0.4.0.dist-info/METADATA +0 -117
  121. squirrels-0.4.0.dist-info/RECORD +0 -60
  122. squirrels-0.4.0.dist-info/entry_points.txt +0 -4
  123. /squirrels/{package_data → _package_data}/base_project/assets/weather.db +0 -0
  124. /squirrels/{package_data → _package_data}/base_project/seeds/seed_categories.csv +0 -0
  125. /squirrels/{package_data → _package_data}/base_project/tmp/.gitignore +0 -0
squirrels/_constants.py CHANGED
@@ -1,35 +1,78 @@
1
1
  # Squirrels CLI commands
2
- INIT_CMD = 'init'
2
+ INIT_CMD = 'new'
3
3
  GET_FILE_CMD = 'get-file'
4
4
  DEPS_CMD = 'deps'
5
+ BUILD_CMD = 'build'
5
6
  COMPILE_CMD = 'compile'
6
7
  RUN_CMD = 'run'
8
+ DUCKDB_CMD = 'duckdb'
7
9
 
8
- # Environment config keys
9
- JWT_SECRET_KEY = 'jwt_secret'
10
+ # Environment variables
11
+ SQRL_SECRET_KEY = 'SQRL_SECRET__KEY'
12
+ SQRL_SECRET_ADMIN_PASSWORD = 'SQRL_SECRET__ADMIN_PASSWORD'
13
+
14
+ SQRL_AUTH_DB_FILE_PATH = 'SQRL_AUTH__DB_FILE_PATH'
15
+ SQRL_AUTH_TOKEN_EXPIRE_MINUTES = 'SQRL_AUTH__TOKEN_EXPIRE_MINUTES'
16
+ SQRL_AUTH_CREDENTIAL_ORIGINS = 'SQRL_AUTH__ALLOWED_ORIGINS_FOR_COOKIES'
17
+
18
+ SQRL_PARAMETERS_CACHE_SIZE = 'SQRL_PARAMETERS__CACHE_SIZE'
19
+ SQRL_PARAMETERS_CACHE_TTL_MINUTES = 'SQRL_PARAMETERS__CACHE_TTL_MINUTES'
20
+ SQRL_PARAMETERS_DATASOURCE_REFRESH_MINUTES = 'SQRL_PARAMETERS__DATASOURCE_REFRESH_MINUTES'
21
+
22
+ SQRL_DATASETS_CACHE_SIZE = 'SQRL_DATASETS__CACHE_SIZE'
23
+ SQRL_DATASETS_CACHE_TTL_MINUTES = 'SQRL_DATASETS__CACHE_TTL_MINUTES'
24
+ SQRL_DATASETS_MAX_ROWS_FOR_AI = 'SQRL_DATASETS__MAX_ROWS_FOR_AI'
25
+
26
+ SQRL_DASHBOARDS_CACHE_SIZE = 'SQRL_DASHBOARDS__CACHE_SIZE'
27
+ SQRL_DASHBOARDS_CACHE_TTL_MINUTES = 'SQRL_DASHBOARDS__CACHE_TTL_MINUTES'
28
+
29
+ SQRL_PERMISSIONS_ELEVATED_ACCESS_LEVEL = 'SQRL_PERMISSIONS__ELEVATED_ACCESS_LEVEL'
30
+
31
+ SQRL_SEEDS_INFER_SCHEMA = 'SQRL_SEEDS__INFER_SCHEMA'
32
+ SQRL_SEEDS_NA_VALUES = 'SQRL_SEEDS__NA_VALUES'
33
+
34
+ SQRL_CONNECTIONS_DEFAULT_NAME_USED = 'SQRL_CONNECTIONS__DEFAULT_NAME_USED'
35
+
36
+ SQRL_VDL_CATALOG_DB_PATH = 'SQRL_VDL__CATALOG_DB_PATH'
37
+ SQRL_VDL_DATA_PATH = 'SQRL_VDL__DATA_PATH'
38
+
39
+ SQRL_STUDIO_BASE_URL = 'SQRL_STUDIO__BASE_URL'
40
+
41
+ SQRL_LOGGING_LOG_LEVEL = 'SQRL_LOGGING__LOG_LEVEL'
42
+ SQRL_LOGGING_LOG_FORMAT = 'SQRL_LOGGING__LOG_FORMAT'
43
+ SQRL_LOGGING_LOG_TO_FILE = 'SQRL_LOGGING__LOG_TO_FILE'
44
+ SQRL_LOGGING_LOG_FILE_SIZE_MB = 'SQRL_LOGGING__LOG_FILE_SIZE_MB'
45
+ SQRL_LOGGING_LOG_FILE_BACKUP_COUNT = 'SQRL_LOGGING__LOG_FILE_BACKUP_COUNT'
10
46
 
11
47
  # Folder/File names
12
- PACKAGE_DATA_FOLDER = 'package_data'
48
+ PACKAGE_DATA_FOLDER = '_package_data'
13
49
  BASE_PROJECT_FOLDER = 'base_project'
14
- ASSETS_FOLDER = 'assets'
15
- TEMPLATES_FOLDER = 'templates'
16
50
 
17
- ENV_CONFIG_FILE = 'env.yml'
51
+ GLOBAL_ENV_FOLDER = '.squirrels'
18
52
  MANIFEST_JINJA_FILE = 'squirrels.yml.j2'
19
53
  CONNECTIONS_YML_FILE = 'connections.yml'
20
54
  PARAMETERS_YML_FILE = 'parameters.yml'
21
55
  DASHBOARDS_YML_FILE = 'dashboards.yml'
22
56
  MANIFEST_FILE = 'squirrels.yml'
57
+ DUCKDB_INIT_FILE = 'duckdb_init.sql'
58
+ DOTENV_FILE = '.env'
59
+ DOTENV_LOCAL_FILE = '.env.local'
60
+ GITIGNORE_FILE = '.gitignore'
23
61
 
24
62
  LOGS_FOLDER = 'logs'
25
63
  LOGS_FILE = 'squirrels.log'
26
64
 
27
65
  DATABASE_FOLDER = 'assets'
28
66
  PACKAGES_FOLDER = 'sqrl_packages'
67
+ PUBLIC_FOLDER = 'public'
29
68
 
30
69
  MACROS_FOLDER = 'macros'
70
+ MACROS_FILE = 'macros_example.sql'
31
71
 
32
72
  MODELS_FOLDER = 'models'
73
+ SOURCES_FILE = 'sources.yml'
74
+ BUILDS_FOLDER = 'builds'
75
+ BUILD_FILE_STEM = 'build_example'
33
76
  DBVIEWS_FOLDER = 'dbviews'
34
77
  DBVIEW_FILE_STEM = 'dbview_example'
35
78
  FEDERATES_FOLDER = 'federates'
@@ -43,39 +86,23 @@ AUTH_FILE = 'auth.py'
43
86
  CONNECTIONS_FILE = 'connections.py'
44
87
  CONTEXT_FILE = 'context.py'
45
88
  PARAMETERS_FILE = 'parameters.py'
89
+ USER_FILE = 'user.py'
90
+ ADMIN_USERNAME = 'admin'
46
91
 
47
92
  TARGET_FOLDER = 'target'
48
93
  COMPILE_FOLDER = 'compile'
94
+ COMPILE_BUILDTIME_FOLDER = 'buildtime'
95
+ COMPILE_RUNTIME_FOLDER = 'runtime'
96
+ DB_FILE = 'auth.sqlite'
97
+ DEFAULT_VDL_CATALOG_DB_PATH = 'ducklake:{project_path}/target/vdl_catalog.duckdb'
98
+ DEFAULT_VDL_DATA_PATH = '{project_path}/target/vdl_data/'
49
99
 
50
100
  SEEDS_FOLDER = 'seeds'
51
- CATEGORY_SEED_FILE = 'seed_categories.csv'
52
- SUBCATEGORY_SEED_FILE = 'seed_subcategories.csv'
53
-
54
- # Dataset setting names
55
- AUTH_TOKEN_EXPIRE_SETTING = 'auth.token.expire_minutes'
56
- PARAMETERS_CACHE_SIZE_SETTING = 'parameters.cache.size'
57
- PARAMETERS_CACHE_TTL_SETTING = 'parameters.cache.ttl_minutes'
58
- RESULTS_CACHE_SIZE_SETTING = 'results.cache.size' # deprecated
59
- RESULTS_CACHE_TTL_SETTING = 'results.cache.ttl_minutes' # deprecated
60
- DATASETS_CACHE_SIZE_SETTING = 'datasets.cache.size'
61
- DATASETS_CACHE_TTL_SETTING = 'datasets.cache.ttl_minutes'
62
- DASHBOARDS_CACHE_SIZE_SETTING = 'dashboards.cache.size'
63
- DASHBOARDS_CACHE_TTL_SETTING = 'dashboards.cache.ttl_minutes'
64
- TEST_SET_DEFAULT_USED_SETTING = 'selection_test_sets.default_name_used'
101
+ SEED_CATEGORY_FILE_STEM = 'seed_categories'
102
+ SEED_SUBCATEGORY_FILE_STEM = 'seed_subcategories'
103
+
104
+ # Test sets
65
105
  DEFAULT_TEST_SET_NAME = 'default'
66
- DB_CONN_DEFAULT_USED_SETTING = 'connections.default_name_used'
67
- DEFAULT_DB_CONN = 'default'
68
- DEFAULT_MATERIALIZE_SETTING = 'defaults.federates.materialized'
69
- DEFAULT_MATERIALIZE = 'table'
70
- SEEDS_INFER_SCHEMA_SETTING = 'seeds.infer_schema'
71
- SEEDS_NA_VALUES_SETTING = 'seeds.na_values'
72
- IN_MEMORY_DB_SETTING = 'in_memory_database'
73
- SQLITE = 'sqlite'
74
- DUCKDB = 'duckdb'
75
-
76
- # Selection cfg sections
77
- USER_ATTRIBUTES_SECTION = 'user_attributes'
78
- PARAMETERS_SECTION = 'parameters'
79
106
 
80
107
  # Init Command Choices
81
108
  SQL_FILE_TYPE = 'sql'
@@ -97,10 +124,13 @@ PNG = "png"
97
124
  HTML = "html"
98
125
 
99
126
  # Function names
100
- GET_USER_FUNC = "get_user_if_valid"
101
- DEP_FUNC = "dependencies"
102
127
  MAIN_FUNC = "main"
103
128
 
104
129
  # Regex
105
- date_regex = r"^\d{4}\-\d{2}\-\d{2}$"
106
- color_regex = r"^#[0-9a-fA-F]{6}$"
130
+ DATE_REGEX = r"^\d{4}\-\d{2}\-\d{2}$"
131
+ COLOR_REGEX = r"^#[0-9a-fA-F]{6}$"
132
+
133
+ # OAuth2
134
+ SUPPORTED_SCOPES = ['read']
135
+ SUPPORTED_GRANT_TYPES = ['authorization_code', 'refresh_token']
136
+ SUPPORTED_RESPONSE_TYPES = ['code']
@@ -0,0 +1,160 @@
1
+ from typing import Type, TypeVar, Callable, Coroutine, Any
2
+ from enum import Enum
3
+ from dataclasses import dataclass
4
+ from pydantic import BaseModel, Field
5
+ import matplotlib.figure as figure
6
+ import os, time, io, abc, typing
7
+
8
+ from ._arguments.run_time_args import DashboardArgs
9
+ from ._py_module import PyModule
10
+ from ._manifest import AnalyticsOutputConfig
11
+ from ._exceptions import InvalidInputError, ConfigurationError, FileExecutionError
12
+ from . import _constants as c, _utils as u
13
+
14
+
15
+ class Dashboard(metaclass=abc.ABCMeta):
16
+ """
17
+ Abstract parent class for all Dashboard classes.
18
+ """
19
+
20
+ @property
21
+ @abc.abstractmethod
22
+ def _content(self) -> bytes | str:
23
+ pass
24
+
25
+ @property
26
+ @abc.abstractmethod
27
+ def _format(self) -> str:
28
+ pass
29
+
30
+
31
+ class PngDashboard(Dashboard):
32
+ """
33
+ Instantiate a Dashboard in PNG format from a matplotlib figure or bytes
34
+ """
35
+
36
+ def __init__(self, content: figure.Figure | io.BytesIO | bytes) -> None:
37
+ """
38
+ Constructor for PngDashboard
39
+
40
+ Arguments:
41
+ content: The content of the dashboard as a matplotlib.figure.Figure or bytes
42
+ """
43
+ if isinstance(content, figure.Figure):
44
+ buffer = io.BytesIO()
45
+ content.savefig(buffer, format=c.PNG)
46
+ content = buffer.getvalue()
47
+
48
+ if isinstance(content, io.BytesIO):
49
+ content = content.getvalue()
50
+
51
+ self.__content = content
52
+
53
+ @property
54
+ def _content(self) -> bytes:
55
+ return self.__content
56
+
57
+ @property
58
+ def _format(self) -> typing.Literal['png']:
59
+ return c.PNG
60
+
61
+ def _repr_png_(self):
62
+ return self._content
63
+
64
+
65
+ class HtmlDashboard(Dashboard):
66
+ """
67
+ Instantiate a Dashboard from an HTML string
68
+ """
69
+
70
+ def __init__(self, content: io.StringIO | str) -> None:
71
+ """
72
+ Constructor for HtmlDashboard
73
+
74
+ Arguments:
75
+ content: The content of the dashboard as HTML string
76
+ """
77
+ if isinstance(content, io.StringIO):
78
+ content = content.getvalue()
79
+
80
+ self.__content = content
81
+
82
+ @property
83
+ def _content(self) -> str:
84
+ return self.__content
85
+
86
+ @property
87
+ def _format(self) -> typing.Literal['html']:
88
+ return c.HTML
89
+
90
+ def _repr_html_(self):
91
+ return self._content
92
+
93
+
94
+ T = TypeVar('T', bound=Dashboard)
95
+
96
+
97
+ class DashboardFormat(Enum):
98
+ PNG = "png"
99
+ HTML = "html"
100
+
101
+ class DashboardDependencies(BaseModel):
102
+ name: str
103
+ dataset: str
104
+ fixed_parameters: list[dict[str, str]] = Field(default_factory=list)
105
+
106
+ class DashboardConfig(AnalyticsOutputConfig):
107
+ format: DashboardFormat = Field(default=DashboardFormat.PNG)
108
+ depends_on: list[DashboardDependencies] = Field(default_factory=list)
109
+
110
+
111
+ @dataclass
112
+ class DashboardDefinition:
113
+ dashboard_name: str
114
+ filepath: str
115
+ config: DashboardConfig
116
+
117
+ @property
118
+ def dashboard_func(self) -> Callable[[DashboardArgs], Coroutine[Any, Any, Dashboard]]:
119
+ if not hasattr(self, '_dashboard_func'):
120
+ module = PyModule(self.filepath)
121
+ self._dashboard_func = module.get_func_or_class(c.MAIN_FUNC)
122
+ return self._dashboard_func
123
+
124
+ def get_dashboard_format(self) -> str:
125
+ return self.config.format.value
126
+
127
+ async def get_dashboard(self, args: DashboardArgs, *, dashboard_type: Type[T] = Dashboard) -> T:
128
+ try:
129
+ dashboard = await self.dashboard_func(args)
130
+ assert isinstance(dashboard, dashboard_type), f"Function does not return expected Dashboard type: {dashboard_type}"
131
+ except (InvalidInputError, ConfigurationError, FileExecutionError) as e:
132
+ raise e
133
+ except Exception as e:
134
+ raise FileExecutionError(f'Failed to run "{c.MAIN_FUNC}" function for dashboard "{self.dashboard_name}"', e) from e
135
+
136
+ return dashboard
137
+
138
+
139
+ class DashboardsIO:
140
+
141
+ @classmethod
142
+ def load_files(cls, logger: u.Logger, base_path: str) -> dict[str, DashboardDefinition]:
143
+ start = time.time()
144
+
145
+ dashboards_by_name = {}
146
+ for dp, _, filenames in os.walk(u.Path(base_path, c.DASHBOARDS_FOLDER)):
147
+ for file in filenames:
148
+ filepath = os.path.join(dp, file)
149
+ file_stem, extension = os.path.splitext(file)
150
+ if not extension == '.py':
151
+ continue
152
+
153
+ # Check for corresponding .yml file
154
+ yml_path = os.path.join(dp, file_stem + '.yml')
155
+ config_dict = u.load_yaml_config(yml_path) if os.path.exists(yml_path) else {}
156
+ config = DashboardConfig(name=file_stem, **config_dict)
157
+ dashboards_by_name[file_stem] = DashboardDefinition(file_stem, filepath, config)
158
+
159
+ logger.log_activity_time("loading files for dashboards", start)
160
+ return dashboards_by_name