detectkit 0.8.1__tar.gz → 0.8.2__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.
- {detectkit-0.8.1/detectkit.egg-info → detectkit-0.8.2}/PKG-INFO +1 -1
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/__init__.py +1 -1
- detectkit-0.8.2/detectkit/cli/_output.py +50 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/cli/commands/clean.py +32 -53
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/cli/commands/unlock.py +5 -14
- {detectkit-0.8.1 → detectkit-0.8.2/detectkit.egg-info}/PKG-INFO +1 -1
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit.egg-info/SOURCES.txt +1 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/LICENSE +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/MANIFEST.in +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/README.md +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/__init__.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/channels/__init__.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/channels/base.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/channels/email.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/channels/factory.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/channels/mattermost.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/channels/slack.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/channels/telegram.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/channels/webhook.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/orchestrator/__init__.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/orchestrator/_base.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/orchestrator/_cooldown.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/orchestrator/_decision.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/orchestrator/_dispatch.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/orchestrator/_recovery.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/orchestrator/_types.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/alerting/orchestrator/orchestrator.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/cli/__init__.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/cli/commands/__init__.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/cli/commands/init.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/cli/commands/run.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/cli/commands/test_alert.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/cli/main.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/config/__init__.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/config/metric_config.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/config/profile.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/config/project_config.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/config/validator.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/core/__init__.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/core/interval.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/core/models.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/database/__init__.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/database/clickhouse_manager.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/database/internal_tables/__init__.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/database/internal_tables/_alert_states.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/database/internal_tables/_base.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/database/internal_tables/_datapoints.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/database/internal_tables/_detections.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/database/internal_tables/_maintenance.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/database/internal_tables/_metrics.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/database/internal_tables/_schema.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/database/internal_tables/_tasks.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/database/internal_tables/manager.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/database/manager.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/database/tables.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/detectors/__init__.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/detectors/base.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/detectors/factory.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/detectors/seasonality.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/detectors/statistical/__init__.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/detectors/statistical/_windowed.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/detectors/statistical/iqr.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/detectors/statistical/mad.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/detectors/statistical/manual_bounds.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/detectors/statistical/zscore.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/loaders/__init__.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/loaders/metric_loader.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/loaders/query_template.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/orchestration/__init__.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/orchestration/error_dispatch.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/orchestration/task_manager/__init__.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/orchestration/task_manager/_alert_step.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/orchestration/task_manager/_base.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/orchestration/task_manager/_detect_step.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/orchestration/task_manager/_load_step.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/orchestration/task_manager/_types.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/orchestration/task_manager/manager.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/utils/__init__.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/utils/datetime_utils.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/utils/env_interpolation.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/utils/json_utils.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit/utils/stats.py +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit.egg-info/dependency_links.txt +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit.egg-info/entry_points.txt +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit.egg-info/requires.txt +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/detectkit.egg-info/top_level.txt +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/pyproject.toml +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/requirements.txt +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/setup.cfg +0 -0
- {detectkit-0.8.1 → detectkit-0.8.2}/setup.py +0 -0
|
@@ -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.8.
|
|
7
|
+
__version__ = "0.8.2"
|
|
8
8
|
|
|
9
9
|
from detectkit.core.interval import Interval
|
|
10
10
|
from detectkit.core.models import ColumnDefinition, TableModel
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Shared CLI output helpers so every command renders in one house style.
|
|
2
|
+
|
|
3
|
+
Mirrors the load → detect → alert pipeline's tree look (``┌─ / │ / └─``) used by
|
|
4
|
+
``dtk run`` so the maintenance commands (``dtk clean``, ``dtk unlock``) match it
|
|
5
|
+
instead of each inventing its own formatting.
|
|
6
|
+
|
|
7
|
+
House conventions:
|
|
8
|
+
- A metric *with* something to report is a tree: a cyan-bold ``┌─ <name>``
|
|
9
|
+
header followed by one child line per item (``│ `` for all but the last,
|
|
10
|
+
``└─ `` for the last).
|
|
11
|
+
- A metric with *nothing* to do is a single ``•`` line.
|
|
12
|
+
- A per-metric error is a red ``✗`` line (to stderr).
|
|
13
|
+
- The final summary is a cyan-bold ``Done. …`` line.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import click
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def echo_tree(name: str, children: list[str], *, warnings: list[str] | None = None) -> None:
|
|
22
|
+
"""Print a ``┌─ name`` header with ``│``/``└─`` child lines.
|
|
23
|
+
|
|
24
|
+
``warnings`` (if any) are rendered as yellow ``│`` continuation lines above
|
|
25
|
+
the children. ``children`` must be non-empty (a metric with no items should
|
|
26
|
+
use :func:`echo_noop` instead).
|
|
27
|
+
"""
|
|
28
|
+
click.echo(click.style(f" ┌─ {name}", fg="cyan", bold=True))
|
|
29
|
+
for warning in warnings or []:
|
|
30
|
+
click.echo(click.style(f" │ ⚠ {warning}", fg="yellow", bold=True))
|
|
31
|
+
last = len(children) - 1
|
|
32
|
+
for i, child in enumerate(children):
|
|
33
|
+
prefix = " └─ " if i == last else " │ "
|
|
34
|
+
click.echo(f"{prefix}{child}")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def echo_noop(name: str, reason: str) -> None:
|
|
38
|
+
"""A metric with nothing to do — a single ``•`` line."""
|
|
39
|
+
click.echo(f" • {name}: {reason}")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def echo_error(name: str, message: str) -> None:
|
|
43
|
+
"""A per-metric failure — a red ``✗`` line on stderr."""
|
|
44
|
+
click.echo(click.style(f" ✗ {name}: {message}", fg="red"), err=True)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def echo_done(summary: str) -> None:
|
|
48
|
+
"""The closing ``Done. …`` summary (cyan, bold), preceded by a blank line."""
|
|
49
|
+
click.echo()
|
|
50
|
+
click.echo(click.style(f"Done. {summary}", fg="cyan", bold=True))
|
|
@@ -27,6 +27,7 @@ from pathlib import Path
|
|
|
27
27
|
|
|
28
28
|
import click
|
|
29
29
|
|
|
30
|
+
from detectkit.cli._output import echo_done, echo_error, echo_noop, echo_tree
|
|
30
31
|
from detectkit.cli.commands.run import find_project_root, select_metrics
|
|
31
32
|
from detectkit.config.metric_config import MetricConfig
|
|
32
33
|
from detectkit.config.profile import ProfilesConfig
|
|
@@ -112,6 +113,8 @@ def _clean_drift(
|
|
|
112
113
|
total_det_groups = 0
|
|
113
114
|
total_alert_rows = 0
|
|
114
115
|
|
|
116
|
+
verb = "deleting" if execute else "would delete"
|
|
117
|
+
|
|
115
118
|
for _, config in metrics:
|
|
116
119
|
metric_name = config.name
|
|
117
120
|
try:
|
|
@@ -120,7 +123,7 @@ def _clean_drift(
|
|
|
120
123
|
db_detectors = internal_manager.list_detector_ids(metric_name)
|
|
121
124
|
db_alerts = internal_manager.list_alert_config_ids(metric_name)
|
|
122
125
|
except Exception as e:
|
|
123
|
-
|
|
126
|
+
echo_error(metric_name, f"error inspecting: {e}")
|
|
124
127
|
continue
|
|
125
128
|
|
|
126
129
|
orphan_detectors = {
|
|
@@ -129,55 +132,42 @@ def _clean_drift(
|
|
|
129
132
|
orphan_alerts = [a for a in db_alerts if a not in valid_alerts]
|
|
130
133
|
|
|
131
134
|
if not orphan_detectors and not orphan_alerts:
|
|
132
|
-
|
|
135
|
+
echo_noop(metric_name, "nothing stale")
|
|
133
136
|
continue
|
|
134
137
|
|
|
135
|
-
|
|
138
|
+
children = [
|
|
139
|
+
f"detector {det_id}: {verb} {count:,} detection row(s)"
|
|
140
|
+
for det_id, count in sorted(orphan_detectors.items())
|
|
141
|
+
] + [
|
|
142
|
+
f"alert_config {alert_id}: {verb} stale alert state"
|
|
143
|
+
for alert_id in sorted(orphan_alerts)
|
|
144
|
+
]
|
|
136
145
|
|
|
137
146
|
# An empty valid set means EVERY stored row is "orphaned" — usually a
|
|
138
147
|
# config mid-edit, not an intent to wipe the metric. Flag it loudly.
|
|
148
|
+
warnings = []
|
|
139
149
|
if orphan_detectors and not valid_detectors:
|
|
140
|
-
|
|
141
|
-
click.style(
|
|
142
|
-
" ⚠ config defines no detectors — ALL detections below would be removed",
|
|
143
|
-
fg="yellow",
|
|
144
|
-
bold=True,
|
|
145
|
-
)
|
|
146
|
-
)
|
|
150
|
+
warnings.append("config defines no detectors — ALL detections below would be removed")
|
|
147
151
|
if orphan_alerts and not valid_alerts:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
fg="yellow",
|
|
152
|
-
bold=True,
|
|
153
|
-
)
|
|
154
|
-
)
|
|
152
|
+
warnings.append("config defines no alerting — ALL alert states below would be removed")
|
|
153
|
+
|
|
154
|
+
echo_tree(metric_name, children, warnings=warnings)
|
|
155
155
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
verb = "deleting" if execute else "would delete"
|
|
159
|
-
click.echo(f" detector {det_id}: {verb} {count:,} detection row(s)")
|
|
160
|
-
if execute:
|
|
156
|
+
if execute:
|
|
157
|
+
for det_id in orphan_detectors:
|
|
161
158
|
internal_manager.delete_detections(
|
|
162
159
|
metric_name=metric_name, detector_id=det_id, mutations_sync=True
|
|
163
160
|
)
|
|
164
|
-
|
|
165
|
-
for alert_id in sorted(orphan_alerts):
|
|
166
|
-
total_alert_rows += 1
|
|
167
|
-
verb = "deleting" if execute else "would delete"
|
|
168
|
-
click.echo(f" alert_config {alert_id}: {verb} stale alert state")
|
|
169
|
-
if execute:
|
|
161
|
+
for alert_id in orphan_alerts:
|
|
170
162
|
internal_manager.delete_alert_state(metric_name, alert_id)
|
|
171
163
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
bold=True,
|
|
180
|
-
)
|
|
164
|
+
total_det_groups += len(orphan_detectors)
|
|
165
|
+
total_alert_rows += len(orphan_alerts)
|
|
166
|
+
|
|
167
|
+
verb_done = "Removed" if execute else "Would remove"
|
|
168
|
+
echo_done(
|
|
169
|
+
f"{verb_done} {total_det_groups} detector group(s) "
|
|
170
|
+
f"and {total_alert_rows} alert-state row(s)."
|
|
181
171
|
)
|
|
182
172
|
if not execute and (total_det_groups or total_alert_rows):
|
|
183
173
|
click.echo("Re-run with --execute to apply.")
|
|
@@ -222,15 +212,10 @@ def _clean_orphaned_metrics(
|
|
|
222
212
|
try:
|
|
223
213
|
counts = internal_manager.count_metric_rows(name)
|
|
224
214
|
except Exception as e:
|
|
225
|
-
|
|
215
|
+
echo_error(name, f"error counting rows: {e}")
|
|
226
216
|
continue
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
detail = ", ".join(f"{table}={count:,}" for table, count in counts.items() if count)
|
|
230
|
-
click.echo(
|
|
231
|
-
click.style(f" {name}: {verb} {total:,} row(s)", bold=True)
|
|
232
|
-
+ (f" [{detail}]" if detail else "")
|
|
233
|
-
)
|
|
217
|
+
children = [f"{table}: {count:,} row(s)" for table, count in counts.items() if count]
|
|
218
|
+
echo_tree(name, children or ["(no rows)"])
|
|
234
219
|
|
|
235
220
|
if not execute:
|
|
236
221
|
click.echo()
|
|
@@ -264,16 +249,10 @@ def _clean_orphaned_metrics(
|
|
|
264
249
|
try:
|
|
265
250
|
internal_manager.purge_metric(name)
|
|
266
251
|
purged += 1
|
|
267
|
-
click.echo(click.style(f" ✓ {name}: purged", fg="green"))
|
|
268
252
|
except Exception as e:
|
|
269
|
-
|
|
253
|
+
echo_error(name, f"error purging: {e}")
|
|
270
254
|
|
|
271
|
-
|
|
272
|
-
click.echo(
|
|
273
|
-
click.style(
|
|
274
|
-
f"Done. Purged {purged} of {len(orphans)} orphaned metric(s).", fg="cyan", bold=True
|
|
275
|
-
)
|
|
276
|
-
)
|
|
255
|
+
echo_done(f"Purged {purged} of {len(orphans)} orphaned metric(s).")
|
|
277
256
|
|
|
278
257
|
|
|
279
258
|
# ── helpers ──────────────────────────────────────────────────────────────────
|
|
@@ -8,6 +8,7 @@ auto-expires after its timeout, but this command clears it immediately.
|
|
|
8
8
|
|
|
9
9
|
import click
|
|
10
10
|
|
|
11
|
+
from detectkit.cli._output import echo_done, echo_error, echo_noop, echo_tree
|
|
11
12
|
from detectkit.cli.commands.run import find_project_root, select_metrics
|
|
12
13
|
from detectkit.config.profile import ProfilesConfig
|
|
13
14
|
from detectkit.database.internal_tables import InternalTablesManager
|
|
@@ -83,23 +84,13 @@ def run_unlock(select: str, profile: str | None):
|
|
|
83
84
|
try:
|
|
84
85
|
was_locked = internal_manager.clear_lock(metric_name)
|
|
85
86
|
except Exception as e:
|
|
86
|
-
|
|
87
|
-
click.style(f" ✗ {metric_name}: error clearing lock: {e}", fg="red"),
|
|
88
|
-
err=True,
|
|
89
|
-
)
|
|
87
|
+
echo_error(metric_name, f"error clearing lock: {e}")
|
|
90
88
|
continue
|
|
91
89
|
|
|
92
90
|
if was_locked:
|
|
93
91
|
cleared += 1
|
|
94
|
-
|
|
92
|
+
echo_tree(metric_name, ["lock cleared"])
|
|
95
93
|
else:
|
|
96
|
-
|
|
94
|
+
echo_noop(metric_name, "no active lock")
|
|
97
95
|
|
|
98
|
-
|
|
99
|
-
click.echo(
|
|
100
|
-
click.style(
|
|
101
|
-
f"Done. Cleared {cleared} lock(s) of {len(metrics)} metric(s).",
|
|
102
|
-
fg="cyan",
|
|
103
|
-
bold=True,
|
|
104
|
-
)
|
|
105
|
-
)
|
|
96
|
+
echo_done(f"Cleared {cleared} lock(s) of {len(metrics)} metric(s).")
|
|
@@ -29,6 +29,7 @@ detectkit/alerting/orchestrator/_recovery.py
|
|
|
29
29
|
detectkit/alerting/orchestrator/_types.py
|
|
30
30
|
detectkit/alerting/orchestrator/orchestrator.py
|
|
31
31
|
detectkit/cli/__init__.py
|
|
32
|
+
detectkit/cli/_output.py
|
|
32
33
|
detectkit/cli/main.py
|
|
33
34
|
detectkit/cli/commands/__init__.py
|
|
34
35
|
detectkit/cli/commands/clean.py
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|