Source code for five_safes_tes_analytics.node.local_processing_base
from abc import ABC, abstractmethod
from sqlalchemy.engine import Result
[docs]
class BaseLocalProcessing(ABC):
"""
Abstract base class for local processing operations in federated analysis.
Each subclass represents a different analysis type that can be run on individual
TREs. The class handles SQL query building and optional Python-side analysis,
returning results that can be aggregated across multiple TREs.
"""
def __init__(self, analysis_type: str = None, user_query: str = None, engine = None):
# Use class attribute as default if no analysis_type provided
self.analysis_type = analysis_type if analysis_type is not None else getattr(self.__class__, 'analysis_type', None)
self.user_query = user_query
self.engine = engine
@property
@abstractmethod
def description(self):
"""Description of the processing step."""
pass
@property
def processing_query(self):
"""SQL fragment for the processing step. By default, returns None."""
return None
@property
@abstractmethod
def user_query_requirements(self):
"""Requirements for the user query."""
pass
[docs]
def build_query(self) -> str:
"""
Build a complete SQL query by combining user's data selection with analysis calculations.
"""
if self.processing_query is None:
return self.user_query
# Combine user query with analysis part
query = f"""WITH user_query AS (
{self.user_query}
)
{self.processing_query}"""
return query
[docs]
def python_analysis(self, sql_result: Result) -> dict | list[dict] | None:
"""
Optional Python-side analysis. Override in subclasses if needed.
By default, does nothing and returns None.
Args:
sql_result: SQLAlchemy Result object from query execution
Returns:
dict: For single result objects (e.g., {"total": 100, "n": 10})
list[dict]: For multiple rows (e.g., [{"gender": "Male", "n": 5}, {"gender": "Female", "n": 3}])
None: If no Python analysis is needed (SQL results will be used instead)
Examples:
Single dict: {"centroids": [...], "count": 100} # PercentileSketch
List of dicts: [{"category": "A", "n": 10}, {"category": "B", "n": 20}] # Contingency tables
"""
return None