sqlobjects 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
sqlobjects/history.py ADDED
@@ -0,0 +1,101 @@
1
+ """SQLObjects History Tracking Module - Field change history tracking"""
2
+
3
+ from datetime import datetime
4
+ from typing import Any
5
+
6
+
7
+ __all__ = [
8
+ "HistoryTrackingMixin",
9
+ "FieldHistory",
10
+ "get_field_history",
11
+ ]
12
+
13
+
14
+ class FieldHistory:
15
+ """Field change history record"""
16
+
17
+ def __init__(self, old_value: Any, new_value: Any, timestamp: datetime | None = None):
18
+ self.old_value = old_value
19
+ self.new_value = new_value
20
+ self.timestamp = timestamp or datetime.now()
21
+
22
+ def to_dict(self) -> dict[str, Any]:
23
+ """Convert to dictionary format"""
24
+ return {
25
+ "old_value": self.old_value,
26
+ "new_value": self.new_value,
27
+ "timestamp": self.timestamp,
28
+ }
29
+
30
+ def __repr__(self) -> str:
31
+ return f"FieldHistory(old={self.old_value!r}, new={self.new_value!r}, time={self.timestamp})"
32
+
33
+
34
+ class HistoryTrackingMixin:
35
+ """History tracking mixin class"""
36
+
37
+ def __init__(self, **kwargs):
38
+ super().__init__(**kwargs)
39
+ self._field_history: dict[str, list[FieldHistory]] = {}
40
+ self._history_initialized = False
41
+ self._initialize_field_tracking()
42
+ self._history_initialized = True
43
+
44
+ def _initialize_field_tracking(self) -> None:
45
+ """Initialize tracking for fields with active history"""
46
+ # Get field names from model class
47
+ if hasattr(self, "_get_field_names"):
48
+ for name in self._get_field_names(): # type: ignore[reportAttributeAccessIssue]
49
+ # Skip private fields
50
+ if name.startswith("_"):
51
+ continue
52
+
53
+ field_attr = getattr(self.__class__, name, None)
54
+ if hasattr(field_attr, "has_active_history") and field_attr.has_active_history: # type: ignore[reportAttributeAccessIssue]
55
+ self._field_history[name] = []
56
+
57
+ def __setattr__(self, name: str, value: Any) -> None:
58
+ # Track field changes only after initialization
59
+ if (
60
+ not name.startswith("_")
61
+ and hasattr(self, "_field_history")
62
+ and hasattr(self, "_history_initialized")
63
+ and self._history_initialized
64
+ and name in self._field_history
65
+ ):
66
+ old_value = getattr(self, name, None)
67
+ if old_value != value:
68
+ history_record = FieldHistory(old_value, value)
69
+ self._field_history[name].append(history_record)
70
+
71
+ super().__setattr__(name, value)
72
+
73
+ def get_field_history(self, field_name: str) -> list[FieldHistory]:
74
+ """Get field change history"""
75
+ return self._field_history.get(field_name, [])
76
+
77
+ def get_field_history_dict(self, field_name: str) -> list[dict[str, Any]]:
78
+ """Get field change history in dictionary format"""
79
+ history = self.get_field_history(field_name)
80
+ return [record.to_dict() for record in history]
81
+
82
+ def clear_field_history(self, field_name: str) -> None:
83
+ """Clear history for specific field"""
84
+ if field_name in self._field_history:
85
+ self._field_history[field_name].clear()
86
+
87
+ def clear_all_history(self) -> None:
88
+ """Clear history for all fields"""
89
+ for field_name in self._field_history:
90
+ self._field_history[field_name].clear()
91
+
92
+ def get_all_field_history(self) -> dict[str, list[dict[str, Any]]]:
93
+ """Get history for all tracked fields"""
94
+ return {field_name: self.get_field_history_dict(field_name) for field_name in self._field_history}
95
+
96
+
97
+ def get_field_history(instance: Any, field_name: str) -> list[FieldHistory]:
98
+ """Get field history from instance"""
99
+ if hasattr(instance, "get_field_history"):
100
+ return instance.get_field_history(field_name)
101
+ return []