detectkit 0.16.1__tar.gz → 0.16.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.
Files changed (105) hide show
  1. {detectkit-0.16.1/detectkit.egg-info → detectkit-0.16.3}/PKG-INFO +1 -1
  2. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/__init__.py +1 -1
  3. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/email.py +27 -6
  4. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/telegram.py +4 -4
  5. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/webhook.py +26 -6
  6. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/rules/alerting.md +9 -0
  7. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/rules/cli.md +3 -2
  8. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/commands/test_alert.py +18 -2
  9. {detectkit-0.16.1 → detectkit-0.16.3/detectkit.egg-info}/PKG-INFO +1 -1
  10. {detectkit-0.16.1 → detectkit-0.16.3}/LICENSE +0 -0
  11. {detectkit-0.16.1 → detectkit-0.16.3}/MANIFEST.in +0 -0
  12. {detectkit-0.16.1 → detectkit-0.16.3}/README.md +0 -0
  13. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/__init__.py +0 -0
  14. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/__init__.py +0 -0
  15. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/base.py +0 -0
  16. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/branding.py +0 -0
  17. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/factory.py +0 -0
  18. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/mattermost.py +0 -0
  19. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/slack.py +0 -0
  20. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/orchestrator/__init__.py +0 -0
  21. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/orchestrator/_base.py +0 -0
  22. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/orchestrator/_cooldown.py +0 -0
  23. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/orchestrator/_decision.py +0 -0
  24. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/orchestrator/_dispatch.py +0 -0
  25. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/orchestrator/_recovery.py +0 -0
  26. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/orchestrator/_types.py +0 -0
  27. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/orchestrator/orchestrator.py +0 -0
  28. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/__init__.py +0 -0
  29. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/_output.py +0 -0
  30. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/CLAUDE.section.md +0 -0
  31. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/rules/detectors.md +0 -0
  32. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/rules/metrics.md +0 -0
  33. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/rules/overview.md +0 -0
  34. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/rules/project.md +0 -0
  35. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/skills/dtk-feedback/SKILL.md +0 -0
  36. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/skills/dtk-new-metric/SKILL.md +0 -0
  37. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/skills/dtk-setup-project/SKILL.md +0 -0
  38. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/commands/__init__.py +0 -0
  39. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/commands/clean.py +0 -0
  40. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/commands/init.py +0 -0
  41. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/commands/init_claude.py +0 -0
  42. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/commands/run.py +0 -0
  43. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/commands/unlock.py +0 -0
  44. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/main.py +0 -0
  45. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/config/__init__.py +0 -0
  46. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/config/metric_config.py +0 -0
  47. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/config/profile.py +0 -0
  48. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/config/project_config.py +0 -0
  49. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/config/validator.py +0 -0
  50. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/core/__init__.py +0 -0
  51. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/core/interval.py +0 -0
  52. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/core/models.py +0 -0
  53. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/__init__.py +0 -0
  54. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/_sql_manager.py +0 -0
  55. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/clickhouse_manager.py +0 -0
  56. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/__init__.py +0 -0
  57. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/_alert_states.py +0 -0
  58. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/_base.py +0 -0
  59. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/_datapoints.py +0 -0
  60. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/_detections.py +0 -0
  61. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/_maintenance.py +0 -0
  62. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/_metrics.py +0 -0
  63. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/_schema.py +0 -0
  64. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/_tasks.py +0 -0
  65. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/manager.py +0 -0
  66. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/manager.py +0 -0
  67. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/mysql_manager.py +0 -0
  68. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/postgres_manager.py +0 -0
  69. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/tables.py +0 -0
  70. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/__init__.py +0 -0
  71. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/base.py +0 -0
  72. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/factory.py +0 -0
  73. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/seasonality.py +0 -0
  74. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/statistical/__init__.py +0 -0
  75. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/statistical/_windowed.py +0 -0
  76. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/statistical/iqr.py +0 -0
  77. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/statistical/mad.py +0 -0
  78. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/statistical/manual_bounds.py +0 -0
  79. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/statistical/zscore.py +0 -0
  80. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/loaders/__init__.py +0 -0
  81. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/loaders/metric_loader.py +0 -0
  82. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/loaders/query_template.py +0 -0
  83. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/__init__.py +0 -0
  84. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/error_dispatch.py +0 -0
  85. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/task_manager/__init__.py +0 -0
  86. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/task_manager/_alert_step.py +0 -0
  87. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/task_manager/_base.py +0 -0
  88. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/task_manager/_detect_step.py +0 -0
  89. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/task_manager/_load_step.py +0 -0
  90. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/task_manager/_types.py +0 -0
  91. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/task_manager/manager.py +0 -0
  92. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/utils/__init__.py +0 -0
  93. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/utils/datetime_utils.py +0 -0
  94. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/utils/env_interpolation.py +0 -0
  95. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/utils/json_utils.py +0 -0
  96. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/utils/stats.py +0 -0
  97. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit.egg-info/SOURCES.txt +0 -0
  98. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit.egg-info/dependency_links.txt +0 -0
  99. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit.egg-info/entry_points.txt +0 -0
  100. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit.egg-info/requires.txt +0 -0
  101. {detectkit-0.16.1 → detectkit-0.16.3}/detectkit.egg-info/top_level.txt +0 -0
  102. {detectkit-0.16.1 → detectkit-0.16.3}/pyproject.toml +0 -0
  103. {detectkit-0.16.1 → detectkit-0.16.3}/requirements.txt +0 -0
  104. {detectkit-0.16.1 → detectkit-0.16.3}/setup.cfg +0 -0
  105. {detectkit-0.16.1 → detectkit-0.16.3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: detectkit
3
- Version: 0.16.1
3
+ Version: 0.16.3
4
4
  Summary: Metric monitoring with automatic anomaly detection
5
5
  Author: detectkit team
6
6
  License: MIT
@@ -4,7 +4,7 @@ detectk - Anomaly Detection for Time-Series Metrics
4
4
  A Python library for data analysts and engineers to monitor metrics with automatic anomaly detection.
5
5
  """
6
6
 
7
- __version__ = "0.16.1"
7
+ __version__ = "0.16.3"
8
8
 
9
9
  from detectkit.core.interval import Interval
10
10
  from detectkit.core.models import ColumnDefinition, TableModel
@@ -315,13 +315,13 @@ class EmailChannel(BaseAlertChannel):
315
315
  parts.append(self._lead_html(ctx["description"]))
316
316
 
317
317
  if kind == "anomaly":
318
- lead = (
319
- f"Fired because the latest {ctx['consecutive_count']}/"
320
- f"{ctx['consecutive_required']} consecutive points each met the quorum "
321
- f"(min_detectors={ctx['min_detectors']}, direction "
322
- f"{html.escape(str(ctx['direction_policy']))})."
318
+ parts.append(self._rule_html(ctx))
319
+ parts.append(
320
+ self._lead_html(
321
+ f"Latest {ctx['consecutive_count']}/{ctx['consecutive_required']} "
322
+ "consecutive points met the quorum."
323
+ )
323
324
  )
324
- parts.append(self._lead_html(lead))
325
325
  parts.append(
326
326
  self._stat_grid(
327
327
  [
@@ -340,6 +340,7 @@ class EmailChannel(BaseAlertChannel):
340
340
  "expected bounds."
341
341
  )
342
342
  parts.append(self._lead_html(lead))
343
+ parts.append(self._rule_html(ctx))
343
344
  parts.append(
344
345
  self._stat_grid(
345
346
  [
@@ -380,6 +381,26 @@ class EmailChannel(BaseAlertChannel):
380
381
  f"{html.escape(text)}</td></tr>"
381
382
  )
382
383
 
384
+ def _rule_html(self, ctx: dict[str, Any]) -> str:
385
+ """The configured firing rule: a bold ``Rule`` label + a monospace chip.
386
+
387
+ Mirrors the inline-code "Rule chip" the webhook/Telegram channels render,
388
+ so the same firing rule reads the same way in every channel.
389
+ """
390
+ expr = (
391
+ f"min_detectors={ctx['min_detectors']} &middot; "
392
+ f"direction={html.escape(str(ctx['direction_policy']))} &middot; "
393
+ f"consecutive={ctx['consecutive_required']}"
394
+ )
395
+ return (
396
+ f'<tr><td style="padding:0 24px 14px 24px;font-family:{_SANS};font-size:13px;'
397
+ f'color:{_MUTED};mso-line-height-rule:exactly;line-height:22px;">'
398
+ f'<strong style="color:{_INK};">Rule</strong>&nbsp;'
399
+ f'<code style="font-family:{_MONO};font-size:12px;background-color:{_PAPER};'
400
+ f'border:1px solid {_BORDER};border-radius:4px;padding:2px 6px;color:{_INK};">'
401
+ f"{expr}</code></td></tr>"
402
+ )
403
+
383
404
  def _stat_grid(self, pairs: list[tuple[str, str]]) -> str:
384
405
  """A 2-column grid of (label, value) cells (table-based)."""
385
406
  cells = ""
@@ -167,9 +167,9 @@ class TelegramChannel(BaseAlertChannel):
167
167
  f"<b>{ctx['consecutive_count']}/{ctx['consecutive_required']}</b> consecutive"
168
168
  )
169
169
  lines.append(
170
- f"<i>Rule: min_detectors={ctx['min_detectors']} · "
170
+ f"<b>Rule</b> <code>min_detectors={ctx['min_detectors']} · "
171
171
  f"direction={esc(ctx['direction_policy'])} · "
172
- f"consecutive={ctx['consecutive_required']}</i>"
172
+ f"consecutive={ctx['consecutive_required']}</code>"
173
173
  )
174
174
  lines.append("")
175
175
  lines.append(
@@ -188,9 +188,9 @@ class TelegramChannel(BaseAlertChannel):
188
188
  "expected bounds."
189
189
  )
190
190
  lines.append(
191
- f"<i>Rule: min_detectors={ctx['min_detectors']} · "
191
+ f"<b>Rule</b> <code>min_detectors={ctx['min_detectors']} · "
192
192
  f"direction={esc(ctx['direction_policy'])} · "
193
- f"consecutive={ctx['consecutive_required']}</i>"
193
+ f"consecutive={ctx['consecutive_required']}</code>"
194
194
  )
195
195
  lines.append("")
196
196
  lines.append(
@@ -256,11 +256,19 @@ class WebhookChannel(BaseAlertChannel):
256
256
  def code(s: str) -> str:
257
257
  return f"`{s}`" if s else ""
258
258
 
259
+ # The configured firing rule, set apart as a bold "Rule" label + an
260
+ # inline-code chip so it reads as "this is the config that fired" at a
261
+ # glance. Backticks render identically on Slack and Mattermost; the bold
262
+ # label is platform-aware (see ``_bold``).
263
+ rule_chip = f"{self._bold('Rule')} " + code(
264
+ f"min_detectors={ctx['min_detectors']} · "
265
+ f"direction={ctx['direction_policy']} · "
266
+ f"consecutive={ctx['consecutive_required']}"
267
+ )
268
+
259
269
  if kind == "anomaly":
260
270
  lead = (
261
- f"Rule: min_detectors={ctx['min_detectors']} · "
262
- f"direction={ctx['direction_policy']} · "
263
- f"consecutive={ctx['consecutive_required']}. "
271
+ f"{rule_chip}\n"
264
272
  f"Latest {ctx['consecutive_count']}/{ctx['consecutive_required']} "
265
273
  "consecutive points met the quorum."
266
274
  )
@@ -275,9 +283,7 @@ class WebhookChannel(BaseAlertChannel):
275
283
  elif kind == "recovery":
276
284
  lead = (
277
285
  "The alert condition no longer holds — the metric is back within "
278
- f"expected bounds. Rule: min_detectors={ctx['min_detectors']} · "
279
- f"direction={ctx['direction_policy']} · "
280
- f"consecutive={ctx['consecutive_required']}."
286
+ f"expected bounds.\n{rule_chip}"
281
287
  )
282
288
  short("Value", code(ctx["value_display"]))
283
289
  short("Expected", code(ctx["expected_range"]))
@@ -329,6 +335,20 @@ class WebhookChannel(BaseAlertChannel):
329
335
  "mrkdwn_in": ["text", "fields"],
330
336
  }
331
337
 
338
+ def _bold(self, text: str) -> str:
339
+ """Render *text* bold in the target platform's markdown.
340
+
341
+ Slack mrkdwn uses ``*bold*``; Mattermost and generic webhooks use
342
+ CommonMark ``**bold**`` (where ``*x*`` would render as italic). Mirrors
343
+ the platform branch in :meth:`_link_markup`, detecting Slack from the
344
+ webhook host.
345
+ """
346
+ if not text:
347
+ return ""
348
+ if "hooks.slack.com" in self.webhook_url:
349
+ return f"*{text}*"
350
+ return f"**{text}**"
351
+
332
352
  def _link_markup(self, url: str, label: str) -> str:
333
353
  """Render *label* as a clickable link in the target platform's syntax.
334
354
 
@@ -213,6 +213,15 @@ leads with a colored **status circle** — 🔴 anomaly, 🟢 recovery, 🟡 no-
213
213
  table, a monospace params box, an optional "Open dashboard" button, and a
214
214
  footer. The plain-text body remains the multipart fallback.
215
215
 
216
+ On anomaly **and** recovery alerts the **firing rule is set apart uniformly** in
217
+ every default-rendered channel: a bold **Rule** label + an inline-code chip
218
+ (`min_detectors=… · direction=… · consecutive=…`), with the quorum explanation
219
+ on its own line, so the rule reads as "this is the config that fired" at a
220
+ glance. Bold is platform-aware (`*Rule*` on Slack, `**Rule**` on
221
+ Mattermost/generic; `<b>Rule</b>` on Telegram; `<strong>` in email), while the
222
+ code chip is identical everywhere. Custom templates and the plain-text fallbacks
223
+ are unchanged.
224
+
216
225
  ## Project label (multi-project channels)
217
226
 
218
227
  The bot keeps the **detectkit brand** name + avatar by default (so users rarely
@@ -55,8 +55,9 @@ dtk run --select <sel> [--steps load,detect,alert] [--from DATE] [--to DATE] \
55
55
 
56
56
  Sends a mock alert (fake value/CI/severity) through the metric's configured
57
57
  channels, using that alert config's own rule (`min_detectors` / `direction` /
58
- `consecutive_anomalies`), so the preview matches a real firing. Use it to
59
- verify webhook URLs, channel permissions, and custom templates.
58
+ `consecutive_anomalies`) and the project-name `[name]` prefix, so the preview
59
+ matches a real firing. Use it to verify webhook URLs, channel permissions, and
60
+ custom templates.
60
61
 
61
62
  ## `dtk unlock --select <sel>`
62
63
 
@@ -23,6 +23,7 @@ def create_mock_alert_data(
23
23
  alerting_config,
24
24
  timezone_display: str = "UTC",
25
25
  help_url: str | None = None,
26
+ project_name: str | None = None,
26
27
  ) -> AlertData:
27
28
  """
28
29
  Create realistic mock AlertData for testing.
@@ -36,6 +37,10 @@ def create_mock_alert_data(
36
37
  timezone_display: Timezone for display
37
38
  help_url: Resolved "how to read this alert" link to preview (the
38
39
  project's ``alert_help_url``); ``None`` renders no help link.
40
+ project_name: Project name from ``detectkit_project.yml`` — stamped so
41
+ the preview carries the same ``[name]`` prefix a real ``dtk run``
42
+ renders (the run pipeline sets it in ``_alert_step.py``); ``None``
43
+ renders no prefix.
39
44
 
40
45
  Returns:
41
46
  AlertData with mock anomaly data
@@ -90,6 +95,7 @@ def create_mock_alert_data(
90
95
  },
91
96
  consecutive_count=consecutive_required,
92
97
  mentions=mentions,
98
+ project_name=project_name,
93
99
  dashboard_url=getattr(alerting_config, "dashboard_url", None),
94
100
  links=dict(getattr(alerting_config, "links", {}) or {}),
95
101
  help_url=help_url,
@@ -123,7 +129,13 @@ def run_test_alert(metric_name: str, profile: str | None = None):
123
129
  with open(project_config_path) as f:
124
130
  project_data = yaml.safe_load(f)
125
131
 
126
- metrics_dir_name = project_data.get("metrics_path", "metrics")
132
+ # Project name drives the [name] prefix a real `dtk run` stamps on every
133
+ # alert (see _alert_step.py); thread it so the preview matches a real firing.
134
+ project_name = project_data.get("name")
135
+
136
+ # Metrics dir lives under `paths.metrics` (default "metrics"); the old
137
+ # top-level `metrics_path` key is deprecated and ignored by ProjectConfig.
138
+ metrics_dir_name = (project_data.get("paths") or {}).get("metrics", "metrics")
127
139
 
128
140
  # Resolve the "how to read this alert" link so the preview matches what real
129
141
  # alerts would carry (brand default, a custom URL, or hidden via false).
@@ -187,7 +199,11 @@ def run_test_alert(metric_name: str, profile: str | None = None):
187
199
  print(f" Channels: {', '.join(alerting_config.channels)}\n")
188
200
 
189
201
  alert_data = create_mock_alert_data(
190
- metric_config, alerting_config, timezone_display, help_url=help_url
202
+ metric_config,
203
+ alerting_config,
204
+ timezone_display,
205
+ help_url=help_url,
206
+ project_name=project_name,
191
207
  )
192
208
 
193
209
  success_count = 0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: detectkit
3
- Version: 0.16.1
3
+ Version: 0.16.3
4
4
  Summary: Metric monitoring with automatic anomaly detection
5
5
  Author: detectkit team
6
6
  License: MIT
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes