|
8 | 8 |
|
9 | 9 | # lib imports |
10 | 10 | import git |
11 | | -from tinydb import TinyDB |
| 11 | +from tinydb import TinyDB, Query |
12 | 12 | from tinydb.storages import JSONStorage |
13 | 13 | from tinydb.middlewares import CachingMiddleware |
14 | 14 |
|
|
17 | 17 |
|
18 | 18 | # Constants |
19 | 19 | DATA_REPO_LOCK = threading.Lock() |
| 20 | +GIT_ENABLED = True # disable to pause pushing to git, useful for heavy db operations |
20 | 21 |
|
21 | 22 |
|
22 | 23 | class Database: |
@@ -274,50 +275,81 @@ def __enter__(self): |
274 | 275 | return self.tinydb |
275 | 276 |
|
276 | 277 | def __exit__(self, exc_type, exc_val, exc_tb): |
277 | | - self.sync() |
278 | | - self.lock.release() |
| 278 | + try: |
| 279 | + self.sync() |
| 280 | + finally: |
| 281 | + self.lock.release() |
279 | 282 |
|
280 | 283 | def sync(self): |
281 | | - # Only call flush if using CachingMiddleware |
282 | | - if hasattr(self.tinydb.storage, 'flush'): |
283 | | - self.tinydb.storage.flush() |
284 | | - |
285 | | - # Git operations - commit and push changes if using git |
286 | | - with DATA_REPO_LOCK: |
287 | | - if self.use_git and self.repo is not None: |
288 | | - try: |
289 | | - # Check for untracked database files and tracked files with changes |
290 | | - status = self.repo.git.status('--porcelain') |
291 | | - |
292 | | - # If there are any changes or untracked files |
293 | | - if status: |
294 | | - # Add ALL json files in the directory to ensure we track all databases |
295 | | - json_files = [f for f in os.listdir(self.db_dir) if f.endswith('.json')] |
296 | | - if json_files: |
297 | | - for json_file in json_files: |
298 | | - file_path = os.path.join(self.db_dir, json_file) |
299 | | - self.repo.git.add(file_path) |
300 | | - |
301 | | - # Check if we have anything to commit after adding |
302 | | - if self.repo.git.status('--porcelain'): |
303 | | - # Ensure the repository is configured with user identity |
304 | | - self._configure_repo() |
305 | | - |
306 | | - # Commit all changes at once with a general message |
307 | | - commit_message = "Update database files" |
308 | | - self.repo.git.commit('-m', commit_message) |
309 | | - print("Committed changes to git data repository") |
310 | | - |
311 | | - # Push to remote with credentials |
312 | | - try: |
313 | | - # Ensure we're using the credentials for push |
314 | | - protocol, repo_path = self.repo_url.split("://", 1) |
315 | | - push_url = f"{protocol}://{self.git_user_name}:{self.git_token}@{repo_path}" |
316 | | - self.repo.git.push(push_url, self.repo_branch) |
317 | | - print("Pushed changes to remote git data repository") |
318 | | - except git.exc.GitCommandError as e: |
319 | | - print(f"Failed to push changes: {str(e)}") |
320 | | - |
321 | | - except Exception as e: |
322 | | - print(f"Git operation failed: {str(e)}") |
323 | | - traceback.print_exc() |
| 284 | + try: |
| 285 | + # Flush changes to disk if possible |
| 286 | + if self.tinydb and hasattr(self.tinydb.storage, 'flush'): |
| 287 | + self.tinydb.storage.flush() |
| 288 | + |
| 289 | + # Close the database to ensure file is available for Git operations |
| 290 | + if self.tinydb is not None: |
| 291 | + self.tinydb.close() |
| 292 | + self.tinydb = None |
| 293 | + |
| 294 | + # Git operations with closed file |
| 295 | + with DATA_REPO_LOCK: |
| 296 | + if self.use_git and self.repo is not None and GIT_ENABLED: |
| 297 | + try: |
| 298 | + # Check for untracked database files and tracked files with changes |
| 299 | + status = self.repo.git.status('--porcelain') |
| 300 | + |
| 301 | + # If there are any changes or untracked files |
| 302 | + if status: |
| 303 | + # Add ALL json files in the directory to ensure we track all databases |
| 304 | + json_files = [f for f in os.listdir(self.db_dir) if f.endswith('.json')] |
| 305 | + if json_files: |
| 306 | + for json_file in json_files: |
| 307 | + file_path = os.path.join(self.db_dir, json_file) |
| 308 | + self.repo.git.add(file_path) |
| 309 | + |
| 310 | + # Check if we have anything to commit after adding |
| 311 | + if self.repo.git.status('--porcelain'): |
| 312 | + # Ensure the repository is configured with user identity |
| 313 | + self._configure_repo() |
| 314 | + |
| 315 | + # Commit all changes at once with a general message |
| 316 | + commit_message = "Update database files" |
| 317 | + self.repo.git.commit('-m', commit_message) |
| 318 | + print("Committed changes to git data repository") |
| 319 | + |
| 320 | + # Push to remote with credentials |
| 321 | + try: |
| 322 | + # Ensure we're using the credentials for push |
| 323 | + protocol, repo_path = self.repo_url.split("://", 1) |
| 324 | + push_url = f"{protocol}://{self.git_user_name}:{self.git_token}@{repo_path}" |
| 325 | + self.repo.git.push(push_url, self.repo_branch) |
| 326 | + print("Pushed changes to remote git data repository") |
| 327 | + except git.exc.GitCommandError as e: |
| 328 | + print(f"Failed to push changes: {str(e)}") |
| 329 | + |
| 330 | + except Exception as e: |
| 331 | + print(f"Git operation failed: {str(e)}") |
| 332 | + traceback.print_exc() |
| 333 | + finally: |
| 334 | + # Ensure database is ready for next use |
| 335 | + if self.tinydb is None: |
| 336 | + self.tinydb = TinyDB( |
| 337 | + self.json_path, |
| 338 | + storage=CachingMiddleware(JSONStorage), |
| 339 | + indent=4, |
| 340 | + ) |
| 341 | + |
| 342 | + @staticmethod |
| 343 | + def query(): |
| 344 | + """ |
| 345 | + Get the TinyDB Query object for constructing database queries. |
| 346 | +
|
| 347 | + This is a helper method to avoid importing the Query class directly |
| 348 | + in modules that use the Database class. |
| 349 | +
|
| 350 | + Returns |
| 351 | + ------- |
| 352 | + Query |
| 353 | + A TinyDB Query object for constructing queries. |
| 354 | + """ |
| 355 | + return Query() |
0 commit comments