mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 18:11:08 -05:00
195 lines
6.9 KiB
Python
195 lines
6.9 KiB
Python
from django.db import transaction
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.utils import timezone
|
|
from typing import List, Dict, Any, Optional
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
import logging
|
|
|
|
from .models import VersionBranch, ChangeSet
|
|
from .caching import VersionHistoryCache
|
|
from .signals import get_current_branch
|
|
|
|
logger = logging.getLogger('version_control')
|
|
|
|
class BatchOperation:
|
|
"""
|
|
Handles batch operations for version control system.
|
|
Provides efficient handling of multiple changes and updates.
|
|
"""
|
|
|
|
def __init__(self, max_workers: int = 4):
|
|
self.max_workers = max_workers
|
|
self.changes: List[Dict[str, Any]] = []
|
|
self.error_handler = self.default_error_handler
|
|
|
|
def default_error_handler(self, error: Exception, item: Dict[str, Any]) -> None:
|
|
"""Default error handling for batch operations"""
|
|
logger.error(f"Batch operation error: {error}, item: {item}")
|
|
raise error
|
|
|
|
def set_error_handler(self, handler) -> None:
|
|
"""Set custom error handler for batch operations"""
|
|
self.error_handler = handler
|
|
|
|
def add_change(self, obj: Any, data: Dict[str, Any], branch: Optional[VersionBranch] = None) -> None:
|
|
"""Add a change to the batch"""
|
|
content_type = ContentType.objects.get_for_model(obj)
|
|
self.changes.append({
|
|
'content_type': content_type,
|
|
'object_id': obj.pk,
|
|
'data': data,
|
|
'branch': branch or get_current_branch()
|
|
})
|
|
|
|
@transaction.atomic
|
|
def process_change(self, change: Dict[str, Any]) -> ChangeSet:
|
|
"""Process a single change in the batch"""
|
|
try:
|
|
changeset = ChangeSet.objects.create(
|
|
branch=change['branch'],
|
|
content_type=change['content_type'],
|
|
object_id=change['object_id'],
|
|
data=change['data'],
|
|
status='pending'
|
|
)
|
|
|
|
# Apply the change
|
|
changeset.apply()
|
|
|
|
# Cache the result
|
|
VersionHistoryCache.cache_change(changeset.to_dict())
|
|
|
|
return changeset
|
|
except Exception as e:
|
|
self.error_handler(e, change)
|
|
raise
|
|
|
|
def process_parallel(self) -> List[ChangeSet]:
|
|
"""Process changes in parallel using thread pool"""
|
|
results = []
|
|
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
|
|
future_to_change = {
|
|
executor.submit(self.process_change, change): change
|
|
for change in self.changes
|
|
}
|
|
|
|
for future in future_to_change:
|
|
try:
|
|
changeset = future.result()
|
|
results.append(changeset)
|
|
except Exception as e:
|
|
change = future_to_change[future]
|
|
self.error_handler(e, change)
|
|
|
|
return results
|
|
|
|
@transaction.atomic
|
|
def process_sequential(self) -> List[ChangeSet]:
|
|
"""Process changes sequentially in a single transaction"""
|
|
results = []
|
|
for change in self.changes:
|
|
try:
|
|
changeset = self.process_change(change)
|
|
results.append(changeset)
|
|
except Exception as e:
|
|
self.error_handler(e, change)
|
|
|
|
return results
|
|
|
|
def commit(self, parallel: bool = False) -> List[ChangeSet]:
|
|
"""Commit all changes in the batch"""
|
|
if not self.changes:
|
|
return []
|
|
|
|
start_time = timezone.now()
|
|
logger.info(f"Starting batch operation with {len(self.changes)} changes")
|
|
|
|
try:
|
|
results = self.process_parallel() if parallel else self.process_sequential()
|
|
|
|
duration = (timezone.now() - start_time).total_seconds()
|
|
logger.info(
|
|
f"Batch operation completed: {len(results)} changes processed in {duration:.2f}s"
|
|
)
|
|
|
|
return results
|
|
finally:
|
|
self.changes = [] # Clear the batch
|
|
|
|
class BulkVersionControl:
|
|
"""
|
|
Handles bulk version control operations for collections of objects.
|
|
"""
|
|
|
|
def __init__(self, model_class, branch: Optional[VersionBranch] = None):
|
|
self.model_class = model_class
|
|
self.branch = branch or get_current_branch()
|
|
self.content_type = ContentType.objects.get_for_model(model_class)
|
|
self.batch = BatchOperation()
|
|
|
|
def prepare_bulk_update(self, objects: List[Any], data: Dict[str, Any]) -> None:
|
|
"""Prepare bulk update for multiple objects"""
|
|
for obj in objects:
|
|
self.batch.add_change(obj, data, self.branch)
|
|
|
|
def prepare_bulk_delete(self, objects: List[Any]) -> None:
|
|
"""Prepare bulk delete for multiple objects"""
|
|
for obj in objects:
|
|
self.batch.add_change(obj, {'action': 'delete'}, self.branch)
|
|
|
|
def prepare_bulk_create(self, data_list: List[Dict[str, Any]]) -> None:
|
|
"""Prepare bulk create for multiple objects"""
|
|
for data in data_list:
|
|
# Create temporary object for content type
|
|
temp_obj = self.model_class()
|
|
self.batch.add_change(temp_obj, {'action': 'create', **data}, self.branch)
|
|
|
|
def commit(self, parallel: bool = True) -> List[ChangeSet]:
|
|
"""Commit all prepared bulk operations"""
|
|
return self.batch.commit(parallel=parallel)
|
|
|
|
class VersionControlQueue:
|
|
"""
|
|
Queue system for handling version control operations.
|
|
Allows for delayed processing and batching of changes.
|
|
"""
|
|
|
|
def __init__(self, batch_size: int = 100, auto_commit: bool = True):
|
|
self.batch_size = batch_size
|
|
self.auto_commit = auto_commit
|
|
self.current_batch = BatchOperation()
|
|
self._queued_count = 0
|
|
|
|
def queue_change(self, obj: Any, data: Dict[str, Any], branch: Optional[VersionBranch] = None) -> None:
|
|
"""Queue a change for processing"""
|
|
self.current_batch.add_change(obj, data, branch)
|
|
self._queued_count += 1
|
|
|
|
if self.auto_commit and self._queued_count >= self.batch_size:
|
|
self.process_queue()
|
|
|
|
def process_queue(self, parallel: bool = True) -> List[ChangeSet]:
|
|
"""Process all queued changes"""
|
|
if not self._queued_count:
|
|
return []
|
|
|
|
results = self.current_batch.commit(parallel=parallel)
|
|
self._queued_count = 0
|
|
return results
|
|
|
|
def batch_version_control(func):
|
|
"""
|
|
Decorator for batching version control operations within a function.
|
|
"""
|
|
def wrapper(*args, **kwargs):
|
|
batch = BatchOperation()
|
|
try:
|
|
with transaction.atomic():
|
|
result = func(*args, batch=batch, **kwargs)
|
|
if batch.changes:
|
|
batch.commit()
|
|
return result
|
|
except Exception as e:
|
|
logger.error(f"Batch operation failed: {e}")
|
|
raise
|
|
return wrapper |