upgini 1.1.271__tar.gz → 1.1.274__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.
Files changed (85) hide show
  1. {upgini-1.1.271/src/upgini.egg-info → upgini-1.1.274}/PKG-INFO +2 -2
  2. {upgini-1.1.271 → upgini-1.1.274}/setup.py +2 -2
  3. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/autofe/date.py +9 -2
  4. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/data_source/data_source_publisher.py +14 -4
  5. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/features_enricher.py +66 -27
  6. upgini-1.1.274/src/upgini/fingerprint.js +8 -0
  7. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/metrics.py +12 -0
  8. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/normalizer/phone_normalizer.py +2 -2
  9. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/resource_bundle/strings.properties +2 -1
  10. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/datetime_utils.py +3 -0
  11. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/track_info.py +25 -13
  12. {upgini-1.1.271 → upgini-1.1.274/src/upgini.egg-info}/PKG-INFO +2 -2
  13. {upgini-1.1.271 → upgini-1.1.274}/src/upgini.egg-info/SOURCES.txt +1 -0
  14. {upgini-1.1.271 → upgini-1.1.274}/src/upgini.egg-info/requires.txt +1 -1
  15. {upgini-1.1.271 → upgini-1.1.274}/tests/test_autofe_operands.py +2 -1
  16. {upgini-1.1.271 → upgini-1.1.274}/tests/test_features_enricher.py +18 -13
  17. {upgini-1.1.271 → upgini-1.1.274}/LICENSE +0 -0
  18. {upgini-1.1.271 → upgini-1.1.274}/README.md +0 -0
  19. {upgini-1.1.271 → upgini-1.1.274}/pyproject.toml +0 -0
  20. {upgini-1.1.271 → upgini-1.1.274}/setup.cfg +0 -0
  21. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/__init__.py +0 -0
  22. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/ads.py +0 -0
  23. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/ads_management/__init__.py +0 -0
  24. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/ads_management/ads_manager.py +0 -0
  25. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/autofe/__init__.py +0 -0
  26. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/autofe/all_operands.py +0 -0
  27. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/autofe/binary.py +0 -0
  28. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/autofe/feature.py +0 -0
  29. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/autofe/groupby.py +0 -0
  30. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/autofe/operand.py +0 -0
  31. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/autofe/unary.py +0 -0
  32. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/autofe/vector.py +0 -0
  33. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/data_source/__init__.py +0 -0
  34. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/dataset.py +0 -0
  35. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/errors.py +0 -0
  36. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/http.py +0 -0
  37. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/mdc/__init__.py +0 -0
  38. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/mdc/context.py +0 -0
  39. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/metadata.py +0 -0
  40. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/normalizer/__init__.py +0 -0
  41. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/resource_bundle/__init__.py +0 -0
  42. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/resource_bundle/exceptions.py +0 -0
  43. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/resource_bundle/strings_widget.properties +0 -0
  44. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/sampler/__init__.py +0 -0
  45. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/sampler/base.py +0 -0
  46. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/sampler/random_under_sampler.py +0 -0
  47. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/sampler/utils.py +0 -0
  48. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/search_task.py +0 -0
  49. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/spinner.py +0 -0
  50. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/__init__.py +0 -0
  51. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/base_search_key_detector.py +0 -0
  52. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/blocked_time_series.py +0 -0
  53. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/country_utils.py +0 -0
  54. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/custom_loss_utils.py +0 -0
  55. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/cv_utils.py +0 -0
  56. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/deduplicate_utils.py +0 -0
  57. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/display_utils.py +0 -0
  58. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/email_utils.py +0 -0
  59. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/fallback_progress_bar.py +0 -0
  60. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/features_validator.py +0 -0
  61. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/format.py +0 -0
  62. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/ip_utils.py +0 -0
  63. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/phone_utils.py +0 -0
  64. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/postal_code_utils.py +0 -0
  65. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/progress_bar.py +0 -0
  66. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/sklearn_ext.py +0 -0
  67. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/target_utils.py +0 -0
  68. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/utils/warning_counter.py +0 -0
  69. {upgini-1.1.271 → upgini-1.1.274}/src/upgini/version_validator.py +0 -0
  70. {upgini-1.1.271 → upgini-1.1.274}/src/upgini.egg-info/dependency_links.txt +0 -0
  71. {upgini-1.1.271 → upgini-1.1.274}/src/upgini.egg-info/top_level.txt +0 -0
  72. {upgini-1.1.271 → upgini-1.1.274}/tests/test_binary_dataset.py +0 -0
  73. {upgini-1.1.271 → upgini-1.1.274}/tests/test_blocked_time_series.py +0 -0
  74. {upgini-1.1.271 → upgini-1.1.274}/tests/test_categorical_dataset.py +0 -0
  75. {upgini-1.1.271 → upgini-1.1.274}/tests/test_continuous_dataset.py +0 -0
  76. {upgini-1.1.271 → upgini-1.1.274}/tests/test_country_utils.py +0 -0
  77. {upgini-1.1.271 → upgini-1.1.274}/tests/test_custom_loss_utils.py +0 -0
  78. {upgini-1.1.271 → upgini-1.1.274}/tests/test_datetime_utils.py +0 -0
  79. {upgini-1.1.271 → upgini-1.1.274}/tests/test_email_utils.py +0 -0
  80. {upgini-1.1.271 → upgini-1.1.274}/tests/test_etalon_validation.py +0 -0
  81. {upgini-1.1.271 → upgini-1.1.274}/tests/test_metrics.py +0 -0
  82. {upgini-1.1.271 → upgini-1.1.274}/tests/test_phone_utils.py +0 -0
  83. {upgini-1.1.271 → upgini-1.1.274}/tests/test_postal_code_utils.py +0 -0
  84. {upgini-1.1.271 → upgini-1.1.274}/tests/test_target_utils.py +0 -0
  85. {upgini-1.1.271 → upgini-1.1.274}/tests/test_widget.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: upgini
3
- Version: 1.1.271
3
+ Version: 1.1.274
4
4
  Summary: Intelligent data search & enrichment for Machine Learning
5
5
  Home-page: https://upgini.com/
6
6
  Author: Upgini Developers
@@ -28,7 +28,7 @@ Description-Content-Type: text/markdown
28
28
  License-File: LICENSE
29
29
  Requires-Dist: python-dateutil>=2.8.0
30
30
  Requires-Dist: requests>=2.8.0
31
- Requires-Dist: pandas<2.0.0,>=1.1.0
31
+ Requires-Dist: pandas<3.0.0,>=1.1.0
32
32
  Requires-Dist: numpy>=1.19.0
33
33
  Requires-Dist: scikit-learn>=1.3.0
34
34
  Requires-Dist: pydantic<2.0.0,>=1.8.2
@@ -40,7 +40,7 @@ def send_log(msg: str):
40
40
 
41
41
 
42
42
  here = Path(__file__).parent.resolve()
43
- version = "1.1.271"
43
+ version = "1.1.274"
44
44
  try:
45
45
  send_log(f"Start setup PyLib version {version}")
46
46
  setup(
@@ -77,7 +77,7 @@ try:
77
77
  install_requires=[
78
78
  "python-dateutil>=2.8.0",
79
79
  "requests>=2.8.0",
80
- "pandas>=1.1.0,<2.0.0",
80
+ "pandas>=1.1.0,<3.0.0",
81
81
  "numpy>=1.19.0",
82
82
  "scikit-learn>=1.3.0",
83
83
  "pydantic>=1.8.2,<2.0.0",
@@ -2,6 +2,7 @@ from typing import Any, Optional, Union
2
2
  import numpy as np
3
3
  import pandas as pd
4
4
  from pydantic import BaseModel
5
+ from pandas.core.arrays.timedeltas import TimedeltaArray
5
6
 
6
7
  from upgini.autofe.operand import PandasOperand
7
8
 
@@ -46,6 +47,7 @@ class DateDiffType2(PandasOperand, DateDiffMixin):
46
47
  future = right + (left.dt.year - right.dt.year).apply(
47
48
  lambda y: np.datetime64("NaT") if np.isnan(y) else pd.tseries.offsets.DateOffset(years=y)
48
49
  )
50
+ future = pd.to_datetime(future)
49
51
  before = future[future < left]
50
52
  future[future < left] = before + pd.tseries.offsets.DateOffset(years=1)
51
53
  diff = (future - left) / np.timedelta64(1, self.diff_unit)
@@ -72,8 +74,13 @@ class DateListDiff(PandasOperand, DateDiffMixin):
72
74
 
73
75
  return pd.Series(left - right.values).apply(lambda x: self._agg(self._diff(x)))
74
76
 
75
- def _diff(self, x):
76
- x = x / np.timedelta64(1, self.diff_unit)
77
+ def _diff(self, x: TimedeltaArray):
78
+ if self.diff_unit == "Y":
79
+ x = (x / 365 / 24 / 60 / 60 / 10**9).astype(int)
80
+ elif self.diff_unit == "M":
81
+ raise Exception("Unsupported difference unit: Month")
82
+ else:
83
+ x = x / np.timedelta64(1, self.diff_unit)
77
84
  return x[x > 0]
78
85
 
79
86
  def _agg(self, x):
@@ -48,6 +48,7 @@ class DataSourcePublisher:
48
48
  data_table_uri: str,
49
49
  search_keys: Dict[str, SearchKey],
50
50
  update_frequency: str,
51
+ exclude_from_autofe_generation: Optional[List[str]],
51
52
  secondary_search_keys: Optional[Dict[str, SearchKey]] = None,
52
53
  sort_column: Optional[str] = None,
53
54
  date_format: Optional[str] = None,
@@ -57,7 +58,6 @@ class DataSourcePublisher:
57
58
  join_date_abs_limit_days: Optional[int] = None,
58
59
  features_for_embeddings: Optional[List[str]] = DEFAULT_GENERATE_EMBEDDINGS,
59
60
  data_table_id_to_replace: Optional[str] = None,
60
- exclude_from_autofe_generation: Optional[List[str]] = None,
61
61
  _force_generation=False,
62
62
  _silent=False,
63
63
  ) -> str:
@@ -72,8 +72,8 @@ class DataSourcePublisher:
72
72
  )
73
73
  if search_keys is None or len(search_keys) == 0:
74
74
  raise ValidationError("Empty search keys")
75
- if SearchKey.DATE in search_keys.values() and date_format is None:
76
- raise ValidationError("date_format is required for DATE search key")
75
+ # if SearchKey.DATE in search_keys.values() and date_format is None:
76
+ # raise ValidationError("date_format is required for DATE search key")
77
77
  if update_frequency not in self.ACCEPTABLE_UPDATE_FREQUENCIES:
78
78
  raise ValidationError(
79
79
  f"Invalid update frequency: {update_frequency}. "
@@ -85,11 +85,19 @@ class DataSourcePublisher:
85
85
  or set(search_keys.values()) == {SearchKey.MSISDN_RANGE_FROM, SearchKey.MSISDN_RANGE_TO}
86
86
  ) and sort_column is None:
87
87
  raise ValidationError("Sort column is required for passed search keys")
88
+ if (
89
+ set(search_keys.values()) == {SearchKey.PHONE, SearchKey.DATE}
90
+ and snapshot_frequency_days is None
91
+ and join_date_abs_limit_days is None
92
+ ):
93
+ raise ValidationError(
94
+ "With MSISDN and DATE keys one of the snapshot_frequency_days or"
95
+ " join_date_abs_limit_days parameters is required"
96
+ )
88
97
 
89
98
  request = {
90
99
  "dataTableUri": data_table_uri,
91
100
  "searchKeys": {k: v.value.value for k, v in search_keys.items()},
92
- "dateFormat": date_format,
93
101
  "excludeColumns": exclude_columns,
94
102
  "hashFeatureNames": str(hash_feature_names).lower(),
95
103
  "snapshotFrequencyDays": snapshot_frequency_days,
@@ -98,6 +106,8 @@ class DataSourcePublisher:
98
106
  "featuresForEmbeddings": features_for_embeddings,
99
107
  "forceGeneration": str(_force_generation).lower(),
100
108
  }
109
+ if date_format is not None:
110
+ request["dateFormat"] = date_format
101
111
  if secondary_search_keys is not None:
102
112
  request["secondarySearchKeys"] = {k: v.value.value for k, v in secondary_search_keys.items()}
103
113
  if sort_column is not None:
@@ -1,4 +1,5 @@
1
1
  import dataclasses
2
+ import datetime
2
3
  import gc
3
4
  import hashlib
4
5
  import itertools
@@ -146,6 +147,7 @@ class FeaturesEnricher(TransformerMixin):
146
147
  """
147
148
 
148
149
  TARGET_NAME = "target"
150
+ CURRENT_DATE = "current_date"
149
151
  RANDOM_STATE = 42
150
152
  CALCULATE_METRICS_THRESHOLD = 50_000_000
151
153
  CALCULATE_METRICS_MIN_THRESHOLD = 500
@@ -207,6 +209,7 @@ class FeaturesEnricher(TransformerMixin):
207
209
  client_ip: Optional[str] = None,
208
210
  client_visitorid: Optional[str] = None,
209
211
  custom_bundle_config: Optional[str] = None,
212
+ add_date_if_missing: bool = True,
210
213
  **kwargs,
211
214
  ):
212
215
  self.bundle = get_custom_bundle(custom_bundle_config)
@@ -317,6 +320,7 @@ class FeaturesEnricher(TransformerMixin):
317
320
  self.raise_validation_error = raise_validation_error
318
321
  self.exclude_columns = exclude_columns
319
322
  self.baseline_score_column = baseline_score_column
323
+ self.add_date_if_missing = add_date_if_missing
320
324
 
321
325
  def _get_api_key(self):
322
326
  return self._api_key
@@ -420,6 +424,9 @@ class FeaturesEnricher(TransformerMixin):
420
424
 
421
425
  self.__validate_search_keys(self.search_keys, self.search_id)
422
426
 
427
+ # Validate client estimator params
428
+ self._get_client_cat_features(estimator, X, self.search_keys)
429
+
423
430
  try:
424
431
  self.X = X
425
432
  self.y = y
@@ -813,6 +820,7 @@ class FeaturesEnricher(TransformerMixin):
813
820
  trace_id = trace_id or str(uuid.uuid4())
814
821
  start_time = time.time()
815
822
  with MDC(trace_id=trace_id):
823
+ self.logger.info("Start calculate metrics")
816
824
  if len(args) > 0:
817
825
  msg = f"WARNING: Unsupported positional arguments for calculate_metrics: {args}"
818
826
  self.logger.warning(msg)
@@ -864,22 +872,9 @@ class FeaturesEnricher(TransformerMixin):
864
872
  self.__display_support_link(msg)
865
873
  return None
866
874
 
867
- cat_features = None
868
- search_keys_for_metrics = []
869
- if (
870
- estimator is not None
871
- and hasattr(estimator, "get_param")
872
- and estimator.get_param("cat_features") is not None
873
- ):
874
- cat_features = estimator.get_param("cat_features")
875
- if len(cat_features) > 0 and isinstance(cat_features[0], int):
876
- cat_features = [effective_X.columns[i] for i in cat_features]
877
- for cat_feature in cat_features:
878
- if cat_feature in self.search_keys:
879
- if self.search_keys[cat_feature] in [SearchKey.COUNTRY, SearchKey.POSTAL_CODE]:
880
- search_keys_for_metrics.append(cat_feature)
881
- else:
882
- raise ValidationError(self.bundle.get("cat_feature_search_key").format(cat_feature))
875
+ cat_features, search_keys_for_metrics = self._get_client_cat_features(
876
+ estimator, effective_X, self.search_keys
877
+ )
883
878
 
884
879
  prepared_data = self._prepare_data_for_metrics(
885
880
  trace_id=trace_id,
@@ -894,6 +889,7 @@ class FeaturesEnricher(TransformerMixin):
894
889
  search_keys_for_metrics=search_keys_for_metrics,
895
890
  progress_bar=progress_bar,
896
891
  progress_callback=progress_callback,
892
+ cat_features=cat_features,
897
893
  )
898
894
  if prepared_data is None:
899
895
  return None
@@ -1269,6 +1265,29 @@ class FeaturesEnricher(TransformerMixin):
1269
1265
 
1270
1266
  return _cv, groups
1271
1267
 
1268
+ def _get_client_cat_features(
1269
+ self, estimator: Optional[Any], X: pd.DataFrame, search_keys: Dict[str, SearchKey]
1270
+ ) -> Optional[List[str]]:
1271
+ cat_features = None
1272
+ search_keys_for_metrics = []
1273
+ if (
1274
+ estimator is not None
1275
+ and hasattr(estimator, "get_param")
1276
+ and estimator.get_param("cat_features") is not None
1277
+ ):
1278
+ cat_features = estimator.get_param("cat_features")
1279
+ if len(cat_features) > 0:
1280
+ if all([isinstance(f, int) for f in cat_features]):
1281
+ cat_features = [X.columns[i] for i in cat_features]
1282
+ self.logger.info(f"Collected categorical features {cat_features} from user estimator")
1283
+ for cat_feature in cat_features:
1284
+ if cat_feature in search_keys:
1285
+ if search_keys[cat_feature] in [SearchKey.COUNTRY, SearchKey.POSTAL_CODE]:
1286
+ search_keys_for_metrics.append(cat_feature)
1287
+ else:
1288
+ raise ValidationError(self.bundle.get("cat_feature_search_key").format(cat_feature))
1289
+ return cat_features, search_keys_for_metrics
1290
+
1272
1291
  def _prepare_data_for_metrics(
1273
1292
  self,
1274
1293
  trace_id: str,
@@ -1283,6 +1302,7 @@ class FeaturesEnricher(TransformerMixin):
1283
1302
  search_keys_for_metrics: Optional[List[str]] = None,
1284
1303
  progress_bar: Optional[ProgressBar] = None,
1285
1304
  progress_callback: Optional[Callable[[SearchProgress], Any]] = None,
1305
+ cat_features: Optional[List[str]] = None,
1286
1306
  ):
1287
1307
  is_input_same_as_fit, X, y, eval_set = self._is_input_same_as_fit(X, y, eval_set)
1288
1308
  is_demo_dataset = hash_input(X, y, eval_set) in DEMO_DATASET_HASHES
@@ -1340,9 +1360,8 @@ class FeaturesEnricher(TransformerMixin):
1340
1360
 
1341
1361
  # Detect and drop high cardinality columns in train
1342
1362
  columns_with_high_cardinality = FeaturesValidator.find_high_cardinality(fitting_X)
1343
- columns_with_high_cardinality = [
1344
- c for c in columns_with_high_cardinality if c not in (self.generate_features or [])
1345
- ]
1363
+ non_excluding_columns = (self.generate_features or []) + (cat_features or [])
1364
+ columns_with_high_cardinality = [c for c in columns_with_high_cardinality if c not in non_excluding_columns]
1346
1365
  if len(columns_with_high_cardinality) > 0:
1347
1366
  self.logger.warning(
1348
1367
  f"High cardinality columns {columns_with_high_cardinality} will be dropped for metrics calculation"
@@ -1804,10 +1823,11 @@ class FeaturesEnricher(TransformerMixin):
1804
1823
  else:
1805
1824
  features_section = ""
1806
1825
 
1807
- api_example = f"""curl 'https://inference-upgini.azurewebsites.net/api/http_inference_trigger' \\
1826
+ search_id = self._search_task.search_task_id
1827
+ api_example = f"""curl 'https://search.upgini.com/online/api/http_inference_trigger?search_id={search_id}' \\
1808
1828
  -H 'Authorization: {self.api_key}' \\
1809
1829
  -H 'Content-Type: application/json' \\
1810
- -d '{{"search_id": "{self._search_task.search_task_id}", "search_keys": {keys}{features_section}}}'"""
1830
+ -d '{{"search_keys": {keys}{features_section}}}'"""
1811
1831
  return api_example
1812
1832
 
1813
1833
  def _get_copy_of_runtime_parameters(self) -> RuntimeParameters:
@@ -1902,6 +1922,8 @@ class FeaturesEnricher(TransformerMixin):
1902
1922
  generated_features.extend(converter.generated_features)
1903
1923
  else:
1904
1924
  self.logger.info("Input dataset hasn't date column")
1925
+ if self.add_date_if_missing:
1926
+ df = self._add_current_date_as_key(df, search_keys, self.logger, self.bundle)
1905
1927
  email_column = self._get_email_column(search_keys)
1906
1928
  hem_column = self._get_hem_column(search_keys)
1907
1929
  email_converted_to_hem = False
@@ -2220,9 +2242,7 @@ class FeaturesEnricher(TransformerMixin):
2220
2242
  self.fit_search_keys = self.search_keys.copy()
2221
2243
  self.fit_search_keys = self.__prepare_search_keys(validated_X, self.fit_search_keys, is_demo_dataset)
2222
2244
 
2223
- validate_dates_distribution(
2224
- validated_X, self.fit_search_keys, self.logger, self.bundle, self.warning_counter
2225
- )
2245
+ validate_dates_distribution(validated_X, self.fit_search_keys, self.logger, self.bundle, self.warning_counter)
2226
2246
 
2227
2247
  maybe_date_column = self._get_date_column(self.fit_search_keys)
2228
2248
  has_date = maybe_date_column is not None
@@ -2273,6 +2293,8 @@ class FeaturesEnricher(TransformerMixin):
2273
2293
  self.fit_generated_features.extend(converter.generated_features)
2274
2294
  else:
2275
2295
  self.logger.info("Input dataset hasn't date column")
2296
+ if self.add_date_if_missing:
2297
+ df = self._add_current_date_as_key(df, self.fit_search_keys, self.logger, self.bundle)
2276
2298
  email_column = self._get_email_column(self.fit_search_keys)
2277
2299
  hem_column = self._get_hem_column(self.fit_search_keys)
2278
2300
  email_converted_to_hem = False
@@ -2853,6 +2875,25 @@ class FeaturesEnricher(TransformerMixin):
2853
2875
  if t in [SearchKey.DATE, SearchKey.DATETIME]:
2854
2876
  return col
2855
2877
 
2878
+ @staticmethod
2879
+ def _add_current_date_as_key(
2880
+ df: pd.DataFrame, search_keys: Dict[str, SearchKey], logger: logging.Logger, bundle: ResourceBundle
2881
+ ) -> pd.DataFrame:
2882
+ if (
2883
+ set(search_keys.values()) == {SearchKey.PHONE}
2884
+ or set(search_keys.values()) == {SearchKey.EMAIL}
2885
+ or set(search_keys.values()) == {SearchKey.HEM}
2886
+ or set(search_keys.values()) == {SearchKey.COUNTRY, SearchKey.POSTAL_CODE}
2887
+ ):
2888
+ msg = bundle.get("current_date_added")
2889
+ print(msg)
2890
+ logger.warning(msg)
2891
+ df[FeaturesEnricher.CURRENT_DATE] = datetime.date.today()
2892
+ search_keys[FeaturesEnricher.CURRENT_DATE] = SearchKey.DATE
2893
+ converter = DateTimeSearchKeyConverter(FeaturesEnricher.CURRENT_DATE, None, logger, bundle)
2894
+ df = converter.convert(df)
2895
+ return df
2896
+
2856
2897
  @staticmethod
2857
2898
  def _get_group_columns(df: pd.DataFrame, search_keys: Dict[str, SearchKey]) -> List[str]:
2858
2899
  return [
@@ -2903,9 +2944,7 @@ class FeaturesEnricher(TransformerMixin):
2903
2944
  [
2904
2945
  c
2905
2946
  for c in df.columns
2906
- if c not in sort_columns
2907
- and c not in sort_exclude_columns
2908
- and df[c].nunique() > 1
2947
+ if c not in sort_columns and c not in sort_exclude_columns and df[c].nunique() > 1
2909
2948
  ]
2910
2949
  # [
2911
2950
  # sk
@@ -0,0 +1,8 @@
1
+ /**
2
+ * FingerprintJS v3.4.2 - Copyright (c) FingerprintJS, Inc, 2023 (https://fingerprint.com)
3
+ * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
4
+ *
5
+ * This software contains code from open-source projects:
6
+ * MurmurHash3 by Karan Lyons (https://github.com/karanlyons/murmurHash3.js)
7
+ */
8
+ var e=function(){return e=Object.assign||function(e){for(var n,t=1,r=arguments.length;t<r;t++)for(var o in n=arguments[t])Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o]);return e},e.apply(this,arguments)};function n(e,n,t,r){return new(t||(t=Promise))((function(o,a){function i(e){try{u(r.next(e))}catch(n){a(n)}}function c(e){try{u(r.throw(e))}catch(n){a(n)}}function u(e){var n;e.done?o(e.value):(n=e.value,n instanceof t?n:new t((function(e){e(n)}))).then(i,c)}u((r=r.apply(e,n||[])).next())}))}function t(e,n){var t,r,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function c(c){return function(u){return function(c){if(t)throw new TypeError("Generator is already executing.");for(;a&&(a=0,c[0]&&(i=0)),i;)try{if(t=1,r&&(o=2&c[0]?r.return:c[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,c[1])).done)return o;switch(r=0,o&&(c=[2&c[0],o.value]),c[0]){case 0:case 1:o=c;break;case 4:return i.label++,{value:c[1],done:!1};case 5:i.label++,r=c[1],c=[0];continue;case 7:c=i.ops.pop(),i.trys.pop();continue;default:if(!(o=i.trys,(o=o.length>0&&o[o.length-1])||6!==c[0]&&2!==c[0])){i=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]<o[3])){i.label=c[1];break}if(6===c[0]&&i.label<o[1]){i.label=o[1],o=c;break}if(o&&i.label<o[2]){i.label=o[2],i.ops.push(c);break}o[2]&&i.ops.pop(),i.trys.pop();continue}c=n.call(e,i)}catch(u){c=[6,u],r=0}finally{t=o=0}if(5&c[0])throw c[1];return{value:c[0]?c[1]:void 0,done:!0}}([c,u])}}}function r(e,n,t){if(t||2===arguments.length)for(var r,o=0,a=n.length;o<a;o++)!r&&o in n||(r||(r=Array.prototype.slice.call(n,0,o)),r[o]=n[o]);return e.concat(r||Array.prototype.slice.call(n))}function o(e,n){return new Promise((function(t){return setTimeout(t,e,n)}))}function a(e){return!!e&&"function"==typeof e.then}function i(e,n){try{var t=e();a(t)?t.then((function(e){return n(!0,e)}),(function(e){return n(!1,e)})):n(!0,t)}catch(r){n(!1,r)}}function c(e,r,a){return void 0===a&&(a=16),n(this,void 0,void 0,(function(){var n,i,c,u;return t(this,(function(t){switch(t.label){case 0:n=Array(e.length),i=Date.now(),c=0,t.label=1;case 1:return c<e.length?(n[c]=r(e[c],c),(u=Date.now())>=i+a?(i=u,[4,o(0)]):[3,3]):[3,4];case 2:t.sent(),t.label=3;case 3:return++c,[3,1];case 4:return[2,n]}}))}))}function u(e){e.then(void 0,(function(){}))}function l(e,n){e=[e[0]>>>16,65535&e[0],e[1]>>>16,65535&e[1]],n=[n[0]>>>16,65535&n[0],n[1]>>>16,65535&n[1]];var t=[0,0,0,0];return t[3]+=e[3]+n[3],t[2]+=t[3]>>>16,t[3]&=65535,t[2]+=e[2]+n[2],t[1]+=t[2]>>>16,t[2]&=65535,t[1]+=e[1]+n[1],t[0]+=t[1]>>>16,t[1]&=65535,t[0]+=e[0]+n[0],t[0]&=65535,[t[0]<<16|t[1],t[2]<<16|t[3]]}function s(e,n){e=[e[0]>>>16,65535&e[0],e[1]>>>16,65535&e[1]],n=[n[0]>>>16,65535&n[0],n[1]>>>16,65535&n[1]];var t=[0,0,0,0];return t[3]+=e[3]*n[3],t[2]+=t[3]>>>16,t[3]&=65535,t[2]+=e[2]*n[3],t[1]+=t[2]>>>16,t[2]&=65535,t[2]+=e[3]*n[2],t[1]+=t[2]>>>16,t[2]&=65535,t[1]+=e[1]*n[3],t[0]+=t[1]>>>16,t[1]&=65535,t[1]+=e[2]*n[2],t[0]+=t[1]>>>16,t[1]&=65535,t[1]+=e[3]*n[1],t[0]+=t[1]>>>16,t[1]&=65535,t[0]+=e[0]*n[3]+e[1]*n[2]+e[2]*n[1]+e[3]*n[0],t[0]&=65535,[t[0]<<16|t[1],t[2]<<16|t[3]]}function d(e,n){return 32===(n%=64)?[e[1],e[0]]:n<32?[e[0]<<n|e[1]>>>32-n,e[1]<<n|e[0]>>>32-n]:(n-=32,[e[1]<<n|e[0]>>>32-n,e[0]<<n|e[1]>>>32-n])}function m(e,n){return 0===(n%=64)?e:n<32?[e[0]<<n|e[1]>>>32-n,e[1]<<n]:[e[1]<<n-32,0]}function f(e,n){return[e[0]^n[0],e[1]^n[1]]}function v(e){return e=f(e,[0,e[0]>>>1]),e=f(e=s(e,[4283543511,3981806797]),[0,e[0]>>>1]),e=f(e=s(e,[3301882366,444984403]),[0,e[0]>>>1])}function h(e,n){n=n||0;var t,r=(e=e||"").length%16,o=e.length-r,a=[0,n],i=[0,n],c=[0,0],u=[0,0],h=[2277735313,289559509],p=[1291169091,658871167];for(t=0;t<o;t+=16)c=[255&e.charCodeAt(t+4)|(255&e.charCodeAt(t+5))<<8|(255&e.charCodeAt(t+6))<<16|(255&e.charCodeAt(t+7))<<24,255&e.charCodeAt(t)|(255&e.charCodeAt(t+1))<<8|(255&e.charCodeAt(t+2))<<16|(255&e.charCodeAt(t+3))<<24],u=[255&e.charCodeAt(t+12)|(255&e.charCodeAt(t+13))<<8|(255&e.charCodeAt(t+14))<<16|(255&e.charCodeAt(t+15))<<24,255&e.charCodeAt(t+8)|(255&e.charCodeAt(t+9))<<8|(255&e.charCodeAt(t+10))<<16|(255&e.charCodeAt(t+11))<<24],c=d(c=s(c,h),31),a=l(a=d(a=f(a,c=s(c,p)),27),i),a=l(s(a,[0,5]),[0,1390208809]),u=d(u=s(u,p),33),i=l(i=d(i=f(i,u=s(u,h)),31),a),i=l(s(i,[0,5]),[0,944331445]);switch(c=[0,0],u=[0,0],r){case 15:u=f(u,m([0,e.charCodeAt(t+14)],48));case 14:u=f(u,m([0,e.charCodeAt(t+13)],40));case 13:u=f(u,m([0,e.charCodeAt(t+12)],32));case 12:u=f(u,m([0,e.charCodeAt(t+11)],24));case 11:u=f(u,m([0,e.charCodeAt(t+10)],16));case 10:u=f(u,m([0,e.charCodeAt(t+9)],8));case 9:u=s(u=f(u,[0,e.charCodeAt(t+8)]),p),i=f(i,u=s(u=d(u,33),h));case 8:c=f(c,m([0,e.charCodeAt(t+7)],56));case 7:c=f(c,m([0,e.charCodeAt(t+6)],48));case 6:c=f(c,m([0,e.charCodeAt(t+5)],40));case 5:c=f(c,m([0,e.charCodeAt(t+4)],32));case 4:c=f(c,m([0,e.charCodeAt(t+3)],24));case 3:c=f(c,m([0,e.charCodeAt(t+2)],16));case 2:c=f(c,m([0,e.charCodeAt(t+1)],8));case 1:c=s(c=f(c,[0,e.charCodeAt(t)]),h),a=f(a,c=s(c=d(c,31),p))}return a=l(a=f(a,[0,e.length]),i=f(i,[0,e.length])),i=l(i,a),a=l(a=v(a),i=v(i)),i=l(i,a),("00000000"+(a[0]>>>0).toString(16)).slice(-8)+("00000000"+(a[1]>>>0).toString(16)).slice(-8)+("00000000"+(i[0]>>>0).toString(16)).slice(-8)+("00000000"+(i[1]>>>0).toString(16)).slice(-8)}function p(e){return parseInt(e)}function b(e){return parseFloat(e)}function y(e,n){return"number"==typeof e&&isNaN(e)?n:e}function g(e){return e.reduce((function(e,n){return e+(n?1:0)}),0)}function w(e,n){if(void 0===n&&(n=1),Math.abs(n)>=1)return Math.round(e/n)*n;var t=1/n;return Math.round(e*t)/t}function L(e){return e&&"object"==typeof e&&"message"in e?e:{message:e}}function k(e){return"function"!=typeof e}function V(e,r,o){var a=Object.keys(e).filter((function(e){return!function(e,n){for(var t=0,r=e.length;t<r;++t)if(e[t]===n)return!0;return!1}(o,e)})),l=c(a,(function(n){return function(e,n){var t=new Promise((function(t){var r=Date.now();i(e.bind(null,n),(function(){for(var e=[],n=0;n<arguments.length;n++)e[n]=arguments[n];var o=Date.now()-r;if(!e[0])return t((function(){return{error:L(e[1]),duration:o}}));var a=e[1];if(k(a))return t((function(){return{value:a,duration:o}}));t((function(){return new Promise((function(e){var n=Date.now();i(a,(function(){for(var t=[],r=0;r<arguments.length;r++)t[r]=arguments[r];var a=o+Date.now()-n;if(!t[0])return e({error:L(t[1]),duration:a});e({value:t[1],duration:a})}))}))}))}))}));return u(t),function(){return t.then((function(e){return e()}))}}(e[n],r)}));return u(l),function(){return n(this,void 0,void 0,(function(){var e,n,r,o;return t(this,(function(t){switch(t.label){case 0:return[4,l];case 1:return[4,c(t.sent(),(function(e){var n=e();return u(n),n}))];case 2:return e=t.sent(),[4,Promise.all(e)];case 3:for(n=t.sent(),r={},o=0;o<a.length;++o)r[a[o]]=n[o];return[2,r]}}))}))}}function Z(e,n){var t=function(e){return k(e)?n(e):function(){var t=e();return a(t)?t.then(n):n(t)}};return function(n){var r=e(n);return a(r)?r.then(t):t(r)}}function W(){var e=window,n=navigator;return g(["MSCSSMatrix"in e,"msSetImmediate"in e,"msIndexedDB"in e,"msMaxTouchPoints"in n,"msPointerEnabled"in n])>=4}function C(){var e=window,n=navigator;return g(["msWriteProfilerMark"in e,"MSStream"in e,"msLaunchUri"in n,"msSaveBlob"in n])>=3&&!W()}function S(){var e=window,n=navigator;return g(["webkitPersistentStorage"in n,"webkitTemporaryStorage"in n,0===n.vendor.indexOf("Google"),"webkitResolveLocalFileSystemURL"in e,"BatteryManager"in e,"webkitMediaStream"in e,"webkitSpeechGrammar"in e])>=5}function x(){var e=window,n=navigator;return g(["ApplePayError"in e,"CSSPrimitiveValue"in e,"Counter"in e,0===n.vendor.indexOf("Apple"),"getStorageUpdates"in n,"WebKitMediaKeys"in e])>=4}function F(){var e=window;return g(["safari"in e,!("DeviceMotionEvent"in e),!("ongestureend"in e),!("standalone"in navigator)])>=3}function Y(){var e,n,t=window;return g(["buildID"in navigator,"MozAppearance"in(null!==(n=null===(e=document.documentElement)||void 0===e?void 0:e.style)&&void 0!==n?n:{}),"onmozfullscreenchange"in t,"mozInnerScreenX"in t,"CSSMozDocumentRule"in t,"CanvasCaptureMediaStream"in t])>=4}function M(){var e=document;return e.fullscreenElement||e.msFullscreenElement||e.mozFullScreenElement||e.webkitFullscreenElement||null}function G(){var e=S(),n=Y();if(!e&&!n)return!1;var t=window;return g(["onorientationchange"in t,"orientation"in t,e&&!("SharedWorker"in t),n&&/android/i.test(navigator.appVersion)])>=2}function R(e){var n=new Error(e);return n.name=e,n}function X(e,r,a){var i,c,u;return void 0===a&&(a=50),n(this,void 0,void 0,(function(){var n,l;return t(this,(function(t){switch(t.label){case 0:n=document,t.label=1;case 1:return n.body?[3,3]:[4,o(a)];case 2:return t.sent(),[3,1];case 3:l=n.createElement("iframe"),t.label=4;case 4:return t.trys.push([4,,10,11]),[4,new Promise((function(e,t){var o=!1,a=function(){o=!0,e()};l.onload=a,l.onerror=function(e){o=!0,t(e)};var i=l.style;i.setProperty("display","block","important"),i.position="absolute",i.top="0",i.left="0",i.visibility="hidden",r&&"srcdoc"in l?l.srcdoc=r:l.src="about:blank",n.body.appendChild(l);var c=function(){var e,n;o||("complete"===(null===(n=null===(e=l.contentWindow)||void 0===e?void 0:e.document)||void 0===n?void 0:n.readyState)?a():setTimeout(c,10))};c()}))];case 5:t.sent(),t.label=6;case 6:return(null===(c=null===(i=l.contentWindow)||void 0===i?void 0:i.document)||void 0===c?void 0:c.body)?[3,8]:[4,o(a)];case 7:return t.sent(),[3,6];case 8:return[4,e(l,l.contentWindow)];case 9:return[2,t.sent()];case 10:return null===(u=l.parentNode)||void 0===u||u.removeChild(l),[7];case 11:return[2]}}))}))}function A(e){for(var n=function(e){for(var n,t,r="Unexpected syntax '".concat(e,"'"),o=/^\s*([a-z-]*)(.*)$/i.exec(e),a=o[1]||void 0,i={},c=/([.:#][\w-]+|\[.+?\])/gi,u=function(e,n){i[e]=i[e]||[],i[e].push(n)};;){var l=c.exec(o[2]);if(!l)break;var s=l[0];switch(s[0]){case".":u("class",s.slice(1));break;case"#":u("id",s.slice(1));break;case"[":var d=/^\[([\w-]+)([~|^$*]?=("(.*?)"|([\w-]+)))?(\s+[is])?\]$/.exec(s);if(!d)throw new Error(r);u(d[1],null!==(t=null!==(n=d[4])&&void 0!==n?n:d[5])&&void 0!==t?t:"");break;default:throw new Error(r)}}return[a,i]}(e),t=n[0],r=n[1],o=document.createElement(null!=t?t:"div"),a=0,i=Object.keys(r);a<i.length;a++){var c=i[a],u=r[c].join(" ");"style"===c?j(o.style,u):o.setAttribute(c,u)}return o}function j(e,n){for(var t=0,r=n.split(";");t<r.length;t++){var o=r[t],a=/^\s*([\w-]+)\s*:\s*(.+?)(\s*!([\w-]+))?\s*$/.exec(o);if(a){var i=a[1],c=a[2],u=a[4];e.setProperty(i,c,u||"")}}}var I=["monospace","sans-serif","serif"],J=["sans-serif-thin","ARNO PRO","Agency FB","Arabic Typesetting","Arial Unicode MS","AvantGarde Bk BT","BankGothic Md BT","Batang","Bitstream Vera Sans Mono","Calibri","Century","Century Gothic","Clarendon","EUROSTILE","Franklin Gothic","Futura Bk BT","Futura Md BT","GOTHAM","Gill Sans","HELV","Haettenschweiler","Helvetica Neue","Humanst521 BT","Leelawadee","Letter Gothic","Levenim MT","Lucida Bright","Lucida Sans","Menlo","MS Mincho","MS Outlook","MS Reference Specialty","MS UI Gothic","MT Extra","MYRIAD PRO","Marlett","Meiryo UI","Microsoft Uighur","Minion Pro","Monotype Corsiva","PMingLiU","Pristina","SCRIPTINA","Segoe UI Light","Serifa","SimHei","Small Fonts","Staccato222 BT","TRAJAN PRO","Univers CE 55 Medium","Vrinda","ZWAdobeF"];function H(e){return e.toDataURL()}var P,N;function z(){var e=this;return function(){if(void 0===N){var e=function(){var n=D();E(n)?N=setTimeout(e,2500):(P=n,N=void 0)};e()}}(),function(){return n(e,void 0,void 0,(function(){var e;return t(this,(function(n){switch(n.label){case 0:return E(e=D())?P?[2,r([],P,!0)]:M()?[4,(t=document,(t.exitFullscreen||t.msExitFullscreen||t.mozCancelFullScreen||t.webkitExitFullscreen).call(t))]:[3,2]:[3,2];case 1:n.sent(),e=D(),n.label=2;case 2:return E(e)||(P=e),[2,e]}var t}))}))}}function D(){var e=screen;return[y(b(e.availTop),null),y(b(e.width)-b(e.availWidth)-y(b(e.availLeft),0),null),y(b(e.height)-b(e.availHeight)-y(b(e.availTop),0),null),y(b(e.availLeft),null)]}function E(e){for(var n=0;n<4;++n)if(e[n])return!1;return!0}function T(e){var r;return n(this,void 0,void 0,(function(){var n,a,i,c,u,l,s;return t(this,(function(t){switch(t.label){case 0:for(n=document,a=n.createElement("div"),i=new Array(e.length),c={},B(a),s=0;s<e.length;++s)"DIALOG"===(u=A(e[s])).tagName&&u.show(),B(l=n.createElement("div")),l.appendChild(u),a.appendChild(l),i[s]=u;t.label=1;case 1:return n.body?[3,3]:[4,o(50)];case 2:return t.sent(),[3,1];case 3:n.body.appendChild(a);try{for(s=0;s<e.length;++s)i[s].offsetParent||(c[e[s]]=!0)}finally{null===(r=a.parentNode)||void 0===r||r.removeChild(a)}return[2,c]}}))}))}function B(e){e.style.setProperty("display","block","important")}function _(e){return matchMedia("(inverted-colors: ".concat(e,")")).matches}function O(e){return matchMedia("(forced-colors: ".concat(e,")")).matches}function U(e){return matchMedia("(prefers-contrast: ".concat(e,")")).matches}function Q(e){return matchMedia("(prefers-reduced-motion: ".concat(e,")")).matches}function K(e){return matchMedia("(dynamic-range: ".concat(e,")")).matches}var q=Math,$=function(){return 0};var ee={default:[],apple:[{font:"-apple-system-body"}],serif:[{fontFamily:"serif"}],sans:[{fontFamily:"sans-serif"}],mono:[{fontFamily:"monospace"}],min:[{fontSize:"1px"}],system:[{fontFamily:"system-ui"}]};var ne={fonts:function(){return X((function(e,n){var t=n.document,r=t.body;r.style.fontSize="48px";var o=t.createElement("div"),a={},i={},c=function(e){var n=t.createElement("span"),r=n.style;return r.position="absolute",r.top="0",r.left="0",r.fontFamily=e,n.textContent="mmMwWLliI0O&1",o.appendChild(n),n},u=I.map(c),l=function(){for(var e={},n=function(n){e[n]=I.map((function(e){return function(e,n){return c("'".concat(e,"',").concat(n))}(n,e)}))},t=0,r=J;t<r.length;t++){n(r[t])}return e}();r.appendChild(o);for(var s=0;s<I.length;s++)a[I[s]]=u[s].offsetWidth,i[I[s]]=u[s].offsetHeight;return J.filter((function(e){return n=l[e],I.some((function(e,t){return n[t].offsetWidth!==a[e]||n[t].offsetHeight!==i[e]}));var n}))}))},domBlockers:function(e){var r=(void 0===e?{}:e).debug;return n(this,void 0,void 0,(function(){var e,n,o,a,i;return t(this,(function(t){switch(t.label){case 0:return x()||G()?(c=atob,e={abpIndo:["#Iklan-Melayang","#Kolom-Iklan-728","#SidebarIklan-wrapper",'[title="ALIENBOLA" i]',c("I0JveC1CYW5uZXItYWRz")],abpvn:[".quangcao","#mobileCatfish",c("LmNsb3NlLWFkcw=="),'[id^="bn_bottom_fixed_"]',"#pmadv"],adBlockFinland:[".mainostila",c("LnNwb25zb3JpdA=="),".ylamainos",c("YVtocmVmKj0iL2NsaWNrdGhyZ2guYXNwPyJd"),c("YVtocmVmXj0iaHR0cHM6Ly9hcHAucmVhZHBlYWsuY29tL2FkcyJd")],adBlockPersian:["#navbar_notice_50",".kadr",'TABLE[width="140px"]',"#divAgahi",c("YVtocmVmXj0iaHR0cDovL2cxLnYuZndtcm0ubmV0L2FkLyJd")],adBlockWarningRemoval:["#adblock-honeypot",".adblocker-root",".wp_adblock_detect",c("LmhlYWRlci1ibG9ja2VkLWFk"),c("I2FkX2Jsb2NrZXI=")],adGuardAnnoyances:[".hs-sosyal","#cookieconsentdiv",'div[class^="app_gdpr"]',".as-oil",'[data-cypress="soft-push-notification-modal"]'],adGuardBase:[".BetterJsPopOverlay",c("I2FkXzMwMFgyNTA="),c("I2Jhbm5lcmZsb2F0MjI="),c("I2NhbXBhaWduLWJhbm5lcg=="),c("I0FkLUNvbnRlbnQ=")],adGuardChinese:[c("LlppX2FkX2FfSA=="),c("YVtocmVmKj0iLmh0aGJldDM0LmNvbSJd"),"#widget-quan",c("YVtocmVmKj0iLzg0OTkyMDIwLnh5eiJd"),c("YVtocmVmKj0iLjE5NTZobC5jb20vIl0=")],adGuardFrench:["#pavePub",c("LmFkLWRlc2t0b3AtcmVjdGFuZ2xl"),".mobile_adhesion",".widgetadv",c("LmFkc19iYW4=")],adGuardGerman:['aside[data-portal-id="leaderboard"]'],adGuardJapanese:["#kauli_yad_1",c("YVtocmVmXj0iaHR0cDovL2FkMi50cmFmZmljZ2F0ZS5uZXQvIl0="),c("Ll9wb3BJbl9pbmZpbml0ZV9hZA=="),c("LmFkZ29vZ2xl"),c("Ll9faXNib29zdFJldHVybkFk")],adGuardMobile:[c("YW1wLWF1dG8tYWRz"),c("LmFtcF9hZA=="),'amp-embed[type="24smi"]',"#mgid_iframe1",c("I2FkX2ludmlld19hcmVh")],adGuardRussian:[c("YVtocmVmXj0iaHR0cHM6Ly9hZC5sZXRtZWFkcy5jb20vIl0="),c("LnJlY2xhbWE="),'div[id^="smi2adblock"]',c("ZGl2W2lkXj0iQWRGb3hfYmFubmVyXyJd"),"#psyduckpockeball"],adGuardSocial:[c("YVtocmVmXj0iLy93d3cuc3R1bWJsZXVwb24uY29tL3N1Ym1pdD91cmw9Il0="),c("YVtocmVmXj0iLy90ZWxlZ3JhbS5tZS9zaGFyZS91cmw/Il0="),".etsy-tweet","#inlineShare",".popup-social"],adGuardSpanishPortuguese:["#barraPublicidade","#Publicidade","#publiEspecial","#queTooltip",".cnt-publi"],adGuardTrackingProtection:["#qoo-counter",c("YVtocmVmXj0iaHR0cDovL2NsaWNrLmhvdGxvZy5ydS8iXQ=="),c("YVtocmVmXj0iaHR0cDovL2hpdGNvdW50ZXIucnUvdG9wL3N0YXQucGhwIl0="),c("YVtocmVmXj0iaHR0cDovL3RvcC5tYWlsLnJ1L2p1bXAiXQ=="),"#top100counter"],adGuardTurkish:["#backkapat",c("I3Jla2xhbWk="),c("YVtocmVmXj0iaHR0cDovL2Fkc2Vydi5vbnRlay5jb20udHIvIl0="),c("YVtocmVmXj0iaHR0cDovL2l6bGVuemkuY29tL2NhbXBhaWduLyJd"),c("YVtocmVmXj0iaHR0cDovL3d3dy5pbnN0YWxsYWRzLm5ldC8iXQ==")],bulgarian:[c("dGQjZnJlZW5ldF90YWJsZV9hZHM="),"#ea_intext_div",".lapni-pop-over","#xenium_hot_offers"],easyList:[".yb-floorad",c("LndpZGdldF9wb19hZHNfd2lkZ2V0"),c("LnRyYWZmaWNqdW5reS1hZA=="),".textad_headline",c("LnNwb25zb3JlZC10ZXh0LWxpbmtz")],easyListChina:[c("LmFwcGd1aWRlLXdyYXBbb25jbGljayo9ImJjZWJvcy5jb20iXQ=="),c("LmZyb250cGFnZUFkdk0="),"#taotaole","#aafoot.top_box",".cfa_popup"],easyListCookie:[".ezmob-footer",".cc-CookieWarning","[data-cookie-number]",c("LmF3LWNvb2tpZS1iYW5uZXI="),".sygnal24-gdpr-modal-wrap"],easyListCzechSlovak:["#onlajny-stickers",c("I3Jla2xhbW5pLWJveA=="),c("LnJla2xhbWEtbWVnYWJvYXJk"),".sklik",c("W2lkXj0ic2tsaWtSZWtsYW1hIl0=")],easyListDutch:[c("I2FkdmVydGVudGll"),c("I3ZpcEFkbWFya3RCYW5uZXJCbG9jaw=="),".adstekst",c("YVtocmVmXj0iaHR0cHM6Ly94bHR1YmUubmwvY2xpY2svIl0="),"#semilo-lrectangle"],easyListGermany:["#SSpotIMPopSlider",c("LnNwb25zb3JsaW5rZ3J1ZW4="),c("I3dlcmJ1bmdza3k="),c("I3Jla2xhbWUtcmVjaHRzLW1pdHRl"),c("YVtocmVmXj0iaHR0cHM6Ly9iZDc0Mi5jb20vIl0=")],easyListItaly:[c("LmJveF9hZHZfYW5udW5jaQ=="),".sb-box-pubbliredazionale",c("YVtocmVmXj0iaHR0cDovL2FmZmlsaWF6aW9uaWFkcy5zbmFpLml0LyJd"),c("YVtocmVmXj0iaHR0cHM6Ly9hZHNlcnZlci5odG1sLml0LyJd"),c("YVtocmVmXj0iaHR0cHM6Ly9hZmZpbGlhemlvbmlhZHMuc25haS5pdC8iXQ==")],easyListLithuania:[c("LnJla2xhbW9zX3RhcnBhcw=="),c("LnJla2xhbW9zX251b3JvZG9z"),c("aW1nW2FsdD0iUmVrbGFtaW5pcyBza3lkZWxpcyJd"),c("aW1nW2FsdD0iRGVkaWt1b3RpLmx0IHNlcnZlcmlhaSJd"),c("aW1nW2FsdD0iSG9zdGluZ2FzIFNlcnZlcmlhaS5sdCJd")],estonian:[c("QVtocmVmKj0iaHR0cDovL3BheTRyZXN1bHRzMjQuZXUiXQ==")],fanboyAnnoyances:["#ac-lre-player",".navigate-to-top","#subscribe_popup",".newsletter_holder","#back-top"],fanboyAntiFacebook:[".util-bar-module-firefly-visible"],fanboyEnhancedTrackers:[".open.pushModal","#issuem-leaky-paywall-articles-zero-remaining-nag","#sovrn_container",'div[class$="-hide"][zoompage-fontsize][style="display: block;"]',".BlockNag__Card"],fanboySocial:["#FollowUs","#meteored_share","#social_follow",".article-sharer",".community__social-desc"],frellwitSwedish:[c("YVtocmVmKj0iY2FzaW5vcHJvLnNlIl1bdGFyZ2V0PSJfYmxhbmsiXQ=="),c("YVtocmVmKj0iZG9rdG9yLXNlLm9uZWxpbmsubWUiXQ=="),"article.category-samarbete",c("ZGl2LmhvbGlkQWRz"),"ul.adsmodern"],greekAdBlock:[c("QVtocmVmKj0iYWRtYW4ub3RlbmV0LmdyL2NsaWNrPyJd"),c("QVtocmVmKj0iaHR0cDovL2F4aWFiYW5uZXJzLmV4b2R1cy5nci8iXQ=="),c("QVtocmVmKj0iaHR0cDovL2ludGVyYWN0aXZlLmZvcnRobmV0LmdyL2NsaWNrPyJd"),"DIV.agores300","TABLE.advright"],hungarian:["#cemp_doboz",".optimonk-iframe-container",c("LmFkX19tYWlu"),c("W2NsYXNzKj0iR29vZ2xlQWRzIl0="),"#hirdetesek_box"],iDontCareAboutCookies:['.alert-info[data-block-track*="CookieNotice"]',".ModuleTemplateCookieIndicator",".o--cookies--container","#cookies-policy-sticky","#stickyCookieBar"],icelandicAbp:[c("QVtocmVmXj0iL2ZyYW1ld29yay9yZXNvdXJjZXMvZm9ybXMvYWRzLmFzcHgiXQ==")],latvian:[c("YVtocmVmPSJodHRwOi8vd3d3LnNhbGlkemluaS5sdi8iXVtzdHlsZT0iZGlzcGxheTogYmxvY2s7IHdpZHRoOiAxMjBweDsgaGVpZ2h0OiA0MHB4OyBvdmVyZmxvdzogaGlkZGVuOyBwb3NpdGlvbjogcmVsYXRpdmU7Il0="),c("YVtocmVmPSJodHRwOi8vd3d3LnNhbGlkemluaS5sdi8iXVtzdHlsZT0iZGlzcGxheTogYmxvY2s7IHdpZHRoOiA4OHB4OyBoZWlnaHQ6IDMxcHg7IG92ZXJmbG93OiBoaWRkZW47IHBvc2l0aW9uOiByZWxhdGl2ZTsiXQ==")],listKr:[c("YVtocmVmKj0iLy9hZC5wbGFuYnBsdXMuY28ua3IvIl0="),c("I2xpdmVyZUFkV3JhcHBlcg=="),c("YVtocmVmKj0iLy9hZHYuaW1hZHJlcC5jby5rci8iXQ=="),c("aW5zLmZhc3R2aWV3LWFk"),".revenue_unit_item.dable"],listeAr:[c("LmdlbWluaUxCMUFk"),".right-and-left-sponsers",c("YVtocmVmKj0iLmFmbGFtLmluZm8iXQ=="),c("YVtocmVmKj0iYm9vcmFxLm9yZyJd"),c("YVtocmVmKj0iZHViaXp6bGUuY29tL2FyLz91dG1fc291cmNlPSJd")],listeFr:[c("YVtocmVmXj0iaHR0cDovL3Byb21vLnZhZG9yLmNvbS8iXQ=="),c("I2FkY29udGFpbmVyX3JlY2hlcmNoZQ=="),c("YVtocmVmKj0id2Vib3JhbWEuZnIvZmNnaS1iaW4vIl0="),".site-pub-interstitiel",'div[id^="crt-"][data-criteo-id]'],officialPolish:["#ceneo-placeholder-ceneo-12",c("W2hyZWZePSJodHRwczovL2FmZi5zZW5kaHViLnBsLyJd"),c("YVtocmVmXj0iaHR0cDovL2Fkdm1hbmFnZXIudGVjaGZ1bi5wbC9yZWRpcmVjdC8iXQ=="),c("YVtocmVmXj0iaHR0cDovL3d3dy50cml6ZXIucGwvP3V0bV9zb3VyY2UiXQ=="),c("ZGl2I3NrYXBpZWNfYWQ=")],ro:[c("YVtocmVmXj0iLy9hZmZ0cmsuYWx0ZXgucm8vQ291bnRlci9DbGljayJd"),c("YVtocmVmXj0iaHR0cHM6Ly9ibGFja2ZyaWRheXNhbGVzLnJvL3Ryay9zaG9wLyJd"),c("YVtocmVmXj0iaHR0cHM6Ly9ldmVudC4ycGVyZm9ybWFudC5jb20vZXZlbnRzL2NsaWNrIl0="),c("YVtocmVmXj0iaHR0cHM6Ly9sLnByb2ZpdHNoYXJlLnJvLyJd"),'a[href^="/url/"]'],ruAd:[c("YVtocmVmKj0iLy9mZWJyYXJlLnJ1LyJd"),c("YVtocmVmKj0iLy91dGltZy5ydS8iXQ=="),c("YVtocmVmKj0iOi8vY2hpa2lkaWtpLnJ1Il0="),"#pgeldiz",".yandex-rtb-block"],thaiAds:["a[href*=macau-uta-popup]",c("I2Fkcy1nb29nbGUtbWlkZGxlX3JlY3RhbmdsZS1ncm91cA=="),c("LmFkczMwMHM="),".bumq",".img-kosana"],webAnnoyancesUltralist:["#mod-social-share-2","#social-tools",c("LmN0cGwtZnVsbGJhbm5lcg=="),".zergnet-recommend",".yt.btn-link.btn-md.btn"]},n=Object.keys(e),[4,T((i=[]).concat.apply(i,n.map((function(n){return e[n]}))))]):[2,void 0];case 1:return o=t.sent(),r&&function(e,n){for(var t="DOM blockers debug:\n```",r=0,o=Object.keys(e);r<o.length;r++){var a=o[r];t+="\n".concat(a,":");for(var i=0,c=e[a];i<c.length;i++){var u=c[i];t+="\n ".concat(n[u]?"🚫":"➡️"," ").concat(u)}}console.log("".concat(t,"\n```"))}(e,o),(a=n.filter((function(n){var t=e[n];return g(t.map((function(e){return o[e]})))>.6*t.length}))).sort(),[2,a]}var c}))}))},fontPreferences:function(){return function(e,n){void 0===n&&(n=4e3);return X((function(t,o){var a=o.document,i=a.body,c=i.style;c.width="".concat(n,"px"),c.webkitTextSizeAdjust=c.textSizeAdjust="none",S()?i.style.zoom="".concat(1/o.devicePixelRatio):x()&&(i.style.zoom="reset");var u=a.createElement("div");return u.textContent=r([],Array(n/20<<0),!0).map((function(){return"word"})).join(" "),i.appendChild(u),e(a,i)}),'<!doctype html><html><head><meta name="viewport" content="width=device-width, initial-scale=1">')}((function(e,n){for(var t={},r={},o=0,a=Object.keys(ee);o<a.length;o++){var i=a[o],c=ee[i],u=c[0],l=void 0===u?{}:u,s=c[1],d=void 0===s?"mmMwWLliI0fiflO&1":s,m=e.createElement("span");m.textContent=d,m.style.whiteSpace="nowrap";for(var f=0,v=Object.keys(l);f<v.length;f++){var h=v[f],p=l[h];void 0!==p&&(m.style[h]=p)}t[i]=m,n.appendChild(e.createElement("br")),n.appendChild(m)}for(var b=0,y=Object.keys(ee);b<y.length;b++){r[i=y[b]]=t[i].getBoundingClientRect().width}return r}))},audio:function(){var e=window,n=e.OfflineAudioContext||e.webkitOfflineAudioContext;if(!n)return-2;if(x()&&!F()&&!function(){var e=window;return g(["DOMRectList"in e,"RTCPeerConnectionIceEvent"in e,"SVGGeometryElement"in e,"ontransitioncancel"in e])>=3}())return-1;var t=new n(1,5e3,44100),r=t.createOscillator();r.type="triangle",r.frequency.value=1e4;var o=t.createDynamicsCompressor();o.threshold.value=-50,o.knee.value=40,o.ratio.value=12,o.attack.value=0,o.release.value=.25,r.connect(o),o.connect(t.destination),r.start(0);var i=function(e){var n=3,t=500,r=500,o=5e3,i=function(){};return[new Promise((function(c,l){var s=!1,d=0,m=0;e.oncomplete=function(e){return c(e.renderedBuffer)};var f=function(){setTimeout((function(){return l(R("timeout"))}),Math.min(r,m+o-Date.now()))},v=function(){try{var r=e.startRendering();switch(a(r)&&u(r),e.state){case"running":m=Date.now(),s&&f();break;case"suspended":document.hidden||d++,s&&d>=n?l(R("suspended")):setTimeout(v,t)}}catch(o){l(o)}};v(),i=function(){s||(s=!0,m>0&&f())}})),i]}(t),c=i[0],l=i[1],s=c.then((function(e){return function(e){for(var n=0,t=0;t<e.length;++t)n+=Math.abs(e[t]);return n}(e.getChannelData(0).subarray(4500))}),(function(e){if("timeout"===e.name||"suspended"===e.name)return-3;throw e}));return u(s),function(){return l(),s}},screenFrame:function(){var e=this,r=z();return function(){return n(e,void 0,void 0,(function(){var e,n;return t(this,(function(t){switch(t.label){case 0:return[4,r()];case 1:return e=t.sent(),[2,[(n=function(e){return null===e?null:w(e,10)})(e[0]),n(e[1]),n(e[2]),n(e[3])]]}}))}))}},osCpu:function(){return navigator.oscpu},languages:function(){var e,n=navigator,t=[],r=n.language||n.userLanguage||n.browserLanguage||n.systemLanguage;if(void 0!==r&&t.push([r]),Array.isArray(n.languages))S()&&g([!("MediaSettingsRange"in(e=window)),"RTCEncodedAudioFrame"in e,""+e.Intl=="[object Intl]",""+e.Reflect=="[object Reflect]"])>=3||t.push(n.languages);else if("string"==typeof n.languages){var o=n.languages;o&&t.push(o.split(","))}return t},colorDepth:function(){return window.screen.colorDepth},deviceMemory:function(){return y(b(navigator.deviceMemory),void 0)},screenResolution:function(){var e=screen,n=function(e){return y(p(e),null)},t=[n(e.width),n(e.height)];return t.sort().reverse(),t},hardwareConcurrency:function(){return y(p(navigator.hardwareConcurrency),void 0)},timezone:function(){var e,n=null===(e=window.Intl)||void 0===e?void 0:e.DateTimeFormat;if(n){var t=(new n).resolvedOptions().timeZone;if(t)return t}var r,o=(r=(new Date).getFullYear(),-Math.max(b(new Date(r,0,1).getTimezoneOffset()),b(new Date(r,6,1).getTimezoneOffset())));return"UTC".concat(o>=0?"+":"").concat(Math.abs(o))},sessionStorage:function(){try{return!!window.sessionStorage}catch(e){return!0}},localStorage:function(){try{return!!window.localStorage}catch(e){return!0}},indexedDB:function(){if(!W()&&!C())try{return!!window.indexedDB}catch(e){return!0}},openDatabase:function(){return!!window.openDatabase},cpuClass:function(){return navigator.cpuClass},platform:function(){var e=navigator.platform;return"MacIntel"===e&&x()&&!F()?function(){if("iPad"===navigator.platform)return!0;var e=screen,n=e.width/e.height;return g(["MediaSource"in window,!!Element.prototype.webkitRequestFullscreen,n>.65&&n<1.53])>=2}()?"iPad":"iPhone":e},plugins:function(){var e=navigator.plugins;if(e){for(var n=[],t=0;t<e.length;++t){var r=e[t];if(r){for(var o=[],a=0;a<r.length;++a){var i=r[a];o.push({type:i.type,suffixes:i.suffixes})}n.push({name:r.name,description:r.description,mimeTypes:o})}}return n}},canvas:function(){var e,n,t=!1,r=function(){var e=document.createElement("canvas");return e.width=1,e.height=1,[e,e.getContext("2d")]}(),o=r[0],a=r[1];if(function(e,n){return!(!n||!e.toDataURL)}(o,a)){t=function(e){return e.rect(0,0,10,10),e.rect(2,2,6,6),!e.isPointInPath(5,5,"evenodd")}(a),function(e,n){e.width=240,e.height=60,n.textBaseline="alphabetic",n.fillStyle="#f60",n.fillRect(100,1,62,20),n.fillStyle="#069",n.font='11pt "Times New Roman"';var t="Cwm fjordbank gly ".concat(String.fromCharCode(55357,56835));n.fillText(t,2,15),n.fillStyle="rgba(102, 204, 0, 0.2)",n.font="18pt Arial",n.fillText(t,4,45)}(o,a);var i=H(o);i!==H(o)?e=n="unstable":(n=i,function(e,n){e.width=122,e.height=110,n.globalCompositeOperation="multiply";for(var t=0,r=[["#f2f",40,40],["#2ff",80,40],["#ff2",60,80]];t<r.length;t++){var o=r[t],a=o[0],i=o[1],c=o[2];n.fillStyle=a,n.beginPath(),n.arc(i,c,40,0,2*Math.PI,!0),n.closePath(),n.fill()}n.fillStyle="#f9c",n.arc(60,60,60,0,2*Math.PI,!0),n.arc(60,60,20,0,2*Math.PI,!0),n.fill("evenodd")}(o,a),e=H(o))}else e=n="";return{winding:t,geometry:e,text:n}},touchSupport:function(){var e,n=navigator,t=0;void 0!==n.maxTouchPoints?t=p(n.maxTouchPoints):void 0!==n.msMaxTouchPoints&&(t=n.msMaxTouchPoints);try{document.createEvent("TouchEvent"),e=!0}catch(r){e=!1}return{maxTouchPoints:t,touchEvent:e,touchStart:"ontouchstart"in window}},vendor:function(){return navigator.vendor||""},vendorFlavors:function(){for(var e=[],n=0,t=["chrome","safari","__crWeb","__gCrWeb","yandex","__yb","__ybro","__firefox__","__edgeTrackingPreventionStatistics","webkit","oprt","samsungAr","ucweb","UCShellJava","puffinDevice"];n<t.length;n++){var r=t[n],o=window[r];o&&"object"==typeof o&&e.push(r)}return e.sort()},cookiesEnabled:function(){var e=document;try{e.cookie="cookietest=1; SameSite=Strict;";var n=-1!==e.cookie.indexOf("cookietest=");return e.cookie="cookietest=1; SameSite=Strict; expires=Thu, 01-Jan-1970 00:00:01 GMT",n}catch(t){return!1}},colorGamut:function(){for(var e=0,n=["rec2020","p3","srgb"];e<n.length;e++){var t=n[e];if(matchMedia("(color-gamut: ".concat(t,")")).matches)return t}},invertedColors:function(){return!!_("inverted")||!_("none")&&void 0},forcedColors:function(){return!!O("active")||!O("none")&&void 0},monochrome:function(){if(matchMedia("(min-monochrome: 0)").matches){for(var e=0;e<=100;++e)if(matchMedia("(max-monochrome: ".concat(e,")")).matches)return e;throw new Error("Too high value")}},contrast:function(){return U("no-preference")?0:U("high")||U("more")?1:U("low")||U("less")?-1:U("forced")?10:void 0},reducedMotion:function(){return!!Q("reduce")||!Q("no-preference")&&void 0},hdr:function(){return!!K("high")||!K("standard")&&void 0},math:function(){var e,n=q.acos||$,t=q.acosh||$,r=q.asin||$,o=q.asinh||$,a=q.atanh||$,i=q.atan||$,c=q.sin||$,u=q.sinh||$,l=q.cos||$,s=q.cosh||$,d=q.tan||$,m=q.tanh||$,f=q.exp||$,v=q.expm1||$,h=q.log1p||$;return{acos:n(.12312423423423424),acosh:t(1e308),acoshPf:(e=1e154,q.log(e+q.sqrt(e*e-1))),asin:r(.12312423423423424),asinh:o(1),asinhPf:function(e){return q.log(e+q.sqrt(e*e+1))}(1),atanh:a(.5),atanhPf:function(e){return q.log((1+e)/(1-e))/2}(.5),atan:i(.5),sin:c(-1e300),sinh:u(1),sinhPf:function(e){return q.exp(e)-1/q.exp(e)/2}(1),cos:l(10.000000000123),cosh:s(1),coshPf:function(e){return(q.exp(e)+1/q.exp(e))/2}(1),tan:d(-1e300),tanh:m(1),tanhPf:function(e){return(q.exp(2*e)-1)/(q.exp(2*e)+1)}(1),exp:f(1),expm1:v(1),expm1Pf:function(e){return q.exp(e)-1}(1),log1p:h(10),log1pPf:function(e){return q.log(1+e)}(10),powPI:function(e){return q.pow(q.PI,e)}(-100)}},videoCard:function(){var e,n=document.createElement("canvas"),t=null!==(e=n.getContext("webgl"))&&void 0!==e?e:n.getContext("experimental-webgl");if(t&&"getExtension"in t){var r=t.getExtension("WEBGL_debug_renderer_info");if(r)return{vendor:(t.getParameter(r.UNMASKED_VENDOR_WEBGL)||"").toString(),renderer:(t.getParameter(r.UNMASKED_RENDERER_WEBGL)||"").toString()}}},pdfViewerEnabled:function(){return navigator.pdfViewerEnabled},architecture:function(){var e=new Float32Array(1),n=new Uint8Array(e.buffer);return e[0]=1/0,e[0]=e[0]-e[0],n[3]}};function te(e){var n=function(e){if(G())return.4;if(x())return F()?.5:.3;var n=e.platform.value||"";if(/^Win/.test(n))return.6;if(/^Mac/.test(n))return.5;return.7}(e),t=function(e){return w(.99+.01*e,1e-4)}(n);return{score:n,comment:"$ if upgrade to Pro: https://fpjs.dev/pro".replace(/\$/g,"".concat(t))}}function re(n){return JSON.stringify(n,(function(n,t){return t instanceof Error?e({name:(r=t).name,message:r.message,stack:null===(o=r.stack)||void 0===o?void 0:o.split("\n")},r):t;var r,o}),2)}function oe(e){return h(function(e){for(var n="",t=0,r=Object.keys(e).sort();t<r.length;t++){var o=r[t],a=e[o],i=a.error?"error":JSON.stringify(a.value);n+="".concat(n?"|":"").concat(o.replace(/([:|\\])/g,"\\$1"),":").concat(i)}return n}(e))}function ae(e){return void 0===e&&(e=50),function(e,n){void 0===n&&(n=1/0);var t=window.requestIdleCallback;return t?new Promise((function(e){return t.call(window,(function(){return e()}),{timeout:n})})):o(Math.min(e,n))}(e,2*e)}function ie(e,r){var o=Date.now();return{get:function(a){return n(this,void 0,void 0,(function(){var n,i,c;return t(this,(function(t){switch(t.label){case 0:return n=Date.now(),[4,e()];case 1:return i=t.sent(),c=function(e){var n;return{get visitorId(){return void 0===n&&(n=oe(this.components)),n},set visitorId(e){n=e},confidence:te(e),components:e,version:"3.4.2"}}(i),(r||(null==a?void 0:a.debug))&&console.log("Copy the text below to get the debug data:\n\n```\nversion: ".concat(c.version,"\nuserAgent: ").concat(navigator.userAgent,"\ntimeBetweenLoadAndGet: ").concat(n-o,"\nvisitorId: ").concat(c.visitorId,"\ncomponents: ").concat(re(i),"\n```")),[2,c]}}))}))}}}function ce(e){var r=void 0===e?{}:e,o=r.delayFallback,a=r.debug;return r.monitoring,n(this,void 0,void 0,(function(){return t(this,(function(e){switch(e.label){case 0:return[4,ae(o)];case 1:return e.sent(),[2,ie(V(ne,{debug:a},[]),a)]}}))}))}var ue={load:ce,hashComponents:oe,componentsToDebugString:re},le=h;export{re as componentsToDebugString,ue as default,M as getFullscreenElement,z as getScreenFrame,oe as hashComponents,G as isAndroid,S as isChromium,F as isDesktopSafari,C as isEdgeHTML,Y as isGecko,W as isTrident,x as isWebKit,ce as load,V as loadSources,le as murmurX64Hash128,ae as prepareForSources,ne as sources,Z as transformSource,X as withIframe};
@@ -1,3 +1,4 @@
1
+ import inspect
1
2
  import logging
2
3
  import re
3
4
  from copy import deepcopy
@@ -381,6 +382,11 @@ class EstimatorWrapper:
381
382
  kwargs["estimator"] = estimator_copy
382
383
  if isinstance(estimator, CatBoostClassifier) or isinstance(estimator, CatBoostRegressor):
383
384
  if cat_features is not None:
385
+ for cat_feature in cat_features:
386
+ if cat_feature not in X.columns:
387
+ logger.error(
388
+ f"Client cat_feature `{cat_feature}` not found in X columns: {X.columns.to_list()}"
389
+ )
384
390
  estimator_copy.set_params(
385
391
  cat_features=[X.columns.get_loc(cat_feature) for cat_feature in cat_features]
386
392
  )
@@ -647,6 +653,12 @@ class OtherEstimatorWrapper(EstimatorWrapper):
647
653
  def validate_scoring_argument(scoring: Union[Callable, str, None]):
648
654
  if isinstance(scoring, str) and scoring is not None:
649
655
  _get_scorer_by_name(scoring)
656
+ elif isinstance(scoring, Callable):
657
+ spec = inspect.getfullargspec(scoring)
658
+ if len(spec.args) < 3:
659
+ raise ValidationError(
660
+ f"Invalid scoring function passed {scoring}. It should accept 3 input arguments: estimator, X, y"
661
+ )
650
662
 
651
663
 
652
664
  def _get_scorer_by_name(scoring: str) -> Tuple[Callable, str, int]:
@@ -1,7 +1,7 @@
1
1
  from typing import Optional
2
2
 
3
3
  import pandas as pd
4
- from pandas.api.types import is_float_dtype, is_int64_dtype, is_string_dtype
4
+ from pandas.api.types import is_float_dtype, is_int64_dtype, is_string_dtype, is_object_dtype
5
5
 
6
6
  from upgini.errors import ValidationError
7
7
 
@@ -44,7 +44,7 @@ class PhoneNormalizer:
44
44
  Method will remove all non numeric chars from string and convert it to int.
45
45
  None will be set for phone numbers that couldn"t be converted to int
46
46
  """
47
- if is_string_dtype(self.df[self.phone_column_name]):
47
+ if is_string_dtype(self.df[self.phone_column_name]) or is_object_dtype(self.df[self.phone_column_name]):
48
48
  convert_func = self.phone_str_to_int_safe
49
49
  elif is_float_dtype(self.df[self.phone_column_name]):
50
50
  convert_func = self.phone_float_to_int_safe
@@ -38,6 +38,7 @@ loss_selection_warn=\nWARNING: Loss `{0}` is not supported for feature selection
38
38
  loss_calc_metrics_warn=\nWARNING: Loss `{0}` is not supported for metrics calculation with {1}
39
39
  multivariate_timeseries_detected=\nWARNING: Multivariate TimeSeries detected. Blocked time series cross-validation split selected.\nMore details: https://github.com/upgini/upgini#-time-series-prediction-support
40
40
  group_k_fold_in_classification=\nWARNING: Using group K-fold cross-validation split for classification task.
41
+ current_date_added=\nWARNING: No date/datetime column was detected in X to be used as a search key. The current date will be used to match the latest version of data sources
41
42
 
42
43
  # Errors
43
44
  failed_search_by_task_id=Failed to retrieve the specified search results
@@ -158,7 +159,7 @@ dataset_invalid_multiclass_target=Unexpected dtype of target for multiclass task
158
159
  dataset_invalid_regression_target=Unexpected dtype of target for regression task type: {}. Expected float
159
160
  dataset_invalid_timeseries_target=Unexpected dtype of target for timeseries task type: {}. Expected float
160
161
  dataset_to_many_multiclass_targets=The number of target classes {} exceeds the allowed threshold: {}. Please, correct your data and try again
161
- dataset_rarest_class_less_min=Frequency of the rarest class `{}` is {}, minimum frequency must be > {} for each class\nPlease, remove rows with rarest class from your dataframe
162
+ dataset_rarest_class_less_min=Count of rows with the rarest class `{}` is {}, minimum count must be > {} for each class\nPlease, remove rows with rarest class from your dataframe
162
163
  dataset_rarest_class_less_threshold=\nWARNING: Target is imbalanced and will be undersampled to the rarest class. Frequency of the rarest class `{}` is {}\nMinimum number of observations for each class to avoid undersampling {} ({}%)
163
164
  dataset_date_features=\nWARNING: Columns {} is a datetime or period type but not used as a search key, removed from X
164
165
  dataset_too_many_features=Too many features. Maximum number of features is {}
@@ -100,6 +100,9 @@ class DateTimeSearchKeyConverter:
100
100
  msg = self.bundle.get("unsupported_date_type").format(self.date_column)
101
101
  self.logger.warning(msg)
102
102
  raise ValidationError(msg)
103
+ else:
104
+ df[self.date_column] = df[self.date_column].astype("string").apply(self.clean_date)
105
+ df[self.date_column] = self.parse_date(df)
103
106
 
104
107
  # If column with date is datetime then extract seconds of the day and minute of the hour
105
108
  # as additional features
@@ -55,7 +55,7 @@ def _get_execution_ide() -> str:
55
55
  def get_track_metrics(client_ip: Optional[str] = None, client_visitorid: Optional[str] = None) -> dict:
56
56
  # default values
57
57
  track = {"ide": _get_execution_ide()}
58
- ident_res = "https://api.ipify.org"
58
+ ident_res = "https://api64.ipify.org"
59
59
 
60
60
  try:
61
61
  track["hostname"] = socket.gethostname()
@@ -74,17 +74,20 @@ def get_track_metrics(client_ip: Optional[str] = None, client_visitorid: Optiona
74
74
  display(
75
75
  Javascript(
76
76
  """
77
- import('https://upgini.github.io/upgini/js/a.js')
77
+ async function getVisitorId() {
78
+ return import('https://upgini.github.io/upgini/js/a.js')
78
79
  .then(FingerprintJS => FingerprintJS.load())
79
80
  .then(fp => fp.get())
80
- .then(result => window.visitorId = result.visitorId);
81
+ .then(result => result.visitorId);
82
+ }
81
83
  """
82
84
  )
83
85
  )
84
- track["visitorId"] = output.eval_js("window.visitorId", timeout_sec=10)
86
+ track["visitorId"] = output.eval_js("getVisitorId()", timeout_sec=30)
85
87
  except Exception as e:
86
88
  track["err"] = str(e)
87
- track["visitorId"] = "None"
89
+ if "visitorId" not in track:
90
+ track["visitorId"] = "None"
88
91
  if client_ip:
89
92
  track["ip"] = client_ip
90
93
  else:
@@ -95,16 +98,19 @@ def get_track_metrics(client_ip: Optional[str] = None, client_visitorid: Optiona
95
98
  display(
96
99
  Javascript(
97
100
  f"""
98
- fetch("{ident_res}")
101
+ async function getIP() {{
102
+ return fetch("{ident_res}")
99
103
  .then(response => response.text())
100
- .then(data => window.clientIP = data);
104
+ .then(data => data);
105
+ }}
101
106
  """
102
107
  )
103
108
  )
104
- track["ip"] = output.eval_js("window.clientIP", timeout_sec=10)
109
+ track["ip"] = output.eval_js("getIP()", timeout_sec=10)
105
110
  except Exception as e:
106
111
  track["err"] = str(e)
107
- track["ip"] = "0.0.0.0"
112
+ if "ip" not in track:
113
+ track["ip"] = "0.0.0.0"
108
114
 
109
115
  elif track["ide"] == "binder":
110
116
  try:
@@ -116,8 +122,10 @@ def get_track_metrics(client_ip: Optional[str] = None, client_visitorid: Optiona
116
122
  track["visitorId"] = sha256(os.environ["CLIENT_IP"].encode()).hexdigest()
117
123
  except Exception as e:
118
124
  track["err"] = str(e)
119
- track["ip"] = "0.0.0.0"
120
- track["visitorId"] = "None"
125
+ if "ip" not in track:
126
+ track["ip"] = "0.0.0.0"
127
+ if "visitorId" not in track:
128
+ track["visitorId"] = "None"
121
129
 
122
130
  elif track["ide"] == "kaggle":
123
131
  try:
@@ -136,8 +144,8 @@ def get_track_metrics(client_ip: Optional[str] = None, client_visitorid: Optiona
136
144
  raise Exception(err)
137
145
  except Exception as e:
138
146
  track["err"] = str(e)
139
- track["ip"] = "0.0.0.0"
140
- track["visitorId"] = "None"
147
+ if "visitorId" not in track:
148
+ track["visitorId"] = "None"
141
149
  else:
142
150
  try:
143
151
  if client_ip:
@@ -150,5 +158,9 @@ def get_track_metrics(client_ip: Optional[str] = None, client_visitorid: Optiona
150
158
  track["visitorId"] = sha256(str(getnode()).encode()).hexdigest()
151
159
  except Exception as e:
152
160
  track["err"] = str(e)
161
+ if "visitorId" not in track:
162
+ track["visitorId"] = "None"
163
+ if "ip" not in track:
164
+ track["ip"] = "0.0.0.0"
153
165
 
154
166
  return track
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: upgini
3
- Version: 1.1.271
3
+ Version: 1.1.274
4
4
  Summary: Intelligent data search & enrichment for Machine Learning
5
5
  Home-page: https://upgini.com/
6
6
  Author: Upgini Developers
@@ -28,7 +28,7 @@ Description-Content-Type: text/markdown
28
28
  License-File: LICENSE
29
29
  Requires-Dist: python-dateutil>=2.8.0
30
30
  Requires-Dist: requests>=2.8.0
31
- Requires-Dist: pandas<2.0.0,>=1.1.0
31
+ Requires-Dist: pandas<3.0.0,>=1.1.0
32
32
  Requires-Dist: numpy>=1.19.0
33
33
  Requires-Dist: scikit-learn>=1.3.0
34
34
  Requires-Dist: pydantic<2.0.0,>=1.8.2
@@ -7,6 +7,7 @@ src/upgini/ads.py
7
7
  src/upgini/dataset.py
8
8
  src/upgini/errors.py
9
9
  src/upgini/features_enricher.py
10
+ src/upgini/fingerprint.js
10
11
  src/upgini/http.py
11
12
  src/upgini/metadata.py
12
13
  src/upgini/metrics.py
@@ -1,6 +1,6 @@
1
1
  python-dateutil>=2.8.0
2
2
  requests>=2.8.0
3
- pandas<2.0.0,>=1.1.0
3
+ pandas<3.0.0,>=1.1.0
4
4
  numpy>=1.19.0
5
5
  scikit-learn>=1.3.0
6
6
  pydantic<2.0.0,>=1.8.2
@@ -30,7 +30,8 @@ def test_date_diff_type2():
30
30
 
31
31
  operand = DateDiffType2(left_unit="s")
32
32
  expected_result = pd.Series([61.0, 182.0])
33
- assert_series_equal(operand.calculate_binary(df.date1, df.date2), expected_result)
33
+ actual = operand.calculate_binary(df.date1, df.date2)
34
+ assert_series_equal(actual, expected_result)
34
35
 
35
36
 
36
37
  def test_date_diff_list():
@@ -246,7 +246,7 @@ def test_eval_set_with_diff_order_of_columns(requests_mock: Mocker):
246
246
  eval1_df = df[10000:11000].reset_index(drop=True)
247
247
  eval1_features = eval1_df.drop(columns="target")
248
248
  # shuffle columns
249
- eval1_features = eval1_features[set(eval1_features.columns)]
249
+ eval1_features = eval1_features[list(eval1_features.columns)]
250
250
  eval1_target = eval1_df["target"].reset_index(drop=True)
251
251
 
252
252
  eval2_df = df[11000:12000]
@@ -375,7 +375,7 @@ def test_saved_features_enricher(requests_mock: Mocker):
375
375
  url = "http://fake_url2"
376
376
 
377
377
  path_to_mock_features = os.path.join(
378
- os.path.dirname(os.path.realpath(__file__)), "test_data/binary/validation_features.parquet"
378
+ os.path.dirname(os.path.realpath(__file__)), "test_data/binary/validation_features_v3.parquet"
379
379
  )
380
380
 
381
381
  mock_default_requests(requests_mock, url)
@@ -462,7 +462,7 @@ def test_saved_features_enricher(requests_mock: Mocker):
462
462
  segment_header: [train_segment, eval_1_segment, eval_2_segment],
463
463
  rows_header: [10000, 1000, 1000],
464
464
  target_mean_header: [0.5044, 0.487, 0.486],
465
- enriched_gini: [-0.000136, 0.000000, -0.003728],
465
+ enriched_gini: [0.021830, -0.006607, -0.018483],
466
466
  }
467
467
  )
468
468
  print("Expected metrics: ")
@@ -487,16 +487,13 @@ def test_saved_features_enricher(requests_mock: Mocker):
487
487
  train_random_indices = random.choice(train_target.index, size=9000, replace=False)
488
488
  train_target.loc[train_random_indices] = 0
489
489
 
490
- metrics = enricher.calculate_metrics(
491
- train_features,
492
- train_target
493
- )
490
+ metrics = enricher.calculate_metrics(train_features, train_target)
494
491
  expected_metrics = pd.DataFrame(
495
492
  {
496
493
  segment_header: [train_segment],
497
494
  rows_header: [10000],
498
495
  target_mean_header: [0.049],
499
- enriched_gini: [0.000985],
496
+ enriched_gini: [0.054454],
500
497
  }
501
498
  )
502
499
  print("Expected metrics: ")
@@ -2230,8 +2227,9 @@ def test_email_search_key(requests_mock: Mocker):
2230
2227
  "hashed_email_64ff8c",
2231
2228
  "email_one_domain_3b0a68",
2232
2229
  "email_domain_10c73f",
2230
+ "current_date_b993c4",
2233
2231
  }
2234
- assert {"hashed_email_64ff8c", "email_one_domain_3b0a68"} == {
2232
+ assert {"hashed_email_64ff8c", "email_one_domain_3b0a68", "current_date_b993c4"} == {
2235
2233
  sk for sublist in self.search_keys for sk in sublist
2236
2234
  }
2237
2235
  raise TestException()
@@ -2276,10 +2274,18 @@ def test_composit_index_search_key(requests_mock: Mocker):
2276
2274
  **kwargs,
2277
2275
  ):
2278
2276
  self.validate()
2279
- assert set(self.columns.to_list()) == {"system_record_id", "country_aff64e", "postal_code_13534a", "target"}
2277
+ assert set(self.columns.to_list()) == {
2278
+ "system_record_id",
2279
+ "country_aff64e",
2280
+ "postal_code_13534a",
2281
+ "current_date_b993c4",
2282
+ "target",
2283
+ }
2280
2284
  assert "country_aff64e" in self.columns
2281
2285
  assert "postal_code_13534a"
2282
- assert {"country_aff64e", "postal_code_13534a"} == {sk for sublist in self.search_keys for sk in sublist}
2286
+ assert {"country_aff64e", "postal_code_13534a", "current_date_b993c4"} == {
2287
+ sk for sublist in self.search_keys for sk in sublist
2288
+ }
2283
2289
  # assert "country_fake_a" in self.columns
2284
2290
  # assert "postal_code_fake_a" in self.columns
2285
2291
  # assert {"country_fake_a", "postal_code_fake_a"} == {sk for sublist in self.search_keys for sk in sublist}
@@ -2652,5 +2658,4 @@ class DataFrameWrapper:
2652
2658
 
2653
2659
 
2654
2660
  class TestException(Exception):
2655
- def __init__(self):
2656
- super().__init__()
2661
+ pass
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes