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.

Files changed (56) hide show
  1. {squirrels-0.3.1 → squirrels-0.3.3}/PKG-INFO +4 -3
  2. {squirrels-0.3.1 → squirrels-0.3.3}/pyproject.toml +4 -3
  3. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/__init__.py +1 -1
  4. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_api_response_models.py +1 -2
  5. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_api_server.py +3 -7
  6. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_authenticator.py +2 -2
  7. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_connection_set.py +5 -2
  8. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_models.py +11 -11
  9. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_parameter_configs.py +49 -34
  10. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_py_module.py +1 -1
  11. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/arguments/run_time_args.py +4 -3
  12. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/data_sources.py +23 -15
  13. squirrels-0.3.3/squirrels/package_data/assets/index.css +1 -0
  14. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/assets/index.js +13 -13
  15. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/parameters.yml +16 -16
  16. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/pyconfigs/parameters.py +16 -16
  17. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/parameter_options.py +5 -4
  18. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/parameters.py +179 -62
  19. squirrels-0.3.1/squirrels/package_data/assets/index.css +0 -1
  20. {squirrels-0.3.1 → squirrels-0.3.3}/LICENSE +0 -0
  21. {squirrels-0.3.1 → squirrels-0.3.3}/README.md +0 -0
  22. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_command_line.py +0 -0
  23. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_constants.py +0 -0
  24. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_environcfg.py +0 -0
  25. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_initializer.py +0 -0
  26. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_manifest.py +0 -0
  27. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_package_loader.py +0 -0
  28. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_parameter_sets.py +0 -0
  29. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_seeds.py +0 -0
  30. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_timer.py +0 -0
  31. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_utils.py +0 -0
  32. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/_version.py +0 -0
  33. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/arguments/init_time_args.py +0 -0
  34. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/dateutils.py +0 -0
  35. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/assets/favicon.ico +0 -0
  36. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/.gitignore +0 -0
  37. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/assets/expenses.db +0 -0
  38. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/assets/weather.db +0 -0
  39. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/connections.yml +0 -0
  40. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/docker/.dockerignore +0 -0
  41. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/docker/Dockerfile +0 -0
  42. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/docker/compose.yml +0 -0
  43. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/env.yml +0 -0
  44. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/models/dbviews/database_view1.py +0 -0
  45. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/models/dbviews/database_view1.sql +0 -0
  46. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/models/federates/dataset_example.py +0 -0
  47. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/models/federates/dataset_example.sql +0 -0
  48. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/pyconfigs/auth.py +0 -0
  49. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/pyconfigs/connections.py +0 -0
  50. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/pyconfigs/context.py +0 -0
  51. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/seeds/seed_categories.csv +0 -0
  52. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/seeds/seed_subcategories.csv +0 -0
  53. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/squirrels.yml.j2 +0 -0
  54. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/base_project/tmp/.gitignore +0 -0
  55. {squirrels-0.3.1 → squirrels-0.3.3}/squirrels/package_data/templates/index.html +0 -0
  56. {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.1
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.110.1,<1.0.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.29.0,<1.0.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.1"
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 = ">=0.110.1,<1.0.0"
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 = ">=0.29.0,<1.0.0"
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]
@@ -1,4 +1,4 @@
1
- __version__ = '0.3.1'
1
+ __version__ = '0.3.3'
2
2
 
3
3
  from .arguments.init_time_args import ConnectionsArgs, ParametersArgs
4
4
  from .arguments.run_time_args import AuthArgs, ContextArgs, ModelDepsArgs, ModelArgs
@@ -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
- is_textarea: bool
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": f"Invalid user input: {str(exc)}"})
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"Squirrels configuration error: {str(exc)}"})
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"Server error: {str(exc)}"})
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
- df = pd.read_sql(query, engine, params=placeholders)
35
- return df
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 NotImplementedError(f"Materialization option not supported: {self.materialized}")
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, "is_param_enabled": ctx_args.param_exists
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 NotImplementedError(f"Query type not supported: {self.query_file.query_type}")
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.InvalidInputError(f"No test set named '{test_set}' was found when compiling dataset '{dataset}'. The test set must be defined if not default for dataset.")
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.InvalidInputError(f"{error_msg_intro}\n Applicable datasets for test set '{test_set}' does not include dataset '{dataset}'.")
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, is_dropdown: bool = True, order_matters: bool = False, none_is_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 joined by comma.", e)
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 joined by comma.", e)
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
- is_textarea: bool
454
+ input_type: str
473
455
 
474
456
  def __init__(
475
- self, name: str, label: str, all_options: Sequence[Union[po.TextParameterOption, dict]], *,
476
- description: str = "", is_textarea: bool = False, user_attribute: Optional[str] = None,
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
- self.is_textarea = is_textarea
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, _TextValue
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[_TextValue, Any]) -> None:
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, _TextValue):
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
- is_dropdown: bool = True, order_matters: bool = False, none_is_all: bool = True, user_group_col: Optional[str] = None,
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, is_dropdown=self._is_dropdown,
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(ds_param.name, ds_param.label, options, description=ds_param.description,
357
- user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name)
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(ds_param.name, ds_param.label, options, description=ds_param.description,
424
- user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name)
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(ds_param.name, ds_param.label, options, description=ds_param.description,
513
- user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name)
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(ds_param.name, ds_param.label, options, description=ds_param.description,
583
- user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name)
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(ds_param.name, ds_param.label, options, description=ds_param.description,
643
- user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name)
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}