|
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