mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 17:51:08 -05:00
Add OWASP compliance mapping and security test case templates, and document version control implementation phases
This commit is contained in:
195
history_tracking/batch.py
Normal file
195
history_tracking/batch.py
Normal file
@@ -0,0 +1,195 @@
|
||||
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
|
||||
Reference in New Issue
Block a user