Skip to content

Commit b57bfbc

Browse files
feat(levels): add levels leaderboard
1 parent a15e068 commit b57bfbc

File tree

14 files changed

+2422
-651
lines changed

14 files changed

+2422
-651
lines changed

src/common/database.py

Lines changed: 78 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
# lib imports
1010
import git
11-
from tinydb import TinyDB
11+
from tinydb import TinyDB, Query
1212
from tinydb.storages import JSONStorage
1313
from tinydb.middlewares import CachingMiddleware
1414

@@ -17,6 +17,7 @@
1717

1818
# Constants
1919
DATA_REPO_LOCK = threading.Lock()
20+
GIT_ENABLED = True # disable to pause pushing to git, useful for heavy db operations
2021

2122

2223
class Database:
@@ -274,50 +275,81 @@ def __enter__(self):
274275
return self.tinydb
275276

276277
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()
279282

280283
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

Comments
 (0)