Skip to content

Commit 13fcf9c

Browse files
bug fixes and lint fixes
1 parent 9126dd5 commit 13fcf9c

File tree

15 files changed

+1467
-284
lines changed

15 files changed

+1467
-284
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,4 +213,8 @@ backend/backend/utils/load_models/yaml_models.yaml
213213

214214
# macOS
215215
.DS_Store
216-
**/.DS_Store
216+
**/.DS_Store
217+
218+
# Local OSS virtual environment
219+
backend/.ossvenv/
220+
backend/pdm.lock

CLAUDE.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,12 @@ Frontend:
4343
- [x] `execute_version`: wired + concurrency-safe (Redis lock + `select_for_update`)
4444
- [x] `DraftValidator`: implemented at `backend/application/model_validator/draft_validator.py`
4545
- [x] `validate_draft` view: fully wired to DraftValidator
46+
47+
### Bug fixes (post-migration)
48+
- `execute_version`: fixed concurrency (Redis lock + `select_for_update`)
49+
- `_serialize_version`: added `is_current` to response dict
50+
- `get_draft_status`: fixed `committed_data` lookup to use project-level ModelVersion (`config_model=None`) instead of model-scoped versions
51+
- dual-write: added `skip_draft_write` flag to `_update_model` — execution pipeline no longer creates false draft records
52+
- `set_current_version`: added cache invalidation after DB update
53+
- `handleExecuteSuccess`: added `loadDraftStatus()` call to clear stale draft indicator after execute
4654
---

backend/backend/application/context/application.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -447,25 +447,26 @@ def _get_source_dependent_models(
447447
children.add(name)
448448
return children
449449

450-
def _update_model(self, model_name: str, model_data: dict[str, Any]) -> None:
451-
"""This method is used to update the model in a database and redis :param
452-
model_name:
450+
def _update_model(self, model_name: str, model_data: dict[str, Any], skip_draft_write: bool = False) -> None:
451+
"""This method is used to update the model in a database and redis.
453452
454453
:param model_name:
455454
:param model_data:
455+
:param skip_draft_write: True when called from execution pipeline (not user edits)
456456
:return:
457457
"""
458458
self.session.update_model(model_name=model_name, model_data=model_data)
459459
# Dual-write: keep UserDraft in sync so draft status is accurate
460-
try:
461-
from backend.core.services.draft_service import (
462-
get_or_create_draft, save_draft_data,
463-
)
464-
config_model = self.session.fetch_model(model_name=model_name)
465-
draft = get_or_create_draft(config_model=config_model)
466-
save_draft_data(draft=draft, model_data=model_data)
467-
except Exception:
468-
logger.debug("Draft dual-write failed for %s — non-blocking", model_name)
460+
if not skip_draft_write:
461+
try:
462+
from backend.core.services.draft_service import (
463+
get_or_create_draft, save_draft_data,
464+
)
465+
config_model = self.session.fetch_model(model_name=model_name)
466+
draft = get_or_create_draft(config_model=config_model)
467+
save_draft_data(draft=draft, model_data=model_data)
468+
except Exception:
469+
logger.debug("Draft dual-write failed for %s — non-blocking", model_name)
469470
# Updating the model spec in cache
470471
if visitran_models := self.session.redis_client.get(self.redis_model_key):
471472
models = yaml.safe_load(visitran_models)
@@ -479,11 +480,11 @@ def _update_model(self, model_name: str, model_data: dict[str, Any]) -> None:
479480
yaml_models = yaml.dump(models, default_flow_style=False, sort_keys=False)
480481
self.session.redis_client.set(self.redis_model_key, yaml_models)
481482

482-
def update_model(self, model_name: str, model_data: dict[str, Any]):
483+
def update_model(self, model_name: str, model_data: dict[str, Any], skip_draft_write: bool = False):
483484
# Converting the current model to python.
484485
self.update_model_graph(model_data, model_name)
485486
parser: ConfigParser = self.convert_to_python(model_data, model_name)
486-
self._update_model(model_name=model_name, model_data=model_data)
487+
self._update_model(model_name=model_name, model_data=model_data, skip_draft_write=skip_draft_write)
487488
sequence_orders, sequence_lineage = set_transformation_sequence(parser)
488489

489490
model_data_yaml: Any | str = yaml.dump(model_data, indent=4, default_flow_style=False)
@@ -734,7 +735,7 @@ def execute_run(self, environment_id=None, model_name: str = None, model_names:
734735
for model in self.session.fetch_all_models(fetch_all=True):
735736
if model_data := model.model_data:
736737
logging.info(f"[Model Update] Converting YAML to Python: {model.model_name}")
737-
self.update_model(model_name=model.model_name, model_data=model_data)
738+
self.update_model(model_name=model.model_name, model_data=model_data, skip_draft_write=True)
738739
self.session.add_sys_path()
739740
visitran_obj.search_n_run_models(model_name=model_name, model_names=model_names)
740741
return visitran_obj

backend/backend/core/services/model_version_service.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ def set_current_version(project_instance: ProjectDetails, version_number: int) -
8080
project_instance=project_instance, config_model__isnull=True,
8181
version_number=version_number,
8282
).update(is_current=True)
83+
vcache.invalidate_model_versions(str(project_instance.project_uuid))
8384

8485

8586
def _build_all_model_data(project_instance: ProjectDetails) -> dict[str, dict]:

frontend/src/ide/chat-ai/ChatLayout.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,10 @@ function ChatLayout({ children }) {
401401
onClick={() => handleRightDrawer(DRAWER_TYPES.VERSION_HISTORY)}
402402
role="button"
403403
tabIndex={0}
404-
onKeyDown={(e) => e.key === "Enter" && handleRightDrawer(DRAWER_TYPES.VERSION_HISTORY)}
404+
onKeyDown={(e) =>
405+
e.key === "Enter" &&
406+
handleRightDrawer(DRAWER_TYPES.VERSION_HISTORY)
407+
}
405408
>
406409
<HistoryOutlined />
407410
</div>

frontend/src/ide/version-history/CommitModal.jsx

Lines changed: 93 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,25 +27,41 @@ function CommitModal({ onCommitSuccess }) {
2727
const orgId = orgStore.getState().selectedOrgId;
2828
const csrfToken = Cookies.get("csrftoken");
2929
const isOpen = useVersionHistoryStore((state) => state.isCommitModalOpen);
30-
const closeCommitModal = useVersionHistoryStore((state) => state.closeCommitModal);
30+
const closeCommitModal = useVersionHistoryStore(
31+
(state) => state.closeCommitModal
32+
);
3133

3234
useEffect(() => {
3335
if (!isOpen) return;
3436
let cancelled = false;
3537
setPreviewLoading(true);
3638
setPreviewData(null);
3739
fetchPendingChanges(axiosRef, orgId, projectId)
38-
.then((data) => { if (!cancelled) setPreviewData(data); })
39-
.catch(() => { if (!cancelled) setPreviewData(null); })
40-
.finally(() => { if (!cancelled) setPreviewLoading(false); });
41-
return () => { cancelled = true; };
40+
.then((data) => {
41+
if (!cancelled) setPreviewData(data);
42+
})
43+
.catch(() => {
44+
if (!cancelled) setPreviewData(null);
45+
})
46+
.finally(() => {
47+
if (!cancelled) setPreviewLoading(false);
48+
});
49+
return () => {
50+
cancelled = true;
51+
};
4252
}, [isOpen]); // eslint-disable-line
4353

4454
const handleOk = async () => {
4555
if (!commitMessage.trim()) return;
4656
setLoading(true);
4757
try {
48-
await commitProjectVersion(axiosRef, orgId, projectId, csrfToken, commitMessage.trim());
58+
await commitProjectVersion(
59+
axiosRef,
60+
orgId,
61+
projectId,
62+
csrfToken,
63+
commitMessage.trim()
64+
);
4965
notify({ type: "success", message: "Version committed successfully" });
5066
setCommitMessage("");
5167
closeCommitModal();
@@ -57,28 +73,83 @@ function CommitModal({ onCommitSuccess }) {
5773
}
5874
};
5975

60-
const handleCancel = () => { setCommitMessage(""); setPreviewData(null); closeCommitModal(); };
76+
const handleCancel = () => {
77+
setCommitMessage("");
78+
setPreviewData(null);
79+
closeCommitModal();
80+
};
6181

62-
const collapseItems = previewData?.changes?.map((change) => ({
63-
key: change.model_name,
64-
label: (<span>{change.model_name} <Tag color={CHANGE_TYPE_COLORS[change.change_type]}>{change.change_type}</Tag></span>),
65-
children: (
66-
<div className="commit-preview-diff">
67-
<DiffViewer originalTitle="Last Committed" modifiedTitle="Current" originalContent={change.old_yaml} modifiedContent={change.new_yaml} forceInline />
68-
</div>
69-
),
70-
})) || [];
82+
const collapseItems =
83+
previewData?.changes?.map((change) => ({
84+
key: change.model_name,
85+
label: (
86+
<span>
87+
{change.model_name}{" "}
88+
<Tag color={CHANGE_TYPE_COLORS[change.change_type]}>
89+
{change.change_type}
90+
</Tag>
91+
</span>
92+
),
93+
children: (
94+
<div className="commit-preview-diff">
95+
<DiffViewer
96+
originalTitle="Last Committed"
97+
modifiedTitle="Current"
98+
originalContent={change.old_yaml}
99+
modifiedContent={change.new_yaml}
100+
forceInline
101+
/>
102+
</div>
103+
),
104+
})) || [];
71105

72106
return (
73-
<Modal title="Commit Version" open={isOpen} onOk={handleOk} onCancel={handleCancel} okText="Commit" okButtonProps={{ disabled: !commitMessage.trim() || loading, loading }} width={MODAL_WIDTH} centered maskClosable={false} destroyOnClose>
74-
<Typography.Paragraph type="secondary" style={{ marginBottom: 12 }}>Create a new version snapshot for <strong>all models</strong> in this project.</Typography.Paragraph>
75-
<Input.TextArea value={commitMessage} onChange={(e) => setCommitMessage(e.target.value)} placeholder="Describe what changed..." maxLength={MAX_MESSAGE_LENGTH} showCount rows={4} autoFocus />
107+
<Modal
108+
title="Commit Version"
109+
open={isOpen}
110+
onOk={handleOk}
111+
onCancel={handleCancel}
112+
okText="Commit"
113+
okButtonProps={{ disabled: !commitMessage.trim() || loading, loading }}
114+
width={MODAL_WIDTH}
115+
centered
116+
maskClosable={false}
117+
destroyOnClose
118+
>
119+
<Typography.Paragraph type="secondary" style={{ marginBottom: 12 }}>
120+
Create a new version snapshot for <strong>all models</strong> in this
121+
project.
122+
</Typography.Paragraph>
123+
<Input.TextArea
124+
value={commitMessage}
125+
onChange={(e) => setCommitMessage(e.target.value)}
126+
placeholder="Describe what changed..."
127+
maxLength={MAX_MESSAGE_LENGTH}
128+
showCount
129+
rows={4}
130+
autoFocus
131+
/>
76132
<div className="commit-preview-section">
77-
{previewLoading && <div className="commit-preview-empty"><Spin size="small" /></div>}
78-
{!previewLoading && previewData && !previewData.has_changes && <div className="commit-preview-empty"><Typography.Text type="secondary">No changes since last commit</Typography.Text></div>}
133+
{previewLoading && (
134+
<div className="commit-preview-empty">
135+
<Spin size="small" />
136+
</div>
137+
)}
138+
{!previewLoading && previewData && !previewData.has_changes && (
139+
<div className="commit-preview-empty">
140+
<Typography.Text type="secondary">
141+
No changes since last commit
142+
</Typography.Text>
143+
</div>
144+
)}
79145
{!previewLoading && previewData?.has_changes && (
80146
<>
81-
<div className="commit-preview-summary"><Typography.Text type="secondary">{previewData.total_models_changed} model{previewData.total_models_changed !== 1 ? "s" : ""} changed</Typography.Text></div>
147+
<div className="commit-preview-summary">
148+
<Typography.Text type="secondary">
149+
{previewData.total_models_changed} model
150+
{previewData.total_models_changed !== 1 ? "s" : ""} changed
151+
</Typography.Text>
152+
</div>
82153
<Collapse items={collapseItems} size="small" />
83154
</>
84155
)}

0 commit comments

Comments
 (0)