squirrels 0.3.1__tar.gz → 0.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of squirrels might be problematic. Click here for more details.
- {squirrels-0.3.1 → squirrels-0.3.3}/PKG-INFO +4 -3
- {squirrels-0.3.1 → squirrels-0.3.3}/pyproject.toml +4 -3
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/__init__.py +1 -1
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_api_response_models.py +1 -2
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_api_server.py +3 -7
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_authenticator.py +2 -2
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_connection_set.py +5 -2
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_models.py +11 -11
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_parameter_configs.py +49 -34
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_py_module.py +1 -1
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/arguments/run_time_args.py +4 -3
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/data_sources.py +23 -15
- squirrels-0.3.3/squirrels/package_data/assets/index.css +1 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/assets/index.js +13 -13
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/parameters.yml +16 -16
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/pyconfigs/parameters.py +16 -16
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/parameter_options.py +5 -4
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/parameters.py +179 -62
- squirrels-0.3.1/squirrels/package_data/assets/index.css +0 -1
- {squirrels-0.3.1 → squirrels-0.3.3}/LICENSE +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/README.md +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_command_line.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_constants.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_environcfg.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_initializer.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_manifest.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_package_loader.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_parameter_sets.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_seeds.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_timer.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_utils.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_version.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/arguments/init_time_args.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/dateutils.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/assets/favicon.ico +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/.gitignore +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/assets/expenses.db +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/assets/weather.db +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/connections.yml +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/docker/.dockerignore +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/docker/Dockerfile +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/docker/compose.yml +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/env.yml +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/models/dbviews/database_view1.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/models/dbviews/database_view1.sql +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/models/federates/dataset_example.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/models/federates/dataset_example.sql +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/pyconfigs/auth.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/pyconfigs/connections.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/pyconfigs/context.py +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/seeds/seed_categories.csv +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/seeds/seed_subcategories.csv +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/squirrels.yml.j2 +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/tmp/.gitignore +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/templates/index.html +0 -0
- {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/user_base.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: squirrels
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Squirrels - API Framework for Data Analytics
|
|
5
5
|
Home-page: https://squirrels-analytics.github.io
|
|
6
6
|
License: Apache-2.0
|
|
@@ -20,7 +20,7 @@ Classifier: Typing :: Typed
|
|
|
20
20
|
Provides-Extra: duckdb
|
|
21
21
|
Requires-Dist: cachetools (>=5.3.2,<6.0.0)
|
|
22
22
|
Requires-Dist: duckdb-engine (>=0.13.0,<1.0.0) ; extra == "duckdb"
|
|
23
|
-
Requires-Dist: fastapi (>=0.
|
|
23
|
+
Requires-Dist: fastapi (>=0.112.1,<0.113.0)
|
|
24
24
|
Requires-Dist: gitpython (>=3.1.41,<4.0.0)
|
|
25
25
|
Requires-Dist: inquirer (>=3.2.1,<4.0.0)
|
|
26
26
|
Requires-Dist: jinja2 (>=3.1.3,<4.0.0)
|
|
@@ -28,9 +28,10 @@ Requires-Dist: matplotlib (>=3.8.3,<4.0.0)
|
|
|
28
28
|
Requires-Dist: networkx (>=3.2.1,<4.0.0)
|
|
29
29
|
Requires-Dist: pandas (>=2.1.4,<3.0.0)
|
|
30
30
|
Requires-Dist: pyjwt (>=2.8.0,<3.0.0)
|
|
31
|
+
Requires-Dist: python-multipart (>=0.0.9,<0.0.10)
|
|
31
32
|
Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
|
|
32
33
|
Requires-Dist: sqlalchemy (>=2.0.25,<3.0.0)
|
|
33
|
-
Requires-Dist: uvicorn (>=0.
|
|
34
|
+
Requires-Dist: uvicorn (>=0.30.6,<0.31.0)
|
|
34
35
|
Project-URL: Documentation, https://squirrels-analytics.github.io
|
|
35
36
|
Project-URL: Repository, https://github.com/squirrels-analytics/squirrels
|
|
36
37
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "squirrels"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.3"
|
|
4
4
|
description = "Squirrels - API Framework for Data Analytics"
|
|
5
5
|
license = "Apache-2.0"
|
|
6
6
|
authors = ["Tim Huang <tim.yuting@hotmail.com>"]
|
|
@@ -22,7 +22,7 @@ sqrl = "squirrels._command_line:main"
|
|
|
22
22
|
[tool.poetry.dependencies]
|
|
23
23
|
python = "^3.9"
|
|
24
24
|
cachetools = "^5.3.2"
|
|
25
|
-
fastapi = "
|
|
25
|
+
fastapi = "^0.112.1"
|
|
26
26
|
gitpython = "^3.1.41"
|
|
27
27
|
inquirer = "^3.2.1"
|
|
28
28
|
jinja2 = "^3.1.3"
|
|
@@ -30,10 +30,11 @@ pandas = "^2.1.4"
|
|
|
30
30
|
pyjwt = "^2.8.0"
|
|
31
31
|
pyyaml = "^6.0.1"
|
|
32
32
|
sqlalchemy = "^2.0.25"
|
|
33
|
-
uvicorn = "
|
|
33
|
+
uvicorn = "^0.30.6"
|
|
34
34
|
duckdb-engine = { version = ">=0.13.0,<1.0.0", optional = true }
|
|
35
35
|
matplotlib = "^3.8.3"
|
|
36
36
|
networkx = "^3.2.1"
|
|
37
|
+
python-multipart = "^0.0.9"
|
|
37
38
|
|
|
38
39
|
|
|
39
40
|
[tool.poetry.extras]
|
|
@@ -33,7 +33,6 @@ class SingleSelectParameterModel(SelectParameterModel):
|
|
|
33
33
|
class MultiSelectParameterModel(SelectParameterModel):
|
|
34
34
|
widget_type: Annotated[str, Field(examples=["multi_select"])]
|
|
35
35
|
show_select_all: bool
|
|
36
|
-
is_dropdown: bool
|
|
37
36
|
order_matters: bool
|
|
38
37
|
selected_ids: list[str]
|
|
39
38
|
|
|
@@ -63,7 +62,7 @@ class NumberRangeParameterModel(NumericParameterModel):
|
|
|
63
62
|
class TextParameterModel(ParameterModelBase):
|
|
64
63
|
widget_type: Annotated[str, Field(examples=["text"])]
|
|
65
64
|
entered_text: str
|
|
66
|
-
|
|
65
|
+
input_type: str
|
|
67
66
|
|
|
68
67
|
class ParametersModel(BaseModel):
|
|
69
68
|
parameters: list[
|
|
@@ -81,19 +81,15 @@ class ApiServer:
|
|
|
81
81
|
except u.InvalidInputError as exc:
|
|
82
82
|
traceback.print_exc()
|
|
83
83
|
return JSONResponse(status_code=status.HTTP_400_BAD_REQUEST,
|
|
84
|
-
content={"message":
|
|
84
|
+
content={"message": str(exc), "blame": "API client"})
|
|
85
85
|
except u.ConfigurationError as exc:
|
|
86
86
|
traceback.print_exc()
|
|
87
87
|
return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
88
|
-
content={"message": f"
|
|
89
|
-
except NotImplementedError as exc:
|
|
90
|
-
traceback.print_exc()
|
|
91
|
-
return JSONResponse(status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
|
92
|
-
content={"message": f"Not implemented error: {str(exc)}"})
|
|
88
|
+
content={"message": f"An unexpected error occurred", "blame": "Squirrels project"})
|
|
93
89
|
except Exception as exc:
|
|
94
90
|
traceback.print_exc()
|
|
95
91
|
return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
96
|
-
content={"message": f"
|
|
92
|
+
content={"message": f"An unexpected error occurred", "blame": "Squirrels framework"})
|
|
97
93
|
|
|
98
94
|
app.add_middleware(
|
|
99
95
|
CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"],
|
|
@@ -39,7 +39,7 @@ class Authenticator:
|
|
|
39
39
|
try:
|
|
40
40
|
real_user = get_user(self._get_auth_args(username, password)) if get_user is not None else None
|
|
41
41
|
except Exception as e:
|
|
42
|
-
raise u.FileExecutionError(f'Failed to run "{c.GET_USER_FUNC}" in {c.AUTH_FILE}', e)
|
|
42
|
+
raise u.FileExecutionError(f'Failed to run "{c.GET_USER_FUNC}" in {c.AUTH_FILE}', e) from e
|
|
43
43
|
|
|
44
44
|
if isinstance(real_user, User):
|
|
45
45
|
return real_user
|
|
@@ -53,7 +53,7 @@ class Authenticator:
|
|
|
53
53
|
try:
|
|
54
54
|
return user_cls.Create(username, is_internal=is_internal, **fake_user)
|
|
55
55
|
except Exception as e:
|
|
56
|
-
raise u.FileExecutionError(f'Failed to create user from User model in {c.AUTH_FILE}', e)
|
|
56
|
+
raise u.FileExecutionError(f'Failed to create user from User model in {c.AUTH_FILE}', e) from e
|
|
57
57
|
|
|
58
58
|
return None
|
|
59
59
|
|
|
@@ -31,8 +31,11 @@ class ConnectionSet:
|
|
|
31
31
|
|
|
32
32
|
def run_sql_query_from_conn_name(self, query: str, conn_name: str, placeholders: dict = {}) -> pd.DataFrame:
|
|
33
33
|
engine = self._get_engine(conn_name)
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
try:
|
|
35
|
+
df = pd.read_sql(query, engine, params=placeholders)
|
|
36
|
+
return df
|
|
37
|
+
except Exception as e:
|
|
38
|
+
raise RuntimeError(e) from e
|
|
36
39
|
|
|
37
40
|
def _dispose(self) -> None:
|
|
38
41
|
"""
|
|
@@ -63,7 +63,7 @@ class _SqlModelConfig:
|
|
|
63
63
|
elif self.materialized == Materialization.VIEW:
|
|
64
64
|
create_prefix = f"CREATE VIEW {model_name} AS\n"
|
|
65
65
|
else:
|
|
66
|
-
raise
|
|
66
|
+
raise u.ConfigurationError(f"Materialization option not supported: {self.materialized}")
|
|
67
67
|
|
|
68
68
|
return create_prefix + select_query
|
|
69
69
|
|
|
@@ -233,7 +233,7 @@ class _Model(_Referable):
|
|
|
233
233
|
kwargs = {
|
|
234
234
|
"proj_vars": ctx_args.proj_vars, "env_vars": ctx_args.env_vars, "user": ctx_args.user, "prms": ctx_args.prms,
|
|
235
235
|
"traits": ctx_args.traits, "ctx": ctx, "is_placeholder": is_placeholder, "set_placeholder": ctx_args.set_placeholder,
|
|
236
|
-
"config": configuration.set_attribute, "
|
|
236
|
+
"config": configuration.set_attribute, "param_exists": ctx_args.param_exists
|
|
237
237
|
}
|
|
238
238
|
dependencies = set()
|
|
239
239
|
if self.query_file.model_type == ModelType.FEDERATE:
|
|
@@ -245,7 +245,7 @@ class _Model(_Referable):
|
|
|
245
245
|
try:
|
|
246
246
|
query = await asyncio.to_thread(u.render_string, raw_query, **kwargs)
|
|
247
247
|
except Exception as e:
|
|
248
|
-
raise u.FileExecutionError(f'Failed to compile sql model "{self.name}"', e)
|
|
248
|
+
raise u.FileExecutionError(f'Failed to compile sql model "{self.name}"', e) from e
|
|
249
249
|
|
|
250
250
|
compiled_query = _SqlModelQuery(query, configuration)
|
|
251
251
|
return compiled_query, dependencies
|
|
@@ -261,7 +261,7 @@ class _Model(_Referable):
|
|
|
261
261
|
try:
|
|
262
262
|
dependencies = await asyncio.to_thread(self.query_file.raw_query.dependencies_func, sqrl_args)
|
|
263
263
|
except Exception as e:
|
|
264
|
-
raise u.FileExecutionError(f'Failed to run "{c.DEP_FUNC}" function for python model "{self.name}"', e)
|
|
264
|
+
raise u.FileExecutionError(f'Failed to run "{c.DEP_FUNC}" function for python model "{self.name}"', e) from e
|
|
265
265
|
|
|
266
266
|
dbview_conn_name = self._get_dbview_conn_name()
|
|
267
267
|
connections = ConnectionSetIO.obj.get_engines_as_dict()
|
|
@@ -276,7 +276,7 @@ class _Model(_Referable):
|
|
|
276
276
|
raw_query: _RawPyQuery = self.query_file.raw_query
|
|
277
277
|
return raw_query.query(sqrl=sqrl_args)
|
|
278
278
|
except Exception as e:
|
|
279
|
-
raise u.FileExecutionError(f'Failed to run "{c.MAIN_FUNC}" function for python model "{self.name}"', e)
|
|
279
|
+
raise u.FileExecutionError(f'Failed to run "{c.MAIN_FUNC}" function for python model "{self.name}"', e) from e
|
|
280
280
|
|
|
281
281
|
return _PyModelQuery(compiled_query), dependencies
|
|
282
282
|
|
|
@@ -295,7 +295,7 @@ class _Model(_Referable):
|
|
|
295
295
|
elif self.query_file.query_type == QueryType.PYTHON:
|
|
296
296
|
compiled_query, dependencies = await self._compile_python_model(ctx, ctx_args, placeholders)
|
|
297
297
|
else:
|
|
298
|
-
raise
|
|
298
|
+
raise u.ConfigurationError(f"Query type not supported: {self.query_file.query_type}")
|
|
299
299
|
|
|
300
300
|
self.compiled_query = compiled_query
|
|
301
301
|
self.wait_count = len(dependencies)
|
|
@@ -344,7 +344,7 @@ class _Model(_Referable):
|
|
|
344
344
|
try:
|
|
345
345
|
return ConnectionSetIO.obj.run_sql_query_from_conn_name(query, config.connection_name, placeholders)
|
|
346
346
|
except RuntimeError as e:
|
|
347
|
-
raise u.FileExecutionError(f'Failed to run dbview sql model "{self.name}"', e)
|
|
347
|
+
raise u.FileExecutionError(f'Failed to run dbview sql model "{self.name}"', e) from e
|
|
348
348
|
|
|
349
349
|
df = await asyncio.to_thread(run_sql_query)
|
|
350
350
|
await asyncio.to_thread(self._load_pandas_to_table, df, conn)
|
|
@@ -356,7 +356,7 @@ class _Model(_Referable):
|
|
|
356
356
|
try:
|
|
357
357
|
return conn.execute(text(create_query), placeholders)
|
|
358
358
|
except Exception as e:
|
|
359
|
-
raise u.FileExecutionError(f'Failed to run federate sql model "{self.name}"', e)
|
|
359
|
+
raise u.FileExecutionError(f'Failed to run federate sql model "{self.name}"', e) from e
|
|
360
360
|
|
|
361
361
|
await asyncio.to_thread(create_table)
|
|
362
362
|
if self.needs_pandas or self.is_target:
|
|
@@ -419,7 +419,7 @@ class _DAG:
|
|
|
419
419
|
try:
|
|
420
420
|
context_func(ctx=context, sqrl=args)
|
|
421
421
|
except Exception as e:
|
|
422
|
-
raise u.FileExecutionError(f'Failed to run {c.CONTEXT_FILE} for dataset "{self.dataset.name}"', e)
|
|
422
|
+
raise u.FileExecutionError(f'Failed to run {c.CONTEXT_FILE} for dataset "{self.dataset.name}"', e) from e
|
|
423
423
|
timer.add_activity_time(f"running context.py for dataset '{self.dataset.name}'", start)
|
|
424
424
|
return context, args
|
|
425
425
|
|
|
@@ -581,11 +581,11 @@ class ModelsIO:
|
|
|
581
581
|
elif test_set in ManifestIO.obj.selection_test_sets:
|
|
582
582
|
test_set_conf = ManifestIO.obj.selection_test_sets[test_set]
|
|
583
583
|
else:
|
|
584
|
-
raise u.
|
|
584
|
+
raise u.ConfigurationError(f"No test set named '{test_set}' was found when compiling dataset '{dataset}'. The test set must be defined if not default for dataset.")
|
|
585
585
|
|
|
586
586
|
error_msg_intro = f"Cannot compile dataset '{dataset}' with test set '{test_set}'."
|
|
587
587
|
if test_set_conf.datasets is not None and dataset not in test_set_conf.datasets:
|
|
588
|
-
raise u.
|
|
588
|
+
raise u.ConfigurationError(f"{error_msg_intro}\n Applicable datasets for test set '{test_set}' does not include dataset '{dataset}'.")
|
|
589
589
|
|
|
590
590
|
user_attributes = test_set_conf.user_attributes.copy()
|
|
591
591
|
selections = test_set_conf.parameters.copy()
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from typing import Annotated, Type, Optional, Union, Sequence, Iterator, Any
|
|
3
|
+
from datetime import datetime
|
|
3
4
|
from dataclasses import dataclass, field
|
|
4
5
|
from abc import ABCMeta, abstractmethod
|
|
5
6
|
from copy import copy
|
|
6
7
|
from fastapi import Query
|
|
7
8
|
from pydantic.fields import Field, FieldInfo
|
|
8
|
-
import pandas as pd
|
|
9
|
+
import pandas as pd, re
|
|
9
10
|
|
|
10
11
|
from . import parameter_options as po, parameters as p, data_sources as d, _api_response_models as arm, _utils as u, _constants as c
|
|
11
12
|
from .user_base import User
|
|
@@ -75,11 +76,6 @@ class ParameterConfigBase(metaclass=ABCMeta):
|
|
|
75
76
|
"""
|
|
76
77
|
return copy(self)
|
|
77
78
|
|
|
78
|
-
def to_json_dict0(self) -> arm.ParameterModelBase:
|
|
79
|
-
return {
|
|
80
|
-
"widget_type": self.widget_type, "name": self.name, "label": self.label, "description": self.description
|
|
81
|
-
}
|
|
82
|
-
|
|
83
79
|
|
|
84
80
|
@dataclass
|
|
85
81
|
class ParameterConfig(ParameterConfigBase):
|
|
@@ -171,11 +167,6 @@ class SelectionParameterConfig(ParameterConfig):
|
|
|
171
167
|
other.children = self.children.copy()
|
|
172
168
|
return other
|
|
173
169
|
|
|
174
|
-
def to_json_dict0(self) -> dict:
|
|
175
|
-
output = super().to_json_dict0()
|
|
176
|
-
output['trigger_refresh'] = self.trigger_refresh
|
|
177
|
-
return output
|
|
178
|
-
|
|
179
170
|
|
|
180
171
|
@dataclass
|
|
181
172
|
class SingleSelectParameterConfig(SelectionParameterConfig):
|
|
@@ -220,19 +211,17 @@ class MultiSelectParameterConfig(SelectionParameterConfig):
|
|
|
220
211
|
Class to define configurations for multi-select parameter widgets.
|
|
221
212
|
"""
|
|
222
213
|
show_select_all: bool # = field(default=True, kw_only=True)
|
|
223
|
-
is_dropdown: bool # = field(default=True, kw_only=True)
|
|
224
214
|
order_matters: bool # = field(default=False, kw_only=True)
|
|
225
215
|
none_is_all: bool # = field(default=True, kw_only=True)
|
|
226
216
|
|
|
227
217
|
def __init__(
|
|
228
218
|
self, name: str, label: str, all_options: Sequence[Union[po.SelectParameterOption, dict]], *, description: str = "",
|
|
229
|
-
show_select_all: bool = True,
|
|
219
|
+
show_select_all: bool = True, order_matters: bool = False, none_is_all: bool = True,
|
|
230
220
|
user_attribute: Optional[str] = None, parent_name: Optional[str] = None
|
|
231
221
|
) -> None:
|
|
232
222
|
super().__init__("multi_select", name, label, all_options, description=description, user_attribute=user_attribute,
|
|
233
223
|
parent_name=parent_name)
|
|
234
224
|
self.show_select_all = show_select_all
|
|
235
|
-
self.is_dropdown = is_dropdown
|
|
236
225
|
self.order_matters = order_matters
|
|
237
226
|
self.none_is_all = none_is_all
|
|
238
227
|
|
|
@@ -250,13 +239,6 @@ class MultiSelectParameterConfig(SelectionParameterConfig):
|
|
|
250
239
|
else:
|
|
251
240
|
selected_ids = u.load_json_or_comma_delimited_str_as_list(selection)
|
|
252
241
|
return p.MultiSelectParameter(self, options, selected_ids)
|
|
253
|
-
|
|
254
|
-
def to_json_dict0(self) -> dict:
|
|
255
|
-
output = super().to_json_dict0()
|
|
256
|
-
output['show_select_all'] = self.show_select_all
|
|
257
|
-
output['is_dropdown'] = self.is_dropdown
|
|
258
|
-
output['order_matters'] = self.order_matters
|
|
259
|
-
return output
|
|
260
242
|
|
|
261
243
|
def get_api_field_info(self) -> APIParamFieldInfo:
|
|
262
244
|
identifiers = [x._identifier for x in self.all_options]
|
|
@@ -354,7 +336,7 @@ class DateRangeParameterConfig(_DateTypeParameterConfig):
|
|
|
354
336
|
try:
|
|
355
337
|
selected_start_date, selected_end_date = u.load_json_or_comma_delimited_str_as_list(selection)
|
|
356
338
|
except ValueError as e:
|
|
357
|
-
self._raise_invalid_input_error(selection, "Date range parameter selection must be two dates
|
|
339
|
+
self._raise_invalid_input_error(selection, "Date range parameter selection must be two dates.", e)
|
|
358
340
|
return p.DateRangeParameter(self, curr_option, selected_start_date, selected_end_date)
|
|
359
341
|
|
|
360
342
|
def get_api_field_info(self) -> APIParamFieldInfo:
|
|
@@ -442,7 +424,7 @@ class NumberRangeParameterConfig(_NumericParameterConfig):
|
|
|
442
424
|
self, selection: Optional[str], user: Optional[User], parent_param: Optional[p._SelectionParameter],
|
|
443
425
|
*, request_version: Optional[int] = None
|
|
444
426
|
) -> p.NumberRangeParameter:
|
|
445
|
-
curr_option: po.NumberRangeParameterOption = next(self._get_options_iterator(user, parent_param), None)
|
|
427
|
+
curr_option: Optional[po.NumberRangeParameterOption] = next(self._get_options_iterator(user, parent_param), None)
|
|
446
428
|
if selection is None:
|
|
447
429
|
if curr_option is not None:
|
|
448
430
|
selected_lower_value = curr_option._default_lower_value
|
|
@@ -453,7 +435,7 @@ class NumberRangeParameterConfig(_NumericParameterConfig):
|
|
|
453
435
|
try:
|
|
454
436
|
selected_lower_value, selected_upper_value = u.load_json_or_comma_delimited_str_as_list(selection)
|
|
455
437
|
except ValueError as e:
|
|
456
|
-
self._raise_invalid_input_error(selection, "Number range parameter selection must be two numbers
|
|
438
|
+
self._raise_invalid_input_error(selection, "Number range parameter selection must be two numbers.", e)
|
|
457
439
|
return p.NumberRangeParameter(self, curr_option, selected_lower_value, selected_upper_value)
|
|
458
440
|
|
|
459
441
|
def get_api_field_info(self) -> APIParamFieldInfo:
|
|
@@ -469,16 +451,54 @@ class TextParameterConfig(ParameterConfig):
|
|
|
469
451
|
Class to define configurations for text parameter widgets.
|
|
470
452
|
"""
|
|
471
453
|
all_options: Sequence[po.TextParameterOption] = field(repr=False)
|
|
472
|
-
|
|
454
|
+
input_type: str
|
|
473
455
|
|
|
474
456
|
def __init__(
|
|
475
|
-
self, name: str, label: str, all_options: Sequence[Union[po.TextParameterOption, dict]], *,
|
|
476
|
-
|
|
477
|
-
parent_name: Optional[str] = None
|
|
457
|
+
self, name: str, label: str, all_options: Sequence[Union[po.TextParameterOption, dict]], *, description: str = "",
|
|
458
|
+
input_type: str = "text", user_attribute: Optional[str] = None, parent_name: Optional[str] = None
|
|
478
459
|
) -> None:
|
|
479
460
|
super().__init__("text", name, label, all_options, description=description, user_attribute=user_attribute,
|
|
480
461
|
parent_name=parent_name)
|
|
481
|
-
|
|
462
|
+
|
|
463
|
+
allowed_input_types = ["text", "textarea", "number", "date", "datetime-local", "month", "time", "color", "password"]
|
|
464
|
+
if input_type not in allowed_input_types:
|
|
465
|
+
raise u.ConfigurationError(f"Invalid input type '{input_type}' for text parameter '{name}'. Must be one of {allowed_input_types}.")
|
|
466
|
+
|
|
467
|
+
self.input_type = input_type
|
|
468
|
+
for option in self.all_options:
|
|
469
|
+
self.validate_entered_text(option._default_text)
|
|
470
|
+
|
|
471
|
+
def validate_entered_text(self, entered_text: str) -> str:
|
|
472
|
+
if self.input_type == "number":
|
|
473
|
+
try:
|
|
474
|
+
int(entered_text)
|
|
475
|
+
except ValueError as e:
|
|
476
|
+
raise self._raise_invalid_input_error(entered_text, "Must be an integer (without decimals).", e)
|
|
477
|
+
elif self.input_type == "date":
|
|
478
|
+
try:
|
|
479
|
+
datetime.strptime(entered_text, "%Y-%m-%d")
|
|
480
|
+
except ValueError as e:
|
|
481
|
+
raise self._raise_invalid_input_error(entered_text, "Must be a date in YYYY-MM-DD format.", e)
|
|
482
|
+
elif self.input_type == "datetime-local":
|
|
483
|
+
try:
|
|
484
|
+
datetime.strptime(entered_text, "%Y-%m-%dT%H:%M")
|
|
485
|
+
except ValueError as e:
|
|
486
|
+
raise self._raise_invalid_input_error(entered_text, "Must be a date in YYYY-MM-DDThh:mm format (e.g. 2020-01-01T07:00).", e)
|
|
487
|
+
elif self.input_type == "month":
|
|
488
|
+
try:
|
|
489
|
+
datetime.strptime(entered_text, "%Y-%m")
|
|
490
|
+
except ValueError as e:
|
|
491
|
+
raise self._raise_invalid_input_error(entered_text, "Must be a date in YYYY-MM format.", e)
|
|
492
|
+
elif self.input_type == "time":
|
|
493
|
+
try:
|
|
494
|
+
datetime.strptime(entered_text, "%H:%M")
|
|
495
|
+
except ValueError as e:
|
|
496
|
+
raise self._raise_invalid_input_error(entered_text, "Must be a time in hh:mm format.", e)
|
|
497
|
+
elif self.input_type == "color":
|
|
498
|
+
if not re.match(r"^#[0-9a-fA-F]{6}$", entered_text):
|
|
499
|
+
raise self._raise_invalid_input_error(entered_text, "Must be a valid color hex code (e.g. #000000).")
|
|
500
|
+
|
|
501
|
+
return entered_text
|
|
482
502
|
|
|
483
503
|
@staticmethod
|
|
484
504
|
def ParameterOption(*args, **kwargs):
|
|
@@ -495,11 +515,6 @@ class TextParameterConfig(ParameterConfig):
|
|
|
495
515
|
curr_option: po.TextParameterOption = next(self._get_options_iterator(user, parent_param), None)
|
|
496
516
|
entered_text = curr_option._default_text if selection is None and curr_option is not None else selection
|
|
497
517
|
return p.TextParameter(self, curr_option, entered_text)
|
|
498
|
-
|
|
499
|
-
def to_json_dict0(self) -> dict:
|
|
500
|
-
output = super().to_json_dict0()
|
|
501
|
-
output['is_textarea'] = self.is_textarea
|
|
502
|
-
return output
|
|
503
518
|
|
|
504
519
|
def get_api_field_info(self) -> APIParamFieldInfo:
|
|
505
520
|
examples = [x._default_text for x in self.all_options]
|
|
@@ -57,4 +57,4 @@ def run_pyconfig_main(filename: str, kwargs: dict[str, Any] = {}) -> None:
|
|
|
57
57
|
try:
|
|
58
58
|
main_function(**kwargs)
|
|
59
59
|
except Exception as e:
|
|
60
|
-
raise u.FileExecutionError(f'Failed to run python file "{filepath}"', e)
|
|
60
|
+
raise u.FileExecutionError(f'Failed to run python file "{filepath}"', e) from e
|
|
@@ -5,7 +5,7 @@ import pandas as pd
|
|
|
5
5
|
|
|
6
6
|
from .init_time_args import ConnectionsArgs, ParametersArgs
|
|
7
7
|
from ..user_base import User
|
|
8
|
-
from ..parameters import Parameter,
|
|
8
|
+
from ..parameters import Parameter, TextValue
|
|
9
9
|
from .._connection_set import ConnectionSetIO
|
|
10
10
|
from .. import _utils as u
|
|
11
11
|
|
|
@@ -24,7 +24,7 @@ class ContextArgs(ParametersArgs):
|
|
|
24
24
|
traits: dict[str, Any]
|
|
25
25
|
_placeholders: dict[str, Any]
|
|
26
26
|
|
|
27
|
-
def set_placeholder(self, placeholder: str, value: Union[
|
|
27
|
+
def set_placeholder(self, placeholder: str, value: Union[TextValue, Any]) -> str:
|
|
28
28
|
"""
|
|
29
29
|
Method to set a placeholder value.
|
|
30
30
|
|
|
@@ -32,9 +32,10 @@ class ContextArgs(ParametersArgs):
|
|
|
32
32
|
placeholder: A string for the name of the placeholder
|
|
33
33
|
value: The value of the placeholder. Can be of any type
|
|
34
34
|
"""
|
|
35
|
-
if isinstance(value,
|
|
35
|
+
if isinstance(value, TextValue):
|
|
36
36
|
value = value._value_do_not_touch
|
|
37
37
|
self._placeholders[placeholder] = value
|
|
38
|
+
return ""
|
|
38
39
|
|
|
39
40
|
def param_exists(self, param_name: str) -> bool:
|
|
40
41
|
"""
|
|
@@ -196,7 +196,7 @@ class SelectDataSource(_SelectionDataSource):
|
|
|
196
196
|
if ds_param.parameter_type == pc.SingleSelectParameterConfig:
|
|
197
197
|
return pc.SingleSelectParameterConfig(
|
|
198
198
|
ds_param.name, ds_param.label, all_options, description=ds_param.description,
|
|
199
|
-
user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name
|
|
199
|
+
user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name, **ds_param.extra_args
|
|
200
200
|
)
|
|
201
201
|
elif ds_param.parameter_type == pc.MultiSelectParameterConfig:
|
|
202
202
|
return pc.MultiSelectParameterConfig(
|
|
@@ -250,14 +250,13 @@ class MultiSelectDataSource(_SelectionDataSource):
|
|
|
250
250
|
DEPRECATED. Use "SelectDataSource" instead.
|
|
251
251
|
"""
|
|
252
252
|
_show_select_all: bool # = field(default=True, kw_only=True)
|
|
253
|
-
_is_dropdown: bool # = field(default=True, kw_only=True)
|
|
254
253
|
_order_matters: bool # = field(default=False, kw_only=True)
|
|
255
254
|
_none_is_all: bool # = field(default=True, kw_only=True)
|
|
256
255
|
|
|
257
256
|
def __init__(
|
|
258
257
|
self, table_or_query: str, id_col: str, options_col: str, *, order_by_col: Optional[str] = None,
|
|
259
258
|
is_default_col: Optional[str] = None, custom_cols: dict[str, str] = {}, show_select_all: bool = True,
|
|
260
|
-
|
|
259
|
+
order_matters: bool = False, none_is_all: bool = True, user_group_col: Optional[str] = None,
|
|
261
260
|
parent_id_col: Optional[str] = None, connection_name: Optional[str] = None, **kwargs
|
|
262
261
|
) -> None:
|
|
263
262
|
"""
|
|
@@ -270,7 +269,6 @@ class MultiSelectDataSource(_SelectionDataSource):
|
|
|
270
269
|
custom_cols=custom_cols, user_group_col=user_group_col, parent_id_col=parent_id_col,
|
|
271
270
|
connection_name=connection_name)
|
|
272
271
|
self._show_select_all = show_select_all
|
|
273
|
-
self._is_dropdown = is_dropdown
|
|
274
272
|
self._order_matters = order_matters
|
|
275
273
|
self._none_is_all = none_is_all
|
|
276
274
|
|
|
@@ -288,7 +286,7 @@ class MultiSelectDataSource(_SelectionDataSource):
|
|
|
288
286
|
self._validate_parameter_type(ds_param, pc.MultiSelectParameterConfig)
|
|
289
287
|
all_options = self._get_all_options(df)
|
|
290
288
|
return pc.MultiSelectParameterConfig(
|
|
291
|
-
ds_param.name, ds_param.label, all_options, show_select_all=self._show_select_all,
|
|
289
|
+
ds_param.name, ds_param.label, all_options, show_select_all=self._show_select_all,
|
|
292
290
|
order_matters=self._order_matters, none_is_all=self._none_is_all, description=ds_param.description,
|
|
293
291
|
user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name
|
|
294
292
|
)
|
|
@@ -353,8 +351,10 @@ class DateDataSource(DataSource):
|
|
|
353
351
|
parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record))
|
|
354
352
|
for _, record in records.items()
|
|
355
353
|
)
|
|
356
|
-
return pc.DateParameterConfig(
|
|
357
|
-
|
|
354
|
+
return pc.DateParameterConfig(
|
|
355
|
+
ds_param.name, ds_param.label, options, description=ds_param.description, user_attribute=ds_param.user_attribute,
|
|
356
|
+
parent_name=ds_param.parent_name, **ds_param.extra_args
|
|
357
|
+
)
|
|
358
358
|
|
|
359
359
|
|
|
360
360
|
@dataclass
|
|
@@ -420,8 +420,10 @@ class DateRangeDataSource(DataSource):
|
|
|
420
420
|
parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record))
|
|
421
421
|
for _, record in records.items()
|
|
422
422
|
)
|
|
423
|
-
return pc.DateRangeParameterConfig(
|
|
424
|
-
|
|
423
|
+
return pc.DateRangeParameterConfig(
|
|
424
|
+
ds_param.name, ds_param.label, options, description=ds_param.description, user_attribute=ds_param.user_attribute,
|
|
425
|
+
parent_name=ds_param.parent_name, **ds_param.extra_args
|
|
426
|
+
)
|
|
425
427
|
|
|
426
428
|
|
|
427
429
|
@dataclass
|
|
@@ -509,8 +511,10 @@ class NumberDataSource(_NumericDataSource):
|
|
|
509
511
|
parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record))
|
|
510
512
|
for _, record in records.items()
|
|
511
513
|
)
|
|
512
|
-
return pc.NumberParameterConfig(
|
|
513
|
-
|
|
514
|
+
return pc.NumberParameterConfig(
|
|
515
|
+
ds_param.name, ds_param.label, options, description=ds_param.description, user_attribute=ds_param.user_attribute,
|
|
516
|
+
parent_name=ds_param.parent_name, **ds_param.extra_args
|
|
517
|
+
)
|
|
514
518
|
|
|
515
519
|
|
|
516
520
|
@dataclass
|
|
@@ -579,8 +583,10 @@ class NumberRangeDataSource(_NumericDataSource):
|
|
|
579
583
|
parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record))
|
|
580
584
|
for _, record in records.items()
|
|
581
585
|
)
|
|
582
|
-
return pc.NumberRangeParameterConfig(
|
|
583
|
-
|
|
586
|
+
return pc.NumberRangeParameterConfig(
|
|
587
|
+
ds_param.name, ds_param.label, options, description=ds_param.description, user_attribute=ds_param.user_attribute,
|
|
588
|
+
parent_name=ds_param.parent_name, **ds_param.extra_args
|
|
589
|
+
)
|
|
584
590
|
|
|
585
591
|
|
|
586
592
|
@dataclass
|
|
@@ -639,5 +645,7 @@ class TextDataSource(DataSource):
|
|
|
639
645
|
parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record))
|
|
640
646
|
for _, record in records.items()
|
|
641
647
|
)
|
|
642
|
-
return pc.TextParameterConfig(
|
|
643
|
-
|
|
648
|
+
return pc.TextParameterConfig(
|
|
649
|
+
ds_param.name, ds_param.label, options, description=ds_param.description, user_attribute=ds_param.user_attribute,
|
|
650
|
+
parent_name=ds_param.parent_name, **ds_param.extra_args
|
|
651
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.padded{padding:8px 20px}.widget{width:100%;box-sizing:border-box;border:1px solid #ccc;border-radius:4px}.widget-label{position:relative;padding-bottom:10px}.widget-container{display:flex;flex-direction:column;gap:20px}input[type=submit]{padding:12px 20px;cursor:pointer}button{cursor:pointer;border-radius:5px;padding:10px}.white-button{background-color:#fff;color:#00f}.white-button:hover{background-color:#d4dae1}.blue-button{background-color:#4285f4;color:#fff;border:none}.blue-button:hover{background-color:#3076c5}.horizontal-container{display:flex}.auth-container{display:flex;align-items:center;gap:10px}#main-container{height:97vh;width:99vw}#left-container{width:250px;min-width:250px;margin-right:20px;padding:10px;overflow-y:auto}#right-container{height:97vh;width:calc(99vw - 300px)}#header-container{background-color:#d4dae1;color:#000;display:flex;align-items:center;justify-content:space-between;padding:10px}#table-container{padding:10px 20px;height:calc(97vh - 70px);overflow-x:auto;overflow-y:auto}.modal-background{position:fixed;z-index:1;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:#0006;padding-top:60px}.modal-content{background-color:#fefefe;padding:30px;margin:5px auto auto;border:2px solid black}#loading-indicator{justify-content:center;align-items:center;text-align:center;font-weight:700;position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff80}.spinner{margin:20px auto;width:40px;height:40px;position:relative;border-radius:50%;border:3px solid transparent;border-top-color:#3498db;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}table{border-collapse:collapse;width:100%}th,td{text-align:left;padding:8px;min-width:50px}th{background-color:#4285f4;color:#fff}tr:nth-child(2n){background-color:#f2f2f2}#result-table{margin:5px 0}.react-daterange-picker{display:inline-flex;position:relative}.react-daterange-picker,.react-daterange-picker *,.react-daterange-picker *:before,.react-daterange-picker *:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.react-daterange-picker--disabled{background-color:#f0f0f0;color:#6d6d6d}.react-daterange-picker__wrapper{display:flex;flex-grow:1;flex-shrink:0;align-items:center;border:thin solid gray}.react-daterange-picker__inputGroup{min-width:calc((4px * 3) + .54em * 8 + .217em * 2);height:100%;flex-grow:1;padding:0 2px;box-sizing:content-box}.react-daterange-picker__inputGroup__divider{padding:1px 0;white-space:pre}.react-daterange-picker__inputGroup__divider,.react-daterange-picker__inputGroup__leadingZero{display:inline-block}.react-daterange-picker__inputGroup__input{min-width:.54em;height:100%;position:relative;padding:0 1px;border:0;background:none;color:currentColor;font:inherit;box-sizing:content-box;-webkit-appearance:textfield;-moz-appearance:textfield;appearance:textfield}.react-daterange-picker__inputGroup__input::-webkit-outer-spin-button,.react-daterange-picker__inputGroup__input::-webkit-inner-spin-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;margin:0}.react-daterange-picker__inputGroup__input:invalid{background:#ff00001a}.react-daterange-picker__inputGroup__input--hasLeadingZero{margin-left:-.54em;padding-left:calc(1px + .54em)}.react-daterange-picker__button{border:0;background:transparent;padding:4px 6px}.react-daterange-picker__button:enabled{cursor:pointer}.react-daterange-picker__button:enabled:hover .react-daterange-picker__button__icon,.react-daterange-picker__button:enabled:focus .react-daterange-picker__button__icon{stroke:#0078d7}.react-daterange-picker__button:disabled .react-daterange-picker__button__icon{stroke:#6d6d6d}.react-daterange-picker__button svg{display:inherit}.react-daterange-picker__calendar{width:350px;max-width:100vw;z-index:1}.react-daterange-picker__calendar--closed{display:none}.react-daterange-picker__calendar .react-calendar{border-width:thin}.react-calendar{width:350px;max-width:100%;background:#fff;border:1px solid #a0a096;font-family:Arial,Helvetica,sans-serif;line-height:1.125em}.react-calendar--doubleView{width:700px}.react-calendar--doubleView .react-calendar__viewContainer{display:flex;margin:-.5em}.react-calendar--doubleView .react-calendar__viewContainer>*{width:50%;margin:.5em}.react-calendar,.react-calendar *,.react-calendar *:before,.react-calendar *:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.react-calendar button{margin:0;border:0;outline:none}.react-calendar button:enabled:hover{cursor:pointer}.react-calendar__navigation{display:flex;height:44px;margin-bottom:1em}.react-calendar__navigation button{min-width:44px;background:none}.react-calendar__navigation button:disabled{background-color:#f0f0f0}.react-calendar__navigation button:enabled:hover,.react-calendar__navigation button:enabled:focus{background-color:#e6e6e6}.react-calendar__month-view__weekdays{text-align:center;text-transform:uppercase;font:inherit;font-size:.75em;font-weight:700}.react-calendar__month-view__weekdays__weekday{padding:.5em}.react-calendar__month-view__weekNumbers .react-calendar__tile{display:flex;align-items:center;justify-content:center;font:inherit;font-size:.75em;font-weight:700}.react-calendar__month-view__days__day--weekend{color:#d10000}.react-calendar__month-view__days__day--neighboringMonth{color:#757575}.react-calendar__year-view .react-calendar__tile,.react-calendar__decade-view .react-calendar__tile,.react-calendar__century-view .react-calendar__tile{padding:2em .5em}.react-calendar__tile{max-width:100%;padding:10px 6.6667px;background:none;text-align:center;line-height:16px;font:inherit;font-size:.833em}.react-calendar__tile:disabled{background-color:#f0f0f0}.react-calendar__tile:enabled:hover,.react-calendar__tile:enabled:focus{background-color:#e6e6e6}.react-calendar__tile--now{background:#ffff76}.react-calendar__tile--now:enabled:hover,.react-calendar__tile--now:enabled:focus{background:#ffffa9}.react-calendar__tile--hasActive{background:#76baff}.react-calendar__tile--hasActive:enabled:hover,.react-calendar__tile--hasActive:enabled:focus{background:#a9d4ff}.react-calendar__tile--active{background:#006edc;color:#fff}.react-calendar__tile--active:enabled:hover,.react-calendar__tile--active:enabled:focus{background:#1087ff}.react-calendar--selectRange .react-calendar__tile--hover{background-color:#e6e6e6}.rc-slider{position:relative;width:100%;height:14px;padding:5px 0;border-radius:6px;touch-action:none;box-sizing:border-box;-webkit-tap-highlight-color:rgba(0,0,0,0)}.rc-slider *{box-sizing:border-box;-webkit-tap-highlight-color:rgba(0,0,0,0)}.rc-slider-rail{position:absolute;width:100%;height:4px;background-color:#e9e9e9;border-radius:6px}.rc-slider-track,.rc-slider-tracks{position:absolute;height:4px;background-color:#abe2fb;border-radius:6px}.rc-slider-track-draggable{z-index:1;box-sizing:content-box;background-clip:content-box;border-top:5px solid rgba(0,0,0,0);border-bottom:5px solid rgba(0,0,0,0);transform:translateY(-5px)}.rc-slider-handle{position:absolute;z-index:1;width:14px;height:14px;margin-top:-5px;background-color:#fff;border:solid 2px #96dbfa;border-radius:50%;cursor:pointer;cursor:-webkit-grab;cursor:grab;opacity:.8;touch-action:pan-x}.rc-slider-handle-dragging.rc-slider-handle-dragging.rc-slider-handle-dragging{border-color:#57c5f7;box-shadow:0 0 0 5px #96dbfa}.rc-slider-handle:focus{outline:none;box-shadow:none}.rc-slider-handle:focus-visible{border-color:#2db7f5;box-shadow:0 0 0 3px #96dbfa}.rc-slider-handle-click-focused:focus{border-color:#96dbfa;box-shadow:unset}.rc-slider-handle:hover{border-color:#57c5f7}.rc-slider-handle:active{border-color:#57c5f7;box-shadow:0 0 5px #57c5f7;cursor:-webkit-grabbing;cursor:grabbing}.rc-slider-mark{position:absolute;top:18px;left:0;width:100%;font-size:12px}.rc-slider-mark-text{position:absolute;display:inline-block;color:#999;text-align:center;vertical-align:middle;cursor:pointer}.rc-slider-mark-text-active{color:#666}.rc-slider-step{position:absolute;width:100%;height:4px;background:transparent}.rc-slider-dot{position:absolute;bottom:-2px;width:8px;height:8px;vertical-align:middle;background-color:#fff;border:2px solid #e9e9e9;border-radius:50%;cursor:pointer}.rc-slider-dot-active{border-color:#96dbfa}.rc-slider-dot-reverse{margin-right:-4px}.rc-slider-disabled{background-color:#e9e9e9}.rc-slider-disabled .rc-slider-track{background-color:#ccc}.rc-slider-disabled .rc-slider-handle,.rc-slider-disabled .rc-slider-dot{background-color:#fff;border-color:#ccc;box-shadow:none;cursor:not-allowed}.rc-slider-disabled .rc-slider-mark-text,.rc-slider-disabled .rc-slider-dot{cursor:not-allowed!important}.rc-slider-vertical{width:14px;height:100%;padding:0 5px}.rc-slider-vertical .rc-slider-rail{width:4px;height:100%}.rc-slider-vertical .rc-slider-track{bottom:0;left:5px;width:4px}.rc-slider-vertical .rc-slider-track-draggable{border-top:0;border-bottom:0;border-right:5px solid rgba(0,0,0,0);border-left:5px solid rgba(0,0,0,0);transform:translate(-5px)}.rc-slider-vertical .rc-slider-handle{position:absolute;z-index:1;margin-top:0;margin-left:-5px;touch-action:pan-y}.rc-slider-vertical .rc-slider-mark{top:0;left:18px;height:100%}.rc-slider-vertical .rc-slider-step{width:4px;height:100%}.rc-slider-vertical .rc-slider-dot{margin-left:-2px}.rc-slider-tooltip-zoom-down-enter,.rc-slider-tooltip-zoom-down-appear,.rc-slider-tooltip-zoom-down-leave{display:block!important;animation-duration:.3s;animation-fill-mode:both;animation-play-state:paused}.rc-slider-tooltip-zoom-down-enter.rc-slider-tooltip-zoom-down-enter-active,.rc-slider-tooltip-zoom-down-appear.rc-slider-tooltip-zoom-down-appear-active{animation-name:rcSliderTooltipZoomDownIn;animation-play-state:running}.rc-slider-tooltip-zoom-down-leave.rc-slider-tooltip-zoom-down-leave-active{animation-name:rcSliderTooltipZoomDownOut;animation-play-state:running}.rc-slider-tooltip-zoom-down-enter,.rc-slider-tooltip-zoom-down-appear{transform:scale(0);animation-timing-function:cubic-bezier(.23,1,.32,1)}.rc-slider-tooltip-zoom-down-leave{animation-timing-function:cubic-bezier(.755,.05,.855,.06)}@keyframes rcSliderTooltipZoomDownIn{0%{transform:scale(0);transform-origin:50% 100%;opacity:0}to{transform:scale(1);transform-origin:50% 100%}}@keyframes rcSliderTooltipZoomDownOut{0%{transform:scale(1);transform-origin:50% 100%}to{transform:scale(0);transform-origin:50% 100%;opacity:0}}.rc-slider-tooltip{position:absolute;top:-9999px;left:-9999px;visibility:visible;box-sizing:border-box;-webkit-tap-highlight-color:rgba(0,0,0,0)}.rc-slider-tooltip *{box-sizing:border-box;-webkit-tap-highlight-color:rgba(0,0,0,0)}.rc-slider-tooltip-hidden{display:none}.rc-slider-tooltip-placement-top{padding:4px 0 8px}.rc-slider-tooltip-inner{min-width:24px;height:24px;padding:6px 2px;color:#fff;font-size:12px;line-height:1;text-align:center;text-decoration:none;background-color:#6c6c6c;border-radius:6px;box-shadow:0 0 4px #d9d9d9}.rc-slider-tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.rc-slider-tooltip-placement-top .rc-slider-tooltip-arrow{bottom:4px;left:50%;margin-left:-4px;border-width:4px 4px 0;border-top-color:#6c6c6c}.hover-text{position:absolute;z-index:1;background-color:#f9f9f9;border:1px solid #ccc;border-radius:4px}input[type=range]{width:80%;padding:12px 0;border:0px}.slider-info{display:flex;font-size:13px;justify-content:space-between}.react-daterange-picker{margin-bottom:15px;height:40px;font-size:14px}.react-daterange-picker__wrapper{width:200px;border:0}.react-daterange-picker__inputGroup{min-width:0px;text-align:center}.slider-wrapper{display:flex;flex-direction:column;align-items:center}.rc-slider{width:95%;margin:10px 0}.rc-slider-handle{z-index:0;opacity:1;width:20px;height:20px;margin:-7px 0}
|