sqlmesh 0.199.1.dev1__py3-none-any.whl → 0.199.1.dev2__py3-none-any.whl

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.
sqlmesh/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.199.1.dev1'
21
- __version_tuple__ = version_tuple = (0, 199, 1, 'dev1')
20
+ __version__ = version = '0.199.1.dev2'
21
+ __version_tuple__ = version_tuple = (0, 199, 1, 'dev2')
sqlmesh/core/console.py CHANGED
@@ -3224,8 +3224,9 @@ class MarkdownConsole(CaptureTerminalConsole):
3224
3224
  )
3225
3225
  else:
3226
3226
  for snapshot_table_info in models:
3227
+ category_str = SNAPSHOT_CHANGE_CATEGORY_STR[snapshot_table_info.change_category]
3227
3228
  self._print(
3228
- f"- `{snapshot_table_info.display_name(environment_naming_info, default_catalog if self.verbosity < Verbosity.VERY_VERBOSE else None, dialect=self.dialect)}`"
3229
+ f"- `{snapshot_table_info.display_name(environment_naming_info, default_catalog if self.verbosity < Verbosity.VERY_VERBOSE else None, dialect=self.dialect)}` ({category_str})"
3229
3230
  )
3230
3231
 
3231
3232
  def _print_modified_models(
@@ -3237,7 +3238,7 @@ class MarkdownConsole(CaptureTerminalConsole):
3237
3238
  no_diff: bool = True,
3238
3239
  ) -> None:
3239
3240
  directly_modified = []
3240
- indirectly_modified = []
3241
+ indirectly_modified: t.List[Snapshot] = []
3241
3242
  metadata_modified = []
3242
3243
  for snapshot in modified_snapshots:
3243
3244
  if context_diff.directly_modified(snapshot.name):
@@ -3249,11 +3250,40 @@ class MarkdownConsole(CaptureTerminalConsole):
3249
3250
  if directly_modified:
3250
3251
  self._print("\n**Directly Modified:**")
3251
3252
  for snapshot in sorted(directly_modified):
3253
+ category_str = SNAPSHOT_CHANGE_CATEGORY_STR[snapshot.change_category]
3252
3254
  self._print(
3253
- f"- `{snapshot.display_name(environment_naming_info, default_catalog if self.verbosity < Verbosity.VERY_VERBOSE else None, dialect=self.dialect)}`"
3255
+ f"* `{snapshot.display_name(environment_naming_info, default_catalog if self.verbosity < Verbosity.VERY_VERBOSE else None, dialect=self.dialect)}` ({category_str})"
3256
+ )
3257
+
3258
+ indirectly_modified_children = sorted(
3259
+ [s for s in indirectly_modified if snapshot.snapshot_id in s.parents]
3254
3260
  )
3261
+
3255
3262
  if not no_diff:
3256
- self._print(f"```diff\n{context_diff.text_diff(snapshot.name)}\n```")
3263
+ diff_text = context_diff.text_diff(snapshot.name)
3264
+ # sometimes there is no text_diff, like on a seed model where the data has been updated
3265
+ if diff_text:
3266
+ diff_text = f"\n```diff\n{diff_text}\n```"
3267
+ # these are part of a Markdown list, so indent them by 2 spaces to relate them to the current list item
3268
+ diff_text_indented = "\n".join(
3269
+ [f" {line}" for line in diff_text.splitlines()]
3270
+ )
3271
+ self._print(diff_text_indented)
3272
+ else:
3273
+ if indirectly_modified_children:
3274
+ self._print("\n")
3275
+
3276
+ if indirectly_modified_children:
3277
+ self._print(" Indirectly Modified Children:")
3278
+ for child_snapshot in indirectly_modified_children:
3279
+ child_category_str = SNAPSHOT_CHANGE_CATEGORY_STR[
3280
+ child_snapshot.change_category
3281
+ ]
3282
+ self._print(
3283
+ f" - `{child_snapshot.display_name(environment_naming_info, default_catalog if self.verbosity < Verbosity.VERY_VERBOSE else None, dialect=self.dialect)}` ({child_category_str})"
3284
+ )
3285
+ self._print("\n")
3286
+
3257
3287
  if indirectly_modified:
3258
3288
  self._print("\n**Indirectly Modified:**")
3259
3289
  self._print_models_with_threshold(
@@ -115,8 +115,12 @@ def _update_pr_environment(controller: GithubController) -> bool:
115
115
  conclusion = controller.update_pr_environment_check(status=GithubCheckStatus.COMPLETED)
116
116
  return conclusion is not None and conclusion.is_success
117
117
  except Exception as e:
118
+ logger.exception("Error occurred when updating PR environment")
118
119
  conclusion = controller.update_pr_environment_check(
119
- status=GithubCheckStatus.COMPLETED, exception=e, plan=controller.pr_plan_or_none
120
+ status=GithubCheckStatus.COMPLETED,
121
+ exception=e,
122
+ plan=controller.pr_plan_or_none,
123
+ plan_flags=controller.pr_plan_flags,
120
124
  )
121
125
  return (
122
126
  conclusion is not None
@@ -147,6 +151,7 @@ def _gen_prod_plan(controller: GithubController) -> bool:
147
151
  )
148
152
  return bool(plan_summary)
149
153
  except Exception as e:
154
+ logger.exception("Error occurred generating prod plan")
150
155
  controller.update_prod_plan_preview_check(
151
156
  status=GithubCheckStatus.COMPLETED,
152
157
  conclusion=GithubCheckConclusion.FAILURE,
@@ -211,6 +216,8 @@ def deploy_production(ctx: click.Context) -> None:
211
216
 
212
217
 
213
218
  def _run_all(controller: GithubController) -> None:
219
+ click.echo(f"SQLMesh Version: {controller.version_info}")
220
+
214
221
  has_required_approval = False
215
222
  is_auto_deploying_prod = (
216
223
  controller.deploy_command_enabled or controller.do_required_approval_check
@@ -9,11 +9,11 @@ import re
9
9
  import traceback
10
10
  import typing as t
11
11
  from enum import Enum
12
- from typing import List
13
12
  from pathlib import Path
13
+ from dataclasses import dataclass
14
+ from functools import cached_property
14
15
 
15
16
  import requests
16
- from hyperscript import Element, h
17
17
  from sqlglot.helper import seq_get
18
18
 
19
19
  from sqlmesh.core import constants as c
@@ -21,13 +21,13 @@ from sqlmesh.core.console import SNAPSHOT_CHANGE_CATEGORY_STR, get_console, Mark
21
21
  from sqlmesh.core.context import Context
22
22
  from sqlmesh.core.test.result import ModelTextTestResult
23
23
  from sqlmesh.core.environment import Environment
24
- from sqlmesh.core.plan import Plan, PlanBuilder
24
+ from sqlmesh.core.plan import Plan, PlanBuilder, SnapshotIntervals
25
+ from sqlmesh.core.plan.definition import UserProvidedFlags
25
26
  from sqlmesh.core.snapshot.definition import (
26
27
  Snapshot,
27
28
  SnapshotChangeCategory,
28
29
  SnapshotId,
29
30
  SnapshotTableInfo,
30
- format_intervals,
31
31
  )
32
32
  from sqlglot.errors import SqlglotError
33
33
  from sqlmesh.core.user import User
@@ -415,6 +415,14 @@ class GithubController:
415
415
  except:
416
416
  return None
417
417
 
418
+ @property
419
+ def pr_plan_flags(self) -> t.Optional[t.Dict[str, UserProvidedFlags]]:
420
+ if pr_plan := self.pr_plan_or_none:
421
+ return pr_plan.user_provided_flags
422
+ if pr_plan_builder := self._pr_plan_builder:
423
+ return pr_plan_builder._user_provided_flags
424
+ return None
425
+
418
426
  @property
419
427
  def prod_plan(self) -> Plan:
420
428
  if not self._prod_plan_builder:
@@ -434,7 +442,9 @@ class GithubController:
434
442
  if not self._prod_plan_with_gaps_builder:
435
443
  self._prod_plan_with_gaps_builder = self._context.plan_builder(
436
444
  c.PROD,
445
+ # this is required to highlight any data gaps between this PR environment and prod (since PR environments may only contain a subset of data)
437
446
  no_gaps=False,
447
+ # this works because the snapshots were already categorized when applying self.pr_plan so there are no uncategorized local snapshots to trigger a plan error
438
448
  no_auto_categorization=True,
439
449
  skip_tests=True,
440
450
  skip_linter=True,
@@ -495,9 +505,17 @@ class GithubController:
495
505
  difference_summary = self._console.consume_captured_output()
496
506
  self._console._show_missing_dates(plan, self._context.default_catalog)
497
507
  missing_dates = self._console.consume_captured_output()
508
+
509
+ plan_flags_section = (
510
+ f"\n\n{self._generate_plan_flags_section(plan.user_provided_flags)}"
511
+ if plan.user_provided_flags
512
+ else ""
513
+ )
514
+
498
515
  if not difference_summary and not missing_dates:
499
- return "No changes to apply."
500
- return f"{difference_summary}\n{missing_dates}"
516
+ return f"No changes to apply.{plan_flags_section}"
517
+
518
+ return f"{difference_summary}\n{missing_dates}{plan_flags_section}"
501
519
  except PlanError as e:
502
520
  return f"Plan failed to generate. Check for pending or unresolved changes. Error: {e}"
503
521
 
@@ -831,6 +849,9 @@ class GithubController:
831
849
  status: GithubCheckStatus,
832
850
  exception: t.Optional[Exception] = None,
833
851
  plan: t.Optional[Plan] = None,
852
+ plan_flags: t.Optional[
853
+ t.Dict[str, UserProvidedFlags]
854
+ ] = None, # note: the plan flags are passed separately in case the plan fails to build
834
855
  ) -> t.Optional[GithubCheckConclusion]:
835
856
  """
836
857
  Updates the status of the merge commit for the PR environment.
@@ -852,58 +873,16 @@ class GithubController:
852
873
  conclusion: GithubCheckConclusion, exception: t.Optional[Exception]
853
874
  ) -> t.Tuple[GithubCheckConclusion, str, t.Optional[str]]:
854
875
  if conclusion.is_success:
855
- if not self.modified_snapshots:
876
+ prod_plan = self.prod_plan_with_gaps
877
+
878
+ if not prod_plan.has_changes:
856
879
  summary = "No models were modified in this PR.\n"
857
880
  else:
858
- header_rows = [
859
- h("th", {"colspan": "3"}, "PR Environment Summary"),
860
- [
861
- h("th", "Model"),
862
- h("th", "Change Type"),
863
- h("th", "Dates Loaded"),
864
- ],
865
- ]
866
- body_rows: List[Element | List[Element]] = []
867
- for modified_snapshot in self.modified_snapshots.values():
868
- # We don't want to display indirect non-breaking since to users these are effectively no-op changes
869
- if modified_snapshot.is_indirect_non_breaking:
870
- continue
871
- if modified_snapshot.snapshot_id in self.removed_snapshots:
872
- # This will be an FQN since we don't have access to node name from a snapshot table info
873
- # which is what a removed snapshot is
874
- model_name = modified_snapshot.name
875
- change_category = SNAPSHOT_CHANGE_CATEGORY_STR[
876
- SnapshotChangeCategory.BREAKING
877
- ]
878
- interval_output = "REMOVED"
879
- else:
880
- assert isinstance(modified_snapshot, Snapshot)
881
- model_name = modified_snapshot.node.name
882
- change_category = (
883
- "Uncategorized"
884
- if not modified_snapshot.change_category
885
- else SNAPSHOT_CHANGE_CATEGORY_STR[modified_snapshot.change_category]
886
- )
887
- intervals = (
888
- modified_snapshot.dev_intervals
889
- if modified_snapshot.is_forward_only
890
- else modified_snapshot.intervals
891
- )
892
- interval_output = (
893
- format_intervals(intervals, modified_snapshot.node.interval_unit)
894
- if intervals
895
- else "N/A"
896
- )
897
- body_rows.append(
898
- [
899
- h("td", model_name, autoescape=False),
900
- h("td", change_category),
901
- h("td", interval_output),
902
- ]
903
- )
904
- table_header = h("thead", [h("tr", row) for row in header_rows])
905
- table_body = h("tbody", [h("tr", row) for row in body_rows])
906
- summary = str(h("table", [table_header, table_body]))
881
+ intro = self._generate_pr_environment_summary_intro()
882
+ summary = intro + self._generate_pr_environment_summary_list(prod_plan)
883
+ if prod_plan.user_provided_flags:
884
+ summary += self._generate_plan_flags_section(prod_plan.user_provided_flags)
885
+
907
886
  vde_title = (
908
887
  "- :eyes: To **review** this PR's changes, use virtual data environment:"
909
888
  )
@@ -943,7 +922,11 @@ class GithubController:
943
922
  "If you would like the bot to automatically categorize changes, check the [documentation](https://sqlmesh.readthedocs.io/en/stable/integrations/github/) for more information."
944
923
  )
945
924
  elif isinstance(exception, PlanError):
946
- failure_msg = f"Plan application failed.\n\n{self._console.captured_output}"
925
+ failure_msg = f"Plan application failed.\n"
926
+ if exception.args and (msg := exception.args[0]) and isinstance(msg, str):
927
+ failure_msg += f"\n{msg}\n"
928
+ if self._console.captured_output:
929
+ failure_msg += f"\n{self._console.captured_output}"
947
930
  elif isinstance(exception, (SQLMeshError, SqlglotError, ValueError)):
948
931
  # this logic is taken from the global error handler attached to the CLI, which uses `click.echo()` to output the message
949
932
  # so cant be re-used here because it bypasses the Console
@@ -959,13 +942,17 @@ class GithubController:
959
942
 
960
943
  conclusion_to_summary = {
961
944
  GithubCheckConclusion.SKIPPED: f":next_track_button: Skipped creating or updating PR Environment `{self.pr_environment_name}`. {skip_reason}",
962
- GithubCheckConclusion.FAILURE: f":x: Failed to create or update PR Environment `{self.pr_environment_name}`.\n\n{failure_msg}",
945
+ GithubCheckConclusion.FAILURE: f":x: Failed to create or update PR Environment `{self.pr_environment_name}` :x:\n\n{failure_msg}",
963
946
  GithubCheckConclusion.CANCELLED: f":stop_sign: Cancelled creating or updating PR Environment `{self.pr_environment_name}`",
964
947
  GithubCheckConclusion.ACTION_REQUIRED: f":warning: Action Required to create or update PR Environment `{self.pr_environment_name}` :warning:\n\n{failure_msg}",
965
948
  }
966
949
  summary = conclusion_to_summary.get(
967
950
  conclusion, f":interrobang: Got an unexpected conclusion: {conclusion.value}"
968
951
  )
952
+ if plan_flags:
953
+ plan_flags_section = self._generate_plan_flags_section(plan_flags)
954
+ summary += f"\n\n{plan_flags_section}"
955
+
969
956
  self._append_output("pr_environment_name", self.pr_environment_name)
970
957
  return conclusion, check_title, summary
971
958
 
@@ -1006,6 +993,12 @@ class GithubController:
1006
993
  title = conclusion_to_title.get(
1007
994
  conclusion, f"Got an unexpected conclusion: {conclusion.value}"
1008
995
  )
996
+ if conclusion == GithubCheckConclusion.SUCCESS and summary:
997
+ summary = (
998
+ f"This is a preview that shows the differences between this PR environment `{self.pr_environment_name}` and `prod`.\n\n"
999
+ "These are the changes that would be deployed.\n\n"
1000
+ ) + summary
1001
+
1009
1002
  return conclusion, title, summary
1010
1003
 
1011
1004
  self._update_check_handler(
@@ -1115,3 +1108,232 @@ class GithubController:
1115
1108
  @property
1116
1109
  def running_in_github_actions(self) -> bool:
1117
1110
  return os.environ.get("GITHUB_ACTIONS", None) == "true"
1111
+
1112
+ @property
1113
+ def version_info(self) -> str:
1114
+ from sqlmesh.cli.main import _sqlmesh_version
1115
+
1116
+ return _sqlmesh_version()
1117
+
1118
+ def _generate_plan_flags_section(
1119
+ self, user_provided_flags: t.Dict[str, UserProvidedFlags]
1120
+ ) -> str:
1121
+ # collapsed section syntax:
1122
+ # https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/organizing-information-with-collapsed-sections#creating-a-collapsed-section
1123
+ section = "<details>\n\n<summary>Plan flags</summary>\n\n"
1124
+ for flag_name, flag_value in user_provided_flags.items():
1125
+ section += f"- `{flag_name}` = `{flag_value}`\n"
1126
+ section += "\n</details>"
1127
+
1128
+ return section
1129
+
1130
+ def _generate_pr_environment_summary_intro(self) -> str:
1131
+ note = ""
1132
+ subset_reasons = []
1133
+
1134
+ if self.bot_config.skip_pr_backfill:
1135
+ subset_reasons.append("`skip_pr_backfill` is enabled")
1136
+
1137
+ if default_pr_start := self.bot_config.default_pr_start:
1138
+ subset_reasons.append(f"`default_pr_start` is set to `{default_pr_start}`")
1139
+
1140
+ if subset_reasons:
1141
+ note = (
1142
+ "> [!IMPORTANT]\n"
1143
+ f"> This PR environment may only contain a subset of data because:\n"
1144
+ + "\n".join(f"> - {r}" for r in subset_reasons)
1145
+ + "\n"
1146
+ "> \n"
1147
+ "> This means that deploying to `prod` may not be a simple virtual update if there is still some data to load.\n"
1148
+ "> See `Dates not loaded in PR` below or the `Prod Plan Preview` check for more information.\n\n"
1149
+ )
1150
+
1151
+ return (
1152
+ f"Here is a summary of data that has been loaded into the PR environment `{self.pr_environment_name}` and could be deployed to `prod`.\n\n"
1153
+ + note
1154
+ )
1155
+
1156
+ def _generate_pr_environment_summary_list(self, plan: Plan) -> str:
1157
+ added_snapshot_ids = set(plan.context_diff.added)
1158
+ modified_snapshot_ids = set(
1159
+ s.snapshot_id for s, _ in plan.context_diff.modified_snapshots.values()
1160
+ )
1161
+ removed_snapshot_ids = set(plan.context_diff.removed_snapshots.keys())
1162
+
1163
+ # note: we sort these to get a deterministic order for the output tests
1164
+ table_records = sorted(
1165
+ [
1166
+ SnapshotSummaryRecord(snapshot_id=snapshot_id, plan=plan)
1167
+ for snapshot_id in (
1168
+ added_snapshot_ids | modified_snapshot_ids | removed_snapshot_ids
1169
+ )
1170
+ ],
1171
+ key=lambda r: r.display_name,
1172
+ )
1173
+
1174
+ sections = [
1175
+ ("### Added", [r for r in table_records if r.is_added]),
1176
+ ("### Removed", [r for r in table_records if r.is_removed]),
1177
+ ("### Directly Modified", [r for r in table_records if r.is_directly_modified]),
1178
+ ("### Indirectly Modified", [r for r in table_records if r.is_indirectly_modified]),
1179
+ (
1180
+ "### Metadata Updated",
1181
+ [r for r in table_records if r.is_metadata_updated and not r.is_modified],
1182
+ ),
1183
+ ]
1184
+
1185
+ summary = ""
1186
+ for title, records in sections:
1187
+ if records:
1188
+ summary += f"\n{title}\n"
1189
+
1190
+ for record in records:
1191
+ summary += f"{record.as_markdown_list_item}\n"
1192
+
1193
+ return summary
1194
+
1195
+
1196
+ @dataclass
1197
+ class SnapshotSummaryRecord:
1198
+ snapshot_id: SnapshotId
1199
+ plan: Plan
1200
+
1201
+ @property
1202
+ def snapshot(self) -> Snapshot:
1203
+ if self.is_removed:
1204
+ raise ValueError("Removed snapshots only have SnapshotTableInfo available")
1205
+ return self.plan.snapshots[self.snapshot_id]
1206
+
1207
+ @cached_property
1208
+ def snapshot_table_info(self) -> SnapshotTableInfo:
1209
+ if self.is_removed:
1210
+ return self.plan.modified_snapshots[self.snapshot_id].table_info
1211
+ return self.plan.snapshots[self.snapshot_id].table_info
1212
+
1213
+ @property
1214
+ def display_name(self) -> str:
1215
+ dialect = None if self.is_removed else self.snapshot.node.dialect
1216
+ return self.snapshot_table_info.display_name(
1217
+ self.plan.environment_naming_info, default_catalog=None, dialect=dialect
1218
+ )
1219
+
1220
+ @property
1221
+ def change_category(self) -> str:
1222
+ if self.is_removed:
1223
+ return SNAPSHOT_CHANGE_CATEGORY_STR[SnapshotChangeCategory.BREAKING]
1224
+
1225
+ if change_category := self.snapshot.change_category:
1226
+ return SNAPSHOT_CHANGE_CATEGORY_STR[change_category]
1227
+
1228
+ return "Uncategorized"
1229
+
1230
+ @property
1231
+ def is_added(self) -> bool:
1232
+ return self.snapshot_id in self.plan.context_diff.added
1233
+
1234
+ @property
1235
+ def is_removed(self) -> bool:
1236
+ return self.snapshot_id in self.plan.context_diff.removed_snapshots
1237
+
1238
+ @property
1239
+ def is_dev_preview(self) -> bool:
1240
+ return not self.plan.deployability_index.is_deployable(self.snapshot_id)
1241
+
1242
+ @property
1243
+ def is_directly_modified(self) -> bool:
1244
+ return self.plan.context_diff.directly_modified(self.snapshot_table_info.name)
1245
+
1246
+ @property
1247
+ def is_indirectly_modified(self) -> bool:
1248
+ return self.plan.context_diff.indirectly_modified(self.snapshot_table_info.name)
1249
+
1250
+ @property
1251
+ def is_modified(self) -> bool:
1252
+ return self.is_directly_modified or self.is_indirectly_modified
1253
+
1254
+ @property
1255
+ def is_metadata_updated(self) -> bool:
1256
+ return self.plan.context_diff.metadata_updated(self.snapshot_table_info.name)
1257
+
1258
+ @property
1259
+ def is_incremental(self) -> bool:
1260
+ return self.snapshot_table_info.is_incremental
1261
+
1262
+ @property
1263
+ def modification_type(self) -> str:
1264
+ if self.is_directly_modified:
1265
+ return "Directly modified"
1266
+ if self.is_indirectly_modified:
1267
+ return "Indirectly modified"
1268
+ if self.is_metadata_updated:
1269
+ return "Metadata updated"
1270
+
1271
+ return "Unknown"
1272
+
1273
+ @property
1274
+ def loaded_intervals(self) -> SnapshotIntervals:
1275
+ if self.is_removed:
1276
+ raise ValueError("Removed snapshots dont have loaded intervals available")
1277
+
1278
+ return SnapshotIntervals(
1279
+ snapshot_id=self.snapshot_id,
1280
+ intervals=(
1281
+ self.snapshot.dev_intervals
1282
+ if self.snapshot.is_forward_only
1283
+ else self.snapshot.intervals
1284
+ ),
1285
+ )
1286
+
1287
+ @property
1288
+ def loaded_intervals_rendered(self) -> str:
1289
+ if self.is_removed:
1290
+ return "REMOVED"
1291
+
1292
+ return self._format_intervals(self.loaded_intervals)
1293
+
1294
+ @property
1295
+ def missing_intervals(self) -> t.Optional[SnapshotIntervals]:
1296
+ return next(
1297
+ (si for si in self.plan.missing_intervals if si.snapshot_id == self.snapshot_id),
1298
+ None,
1299
+ )
1300
+
1301
+ @property
1302
+ def missing_intervals_formatted(self) -> str:
1303
+ if not self.is_removed and (intervals := self.missing_intervals):
1304
+ return self._format_intervals(intervals)
1305
+
1306
+ return "N/A"
1307
+
1308
+ @property
1309
+ def as_markdown_list_item(self) -> str:
1310
+ if self.is_removed:
1311
+ return f"- `{self.display_name}` ({self.change_category})"
1312
+
1313
+ how_applied = ""
1314
+
1315
+ if not self.is_incremental:
1316
+ from sqlmesh.core.console import _format_missing_intervals
1317
+
1318
+ # note: this is to re-use the '[recreate view]' and '[full refresh]' text and keep it in sync with updates to the CLI
1319
+ # it doesnt actually use the passed intervals, those are handled differently
1320
+ how_applied = _format_missing_intervals(self.snapshot, self.loaded_intervals)
1321
+
1322
+ how_applied_str = f" [{how_applied}]" if how_applied else ""
1323
+
1324
+ item = f"- `{self.display_name}` ({self.change_category})\n"
1325
+
1326
+ if self.snapshot_table_info.model_kind_name:
1327
+ item += f" **Kind:** {self.snapshot_table_info.model_kind_name}{how_applied_str}\n"
1328
+
1329
+ if self.is_incremental:
1330
+ # in-depth interval info is only relevant for incremental models
1331
+ item += f" **Dates loaded in PR:** [{self.loaded_intervals_rendered}]\n"
1332
+ if self.missing_intervals:
1333
+ item += f" **Dates *not* loaded in PR:** [{self.missing_intervals_formatted}]\n"
1334
+
1335
+ return item
1336
+
1337
+ def _format_intervals(self, intervals: SnapshotIntervals) -> str:
1338
+ preview_modifier = " (**preview**)" if self.is_dev_preview else ""
1339
+ return f"{intervals.format_intervals(self.snapshot.node.interval_unit)}{preview_modifier}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlmesh
3
- Version: 0.199.1.dev1
3
+ Version: 0.199.1.dev2
4
4
  Summary: Next-generation data transformation framework
5
5
  Author-email: "TobikoData Inc." <engineering@tobikodata.com>
6
6
  License: Apache License
@@ -1,5 +1,5 @@
1
1
  sqlmesh/__init__.py,sha256=QjwaamJIdW7voXoS880yAx7tHm-Im4_mqGA-XJgafos,7631
2
- sqlmesh/_version.py,sha256=zOqzr9tB5Ig4Cqi2rqDvOF1BHWDDqZBx5ng9PsPP81o,528
2
+ sqlmesh/_version.py,sha256=xr44hQ2o7jmOZ347ejlZNhnmweEDkJf_mQ6dquBDsDM,528
3
3
  sqlmesh/magics.py,sha256=R9U0Mp9ZQhDYe751nxAQDLaMv3zLAR4HcTdIflKTmEE,41709
4
4
  sqlmesh/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  sqlmesh/cicd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -11,7 +11,7 @@ sqlmesh/cli/options.py,sha256=-4ZclMIeIg8tghLowtmQNmpEFlm0QtOVEh40NqRdFHA,2499
11
11
  sqlmesh/cli/project_init.py,sha256=5JmDmUK0WJs39hM-p76uzHYdo1Hnx6fKuJg_pR3Hn5A,16419
12
12
  sqlmesh/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  sqlmesh/core/_typing.py,sha256=e1f7xO2aZCy38M_3VoDy-XI3shyd99-IFctgIFSgXWM,497
14
- sqlmesh/core/console.py,sha256=HInr6MOcxgn8Z-B9fAoghoDXbBEZqkB5ClyZF-fRP58,159032
14
+ sqlmesh/core/console.py,sha256=ieSXS0G--HpVgziwAqgcJvE4zUn60xRswljpc1GO6pU,160753
15
15
  sqlmesh/core/constants.py,sha256=gUABXWYgH_XOSbaZAJsR5o1dmA61JDCvBDyxqqXX3LM,2630
16
16
  sqlmesh/core/context.py,sha256=K9bQqk5ATZ0cl7v7qaiOQq-b7hYfPIgdo9G9v51Y6y8,119111
17
17
  sqlmesh/core/context_diff.py,sha256=CeUmMBJzN__98YohrtAiIYPIZI9mrGBk6bVADWBLUw8,21692
@@ -162,9 +162,9 @@ sqlmesh/integrations/llm.py,sha256=URgp3JKr06HYv6hGlJ7DQ9m3QCc41xRWK23HzmGpbDI,1
162
162
  sqlmesh/integrations/slack.py,sha256=nxLxu5WztGbZH3JdqnzyPqmJUMfRdJ_49LQ7zR-u39Q,6801
163
163
  sqlmesh/integrations/github/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
164
164
  sqlmesh/integrations/github/cicd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
165
- sqlmesh/integrations/github/cicd/command.py,sha256=1tsLOrGibDwoKKgFWf03H8OKOoAW3TWLesXuBRYTtBY,11824
165
+ sqlmesh/integrations/github/cicd/command.py,sha256=neBs7BfuywRR_AnB8pXHMlEmgFe2-WPJka_KsE9YzkY,12097
166
166
  sqlmesh/integrations/github/cicd/config.py,sha256=tRhMZaLf1p6AWXvi67n85Q-lptJNGt1Wk_Jr61ytpiM,2104
167
- sqlmesh/integrations/github/cicd/controller.py,sha256=QCs51Ag52Eu-2C5B2ivJ3Jgh7yThKj0eBqILGCHqizk,44436
167
+ sqlmesh/integrations/github/cicd/controller.py,sha256=I8jnpS2hvYwa-dpki0Cly246eeBM9-9Qy9loxw4bMpI,52284
168
168
  sqlmesh/lsp/api.py,sha256=_lA_buSPZFIoLJuINbS53LbNOOaHp3orb0uFTE3aCqI,2328
169
169
  sqlmesh/lsp/completions.py,sha256=Nt1XpFL_-QAIRM8BBBC6Ia6YG0hMZriiNJra7nGfD4Q,6676
170
170
  sqlmesh/lsp/context.py,sha256=2aD_lH-GqAwCjTFHfCavH2fZz2T4IXQZVS7LDMAa2X4,12815
@@ -284,7 +284,7 @@ sqlmesh/utils/pydantic.py,sha256=o_NsXbIpDqNpUA1Uc5xF0ZzoXQYYB0DfHwdRxBwXPNk,120
284
284
  sqlmesh/utils/rich.py,sha256=BpB47G0-JcU41LkGkeGKroexj4NOV5fox4-GOIXPgIc,3545
285
285
  sqlmesh/utils/windows.py,sha256=r7Yin4WEwbvH7sKA3hOJtRsclmscNuBeCklCfVPvtQs,611
286
286
  sqlmesh/utils/yaml.py,sha256=hvo5lmybYLcIx4p6CQwufS8X4MbwS60lEBnCmYJCXmU,2336
287
- sqlmesh-0.199.1.dev1.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
287
+ sqlmesh-0.199.1.dev2.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
288
288
  web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
289
289
  web/client/dist/index.html,sha256=kNpFjWxt34Pf-P-cROtPWi3GNSVk6zqA3eoN9tlZXI0,5472
290
290
  web/client/dist/assets/Audits-DKgXravk.js,sha256=SaVQhNlislD_yQc_KivO7SPi1JH0ts1GnDPD5Zssutw,1041
@@ -402,8 +402,8 @@ web/server/api/endpoints/models.py,sha256=ud-lRwriyI_OYg9tZchrBgeiXELiRTLNEodraf
402
402
  web/server/api/endpoints/modules.py,sha256=8hqqgonGay_mJmpCw0IdbjsPhWlQH2VLdKAqha-myac,468
403
403
  web/server/api/endpoints/plan.py,sha256=bbbY50W_2MsZSTxOHWMKz0tbIm75nsRSlPy8GI2fg9Q,9306
404
404
  web/server/api/endpoints/table_diff.py,sha256=SCy3-pr5x6bEEiOaZGCDbiHQhpgxwmnz3Acqa2Ie6jI,2090
405
- sqlmesh-0.199.1.dev1.dist-info/METADATA,sha256=J9wTKr23lqz22qxIQ2oQZga5pQJ7auPT1rBlGN0qFxY,26658
406
- sqlmesh-0.199.1.dev1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
407
- sqlmesh-0.199.1.dev1.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
408
- sqlmesh-0.199.1.dev1.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
409
- sqlmesh-0.199.1.dev1.dist-info/RECORD,,
405
+ sqlmesh-0.199.1.dev2.dist-info/METADATA,sha256=EC4u5rjn5cBDWonMr78Q0TCJHLbRJ4vZUx3Ay9ui6lo,26658
406
+ sqlmesh-0.199.1.dev2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
407
+ sqlmesh-0.199.1.dev2.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
408
+ sqlmesh-0.199.1.dev2.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
409
+ sqlmesh-0.199.1.dev2.dist-info/RECORD,,