cinchdb 0.1.0__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.
- cinchdb/__init__.py +7 -0
- cinchdb/__main__.py +6 -0
- cinchdb/api/__init__.py +5 -0
- cinchdb/api/app.py +76 -0
- cinchdb/api/auth.py +290 -0
- cinchdb/api/main.py +137 -0
- cinchdb/api/routers/__init__.py +25 -0
- cinchdb/api/routers/auth.py +135 -0
- cinchdb/api/routers/branches.py +368 -0
- cinchdb/api/routers/codegen.py +164 -0
- cinchdb/api/routers/columns.py +290 -0
- cinchdb/api/routers/data.py +479 -0
- cinchdb/api/routers/databases.py +177 -0
- cinchdb/api/routers/projects.py +133 -0
- cinchdb/api/routers/query.py +156 -0
- cinchdb/api/routers/tables.py +349 -0
- cinchdb/api/routers/tenants.py +216 -0
- cinchdb/api/routers/views.py +219 -0
- cinchdb/cli/__init__.py +0 -0
- cinchdb/cli/commands/__init__.py +1 -0
- cinchdb/cli/commands/branch.py +479 -0
- cinchdb/cli/commands/codegen.py +176 -0
- cinchdb/cli/commands/column.py +308 -0
- cinchdb/cli/commands/database.py +212 -0
- cinchdb/cli/commands/query.py +136 -0
- cinchdb/cli/commands/remote.py +144 -0
- cinchdb/cli/commands/table.py +289 -0
- cinchdb/cli/commands/tenant.py +173 -0
- cinchdb/cli/commands/view.py +189 -0
- cinchdb/cli/handlers/__init__.py +5 -0
- cinchdb/cli/handlers/codegen_handler.py +189 -0
- cinchdb/cli/main.py +137 -0
- cinchdb/cli/utils.py +182 -0
- cinchdb/config.py +177 -0
- cinchdb/core/__init__.py +5 -0
- cinchdb/core/connection.py +175 -0
- cinchdb/core/database.py +537 -0
- cinchdb/core/maintenance.py +73 -0
- cinchdb/core/path_utils.py +153 -0
- cinchdb/managers/__init__.py +26 -0
- cinchdb/managers/branch.py +167 -0
- cinchdb/managers/change_applier.py +414 -0
- cinchdb/managers/change_comparator.py +194 -0
- cinchdb/managers/change_tracker.py +182 -0
- cinchdb/managers/codegen.py +523 -0
- cinchdb/managers/column.py +579 -0
- cinchdb/managers/data.py +455 -0
- cinchdb/managers/merge_manager.py +429 -0
- cinchdb/managers/query.py +214 -0
- cinchdb/managers/table.py +383 -0
- cinchdb/managers/tenant.py +258 -0
- cinchdb/managers/view.py +252 -0
- cinchdb/models/__init__.py +27 -0
- cinchdb/models/base.py +44 -0
- cinchdb/models/branch.py +26 -0
- cinchdb/models/change.py +47 -0
- cinchdb/models/database.py +20 -0
- cinchdb/models/project.py +20 -0
- cinchdb/models/table.py +86 -0
- cinchdb/models/tenant.py +19 -0
- cinchdb/models/view.py +15 -0
- cinchdb/utils/__init__.py +15 -0
- cinchdb/utils/sql_validator.py +137 -0
- cinchdb-0.1.0.dist-info/METADATA +195 -0
- cinchdb-0.1.0.dist-info/RECORD +68 -0
- cinchdb-0.1.0.dist-info/WHEEL +4 -0
- cinchdb-0.1.0.dist-info/entry_points.txt +3 -0
- cinchdb-0.1.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,182 @@
|
|
1
|
+
"""Change tracking for CinchDB."""
|
2
|
+
|
3
|
+
import json
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import List
|
6
|
+
from datetime import datetime, timezone
|
7
|
+
|
8
|
+
from cinchdb.models import Change
|
9
|
+
from cinchdb.core.path_utils import get_branch_path
|
10
|
+
|
11
|
+
|
12
|
+
class ChangeTracker:
|
13
|
+
"""Tracks schema changes within a branch."""
|
14
|
+
|
15
|
+
def __init__(self, project_root: Path, database: str, branch: str):
|
16
|
+
"""Initialize change tracker.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
project_root: Path to project root
|
20
|
+
database: Database name
|
21
|
+
branch: Branch name
|
22
|
+
"""
|
23
|
+
self.project_root = Path(project_root)
|
24
|
+
self.database = database
|
25
|
+
self.branch = branch
|
26
|
+
self.branch_path = get_branch_path(self.project_root, database, branch)
|
27
|
+
self.changes_file = self.branch_path / "changes.json"
|
28
|
+
|
29
|
+
def get_changes(self) -> List[Change]:
|
30
|
+
"""Get all changes for the branch.
|
31
|
+
|
32
|
+
Returns:
|
33
|
+
List of Change objects
|
34
|
+
"""
|
35
|
+
if not self.changes_file.exists():
|
36
|
+
return []
|
37
|
+
|
38
|
+
with open(self.changes_file, "r") as f:
|
39
|
+
changes_data = json.load(f)
|
40
|
+
|
41
|
+
changes = []
|
42
|
+
for data in changes_data:
|
43
|
+
# Convert string dates back to datetime
|
44
|
+
if data.get("created_at"):
|
45
|
+
data["created_at"] = datetime.fromisoformat(data["created_at"])
|
46
|
+
if data.get("updated_at"):
|
47
|
+
data["updated_at"] = datetime.fromisoformat(data["updated_at"])
|
48
|
+
|
49
|
+
change = Change(**data)
|
50
|
+
changes.append(change)
|
51
|
+
|
52
|
+
return changes
|
53
|
+
|
54
|
+
def add_change(self, change: Change) -> Change:
|
55
|
+
"""Add a new change to the branch.
|
56
|
+
|
57
|
+
Args:
|
58
|
+
change: Change object to add
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
The added Change object with ID and timestamp
|
62
|
+
"""
|
63
|
+
# Get existing changes
|
64
|
+
changes = self.get_changes()
|
65
|
+
|
66
|
+
# Ensure change has required fields
|
67
|
+
if not change.id:
|
68
|
+
import uuid
|
69
|
+
|
70
|
+
change.id = str(uuid.uuid4())
|
71
|
+
|
72
|
+
if not change.created_at:
|
73
|
+
change.created_at = datetime.now(timezone.utc)
|
74
|
+
|
75
|
+
# Add to list
|
76
|
+
changes.append(change)
|
77
|
+
|
78
|
+
# Save
|
79
|
+
self._save_changes(changes)
|
80
|
+
|
81
|
+
return change
|
82
|
+
|
83
|
+
def get_unapplied_changes(self) -> List[Change]:
|
84
|
+
"""Get all unapplied changes.
|
85
|
+
|
86
|
+
Returns:
|
87
|
+
List of unapplied Change objects
|
88
|
+
"""
|
89
|
+
changes = self.get_changes()
|
90
|
+
return [c for c in changes if not c.applied]
|
91
|
+
|
92
|
+
def mark_change_applied(self, change_id: str) -> None:
|
93
|
+
"""Mark a change as applied.
|
94
|
+
|
95
|
+
Args:
|
96
|
+
change_id: ID of change to mark as applied
|
97
|
+
"""
|
98
|
+
changes = self.get_changes()
|
99
|
+
|
100
|
+
for change in changes:
|
101
|
+
if change.id == change_id:
|
102
|
+
change.applied = True
|
103
|
+
change.updated_at = datetime.now(timezone.utc)
|
104
|
+
break
|
105
|
+
|
106
|
+
self._save_changes(changes)
|
107
|
+
|
108
|
+
def get_changes_since(self, change_id: str) -> List[Change]:
|
109
|
+
"""Get all changes after a specific change.
|
110
|
+
|
111
|
+
Args:
|
112
|
+
change_id: ID of change to start after
|
113
|
+
|
114
|
+
Returns:
|
115
|
+
List of Change objects after the specified change
|
116
|
+
"""
|
117
|
+
changes = self.get_changes()
|
118
|
+
|
119
|
+
# Find the index of the specified change
|
120
|
+
start_index = None
|
121
|
+
for i, change in enumerate(changes):
|
122
|
+
if change.id == change_id:
|
123
|
+
start_index = i + 1
|
124
|
+
break
|
125
|
+
|
126
|
+
if start_index is None:
|
127
|
+
return []
|
128
|
+
|
129
|
+
return changes[start_index:]
|
130
|
+
|
131
|
+
def clear_changes(self) -> None:
|
132
|
+
"""Clear all changes from the branch."""
|
133
|
+
self._save_changes([])
|
134
|
+
|
135
|
+
def has_change_id(self, change_id: str) -> bool:
|
136
|
+
"""Check if a change ID exists.
|
137
|
+
|
138
|
+
Args:
|
139
|
+
change_id: ID to check
|
140
|
+
|
141
|
+
Returns:
|
142
|
+
True if change exists
|
143
|
+
"""
|
144
|
+
changes = self.get_changes()
|
145
|
+
return any(c.id == change_id for c in changes)
|
146
|
+
|
147
|
+
def remove_change(self, change_id: str) -> bool:
|
148
|
+
"""Remove a change from the branch.
|
149
|
+
|
150
|
+
Args:
|
151
|
+
change_id: ID of change to remove
|
152
|
+
|
153
|
+
Returns:
|
154
|
+
True if change was removed, False if not found
|
155
|
+
"""
|
156
|
+
changes = self.get_changes()
|
157
|
+
|
158
|
+
# Find and remove the change
|
159
|
+
for i, change in enumerate(changes):
|
160
|
+
if change.id == change_id:
|
161
|
+
changes.pop(i)
|
162
|
+
self._save_changes(changes)
|
163
|
+
return True
|
164
|
+
|
165
|
+
return False
|
166
|
+
|
167
|
+
def _save_changes(self, changes: List[Change]) -> None:
|
168
|
+
"""Save changes to disk.
|
169
|
+
|
170
|
+
Args:
|
171
|
+
changes: List of Change objects to save
|
172
|
+
"""
|
173
|
+
# Convert to JSON-serializable format
|
174
|
+
changes_data = []
|
175
|
+
for change in changes:
|
176
|
+
# model_dump with mode='json' handles datetime serialization
|
177
|
+
data = change.model_dump(mode="json")
|
178
|
+
changes_data.append(data)
|
179
|
+
|
180
|
+
# Save to file
|
181
|
+
with open(self.changes_file, "w") as f:
|
182
|
+
json.dump(changes_data, f, indent=2)
|