Skip to content

Commit ebfe12c

Browse files
authored
Fix: Add ignored snapshots for invalid start date (#1324)
* add ignored snapshots * add tests * cleanup * cleanup * add refresh dag to setting end * feedback
1 parent f7e575a commit ebfe12c

File tree

11 files changed

+405
-131
lines changed

11 files changed

+405
-131
lines changed

examples/sushi/models/items.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
@model(
5151
"sushi.items",
5252
kind=IncrementalByTimeRangeKind(time_column="ds", batch_size=30),
53-
start="3 months ago",
53+
start="1 week ago",
5454
cron="@daily",
5555
columns={
5656
"id": "int",

examples/sushi/models/orders.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"sushi.orders",
1818
description="Table of sushi orders.",
1919
kind=IncrementalByTimeRangeKind(time_column="ds", batch_size=30),
20-
start="3 months ago",
20+
start="1 week ago",
2121
cron="@daily",
2222
grains=[
2323
"id AS order_id",

sqlmesh/core/console.py

Lines changed: 88 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from rich.tree import Tree
2525

2626
from sqlmesh.core.environment import EnvironmentNamingInfo
27-
from sqlmesh.core.snapshot import Snapshot, SnapshotChangeCategory
27+
from sqlmesh.core.snapshot import Snapshot, SnapshotChangeCategory, start_date
2828
from sqlmesh.core.test import ModelTest
2929
from sqlmesh.utils import rich as srich
3030
from sqlmesh.utils.date import to_date, yesterday_ds
@@ -111,7 +111,10 @@ def stop_migration_progress(self, success: bool = True) -> None:
111111

112112
@abc.abstractmethod
113113
def show_model_difference_summary(
114-
self, context_diff: ContextDiff, detailed: bool = False
114+
self,
115+
context_diff: ContextDiff,
116+
detailed: bool = False,
117+
ignored_snapshot_names: t.Optional[t.Set[str]] = None,
115118
) -> None:
116119
"""Displays a summary of differences for the given models."""
117120

@@ -386,14 +389,19 @@ def stop_migration_progress(self, success: bool = True) -> None:
386389
self.log_success("The migration has been completed successfully")
387390

388391
def show_model_difference_summary(
389-
self, context_diff: ContextDiff, detailed: bool = False
392+
self,
393+
context_diff: ContextDiff,
394+
detailed: bool = False,
395+
ignored_snapshot_names: t.Optional[t.Set[str]] = None,
390396
) -> None:
391397
"""Shows a summary of the differences.
392398
393399
Args:
394400
context_diff: The context diff to use to print the summary
395401
detailed: Show the actual SQL differences if True.
402+
ignored_snapshot_names: A set of snapshot names that are ignored
396403
"""
404+
ignored_snapshot_names = ignored_snapshot_names or set()
397405
if context_diff.is_new_environment:
398406
self._print(
399407
Tree(
@@ -408,40 +416,44 @@ def show_model_difference_summary(
408416
return
409417

410418
tree = Tree(f"[bold]Summary of differences against `{context_diff.environment}`:")
411-
412-
if context_diff.added:
419+
added_model_names = context_diff.added - ignored_snapshot_names
420+
if added_model_names:
413421
added_tree = Tree(f"[bold][added]Added Models:")
414-
for model in context_diff.added:
415-
added_tree.add(f"[added]{model}")
422+
for model_name in added_model_names:
423+
added_tree.add(f"[added]{model_name}")
416424
tree.add(added_tree)
417425

418-
if context_diff.removed:
426+
removed_model_names = context_diff.removed - ignored_snapshot_names
427+
if removed_model_names:
419428
removed_tree = Tree(f"[bold][removed]Removed Models:")
420-
for model in context_diff.removed:
421-
removed_tree.add(f"[removed]{model}")
429+
for model_name in removed_model_names:
430+
removed_tree.add(f"[removed]{model_name}")
422431
tree.add(removed_tree)
423432

424-
if context_diff.modified_snapshots:
433+
modified_model_names = context_diff.modified_snapshots.keys() - ignored_snapshot_names
434+
if modified_model_names:
425435
direct = Tree(f"[bold][direct]Directly Modified:")
426436
indirect = Tree(f"[bold][indirect]Indirectly Modified:")
427437
metadata = Tree(f"[bold][metadata]Metadata Updated:")
428-
for model in context_diff.modified_snapshots:
429-
if context_diff.directly_modified(model):
438+
for model_name in modified_model_names:
439+
if context_diff.directly_modified(model_name):
430440
direct.add(
431-
Syntax(f"{model}\n{context_diff.text_diff(model)}", "sql")
441+
Syntax(f"{model_name}\n{context_diff.text_diff(model_name)}", "sql")
432442
if detailed
433-
else f"[direct]{model}"
443+
else f"[direct]{model_name}"
434444
)
435-
elif context_diff.indirectly_modified(model):
436-
indirect.add(f"[indirect]{model}")
437-
elif context_diff.metadata_updated(model):
438-
metadata.add(f"[metadata]{model}")
445+
elif context_diff.indirectly_modified(model_name):
446+
indirect.add(f"[indirect]{model_name}")
447+
elif context_diff.metadata_updated(model_name):
448+
metadata.add(f"[metadata]{model_name}")
439449
if direct.children:
440450
tree.add(direct)
441451
if indirect.children:
442452
tree.add(indirect)
443453
if metadata.children:
444454
tree.add(metadata)
455+
if ignored_snapshot_names:
456+
tree.add(self._get_ignored_tree(ignored_snapshot_names, context_diff.snapshots))
445457
self._print(tree)
446458

447459
def plan(self, plan: Plan, auto_apply: bool) -> None:
@@ -460,22 +472,35 @@ def plan(self, plan: Plan, auto_apply: bool) -> None:
460472
if auto_apply:
461473
plan.apply()
462474

475+
def _get_ignored_tree(
476+
self, ignored_snapshot_names: t.Set[str], snapshots: t.Dict[str, Snapshot]
477+
) -> Tree:
478+
ignored = Tree(f"[bold][ignored]Ignored Models (Expected Plan Start):")
479+
for model in ignored_snapshot_names:
480+
snapshot = snapshots[model]
481+
ignored.add(
482+
f"[ignored]{model} ({snapshot.get_latest(start_date(snapshot, snapshots.values()))})"
483+
)
484+
return ignored
485+
463486
def _show_options_after_categorization(self, plan: Plan, auto_apply: bool) -> None:
464487
if plan.forward_only and plan.new_snapshots:
465488
self._prompt_effective_from(plan, auto_apply)
466489

467490
if plan.requires_backfill:
468491
self._show_missing_dates(plan)
469492
self._prompt_backfill(plan, auto_apply)
470-
elif plan.context_diff.has_changes and not auto_apply:
493+
elif plan.has_changes and not auto_apply:
471494
self._prompt_promote(plan)
472495
elif plan.has_unmodified_unpromoted and not auto_apply:
473496
self.log_status_update("\n[bold]Virtually updating unmodified models\n")
474497
self._prompt_promote(plan)
475498

476499
def _prompt_categorize(self, plan: Plan, auto_apply: bool) -> None:
477500
"""Get the user's change category for the directly modified models."""
478-
self.show_model_difference_summary(plan.context_diff)
501+
self.show_model_difference_summary(
502+
plan.context_diff, ignored_snapshot_names=plan.ignored_snapshot_names
503+
)
479504

480505
self._show_categorized_snapshots(plan)
481506

@@ -565,7 +590,10 @@ def _prompt_backfill(self, plan: Plan, auto_apply: bool) -> None:
565590
)
566591
if end:
567592
plan.end = end
568-
593+
if plan.ignored_snapshot_names:
594+
self._print(
595+
self._get_ignored_tree(plan.ignored_snapshot_names, plan.context_diff.snapshots)
596+
)
569597
if not auto_apply and self._confirm(f"Apply - {backfill_or_preview.capitalize()} Tables"):
570598
plan.apply()
571599

@@ -1040,14 +1068,19 @@ class MarkdownConsole(CaptureTerminalConsole):
10401068
"""
10411069

10421070
def show_model_difference_summary(
1043-
self, context_diff: ContextDiff, detailed: bool = False
1071+
self,
1072+
context_diff: ContextDiff,
1073+
detailed: bool = False,
1074+
ignored_snapshot_names: t.Optional[t.Set[str]] = None,
10441075
) -> None:
10451076
"""Shows a summary of the differences.
10461077
10471078
Args:
10481079
context_diff: The context diff to use to print the summary.
10491080
detailed: Show the actual SQL differences if True.
1081+
ignored_snapshot_names: A set of snapshot names that are ignored
10501082
"""
1083+
ignored_snapshot_names = ignored_snapshot_names or set()
10511084
if context_diff.is_new_environment:
10521085
self._print(
10531086
f"**New environment `{context_diff.environment}` will be created from `{context_diff.create_from}`**\n\n"
@@ -1061,46 +1094,57 @@ def show_model_difference_summary(
10611094

10621095
self._print(f"**Summary of differences against `{context_diff.environment}`:**\n\n")
10631096

1064-
if context_diff.added:
1097+
added_model_names = context_diff.added - ignored_snapshot_names
1098+
if added_model_names:
10651099
self._print(f"**Added Models:**\n")
1066-
for model in context_diff.added:
1067-
self._print(f"- {model}\n")
1100+
for model_name in added_model_names:
1101+
self._print(f"- {model_name}\n")
10681102
self._print("\n")
10691103

1070-
if context_diff.removed:
1104+
removed_model_names = context_diff.removed - ignored_snapshot_names
1105+
if removed_model_names:
10711106
self._print(f"**Removed Models:**\n")
1072-
for model in context_diff.removed:
1073-
self._print(f"- {model}\n")
1107+
for model_name in removed_model_names:
1108+
self._print(f"- {model_name}\n")
10741109
self._print("\n")
10751110

1076-
if context_diff.modified_snapshots:
1111+
modified_model_names = context_diff.modified_snapshots.keys() - ignored_snapshot_names
1112+
if modified_model_names:
10771113
directly_modified = []
10781114
indirectly_modified = []
10791115
metadata_modified = []
1080-
for model in context_diff.modified_snapshots:
1081-
if context_diff.directly_modified(model):
1082-
directly_modified.append(model)
1083-
elif context_diff.indirectly_modified(model):
1084-
indirectly_modified.append(model)
1085-
elif context_diff.metadata_updated(model):
1086-
metadata_modified.append(model)
1116+
for model_name in modified_model_names:
1117+
if context_diff.directly_modified(model_name):
1118+
directly_modified.append(model_name)
1119+
elif context_diff.indirectly_modified(model_name):
1120+
indirectly_modified.append(model_name)
1121+
elif context_diff.metadata_updated(model_name):
1122+
metadata_modified.append(model_name)
10871123
if directly_modified:
10881124
self._print(f"**Directly Modified:**\n")
1089-
for model in directly_modified:
1090-
self._print(f"- `{model}`\n")
1125+
for model_name in directly_modified:
1126+
self._print(f"- `{model_name}`\n")
10911127
if detailed:
1092-
self._print(f"```diff\n{context_diff.text_diff(model)}\n```\n")
1128+
self._print(f"```diff\n{context_diff.text_diff(model_name)}\n```\n")
10931129
self._print("\n")
10941130
if indirectly_modified:
10951131
self._print(f"**Indirectly Modified:**\n")
1096-
for model in indirectly_modified:
1097-
self._print(f"- `{model}`\n")
1132+
for model_name in indirectly_modified:
1133+
self._print(f"- `{model_name}`\n")
10981134
self._print("\n")
10991135
if metadata_modified:
11001136
self._print(f"**Metadata Updated:**\n")
1101-
for model in metadata_modified:
1102-
self._print(f"- `{model}`\n")
1137+
for model_name in metadata_modified:
1138+
self._print(f"- `{model_name}`\n")
11031139
self._print("\n")
1140+
if ignored_snapshot_names:
1141+
self._print(f"**Ignored Models (Expected Plan Start):**\n")
1142+
for model_name in ignored_snapshot_names:
1143+
snapshot = context_diff.snapshots[model_name]
1144+
self._print(
1145+
f"- `{model_name}` ({snapshot.get_latest(start_date(snapshot, context_diff.snapshots.values()))})\n"
1146+
)
1147+
self._print("\n")
11041148

11051149
def _show_missing_dates(self, plan: Plan) -> None:
11061150
"""Displays the models with missing dates."""

sqlmesh/core/node.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,26 @@ def from_cron(klass, cron: str, sample_size: int = 10) -> IntervalUnit:
4848
def is_date_granularity(self) -> bool:
4949
return self in (IntervalUnit.YEAR, IntervalUnit.MONTH, IntervalUnit.DAY)
5050

51+
@property
52+
def is_year(self) -> bool:
53+
return self == IntervalUnit.YEAR
54+
55+
@property
56+
def is_month(self) -> bool:
57+
return self == IntervalUnit.MONTH
58+
59+
@property
60+
def is_day(self) -> bool:
61+
return self == IntervalUnit.DAY
62+
63+
@property
64+
def is_hour(self) -> bool:
65+
return self == IntervalUnit.HOUR
66+
67+
@property
68+
def is_minute(self) -> bool:
69+
return self == IntervalUnit.MINUTE
70+
5171
@property
5272
def _cron_expr(self) -> str:
5373
if self == IntervalUnit.MINUTE:

0 commit comments

Comments
 (0)