ytptube/app/library/operations.py
2025-12-30 20:42:50 +03:00

274 lines
8.5 KiB
Python

from enum import Enum
from typing import Any
class Operation(str, Enum):
"""Comparison operations for filtering items."""
EQUAL = "=="
"""Exact equality comparison."""
NOT_EQUAL = "!="
"""Not equal comparison."""
CONTAIN = "in"
"""Check if value is contained in the field (substring match)."""
NOT_CONTAIN = "not_in"
"""Check if value is not contained in the field."""
GREATER_THAN = ">"
"""Greater than comparison."""
LESS_THAN = "<"
"""Less than comparison."""
GREATER_EQUAL = ">="
"""Greater than or equal comparison."""
LESS_EQUAL = "<="
"""Less than or equal comparison."""
STARTS_WITH = "startswith"
"""Check if field starts with value."""
ENDS_WITH = "endswith"
"""Check if field ends with value."""
def __str__(self) -> str:
return self.value
def matches(operation: Operation | str, haystack: Any, needle: Any) -> bool:
"""
Generic comparison function that compares two values using the specified operation.
Args:
operation: The comparison operation to perform (Operation enum or string)
haystack: The first value (usually the field value from data)
needle: The second value (usually the comparison value)
Returns:
bool: True if the comparison matches, False otherwise
Examples:
>>> matches(Operation.EQUAL, "test", "test")
True
>>> matches(Operation.CONTAIN, "Python Tutorial", "Python")
True
>>> matches(Operation.GREATER_THAN, 100, 50)
True
>>> matches("==", "test", "test")
True
"""
# Parse operation if it's a string
if isinstance(operation, str):
try:
operation = Operation(operation)
except ValueError:
operation = Operation.EQUAL
try:
if Operation.EQUAL == operation:
return haystack == needle
if Operation.NOT_EQUAL == operation:
return haystack != needle
if Operation.CONTAIN == operation:
return str(needle) in str(haystack) if haystack is not None else False
if Operation.NOT_CONTAIN == operation:
return str(needle) not in str(haystack) if haystack is not None else True
if Operation.GREATER_THAN == operation:
if haystack is None or needle is None:
return False
return haystack > needle
if Operation.LESS_THAN == operation:
if haystack is None or needle is None:
return False
return haystack < needle
if Operation.GREATER_EQUAL == operation:
if haystack is None or needle is None:
return False
return haystack >= needle
if Operation.LESS_EQUAL == operation:
if haystack is None or needle is None:
return False
return haystack <= needle
if Operation.STARTS_WITH == operation:
return str(haystack).startswith(str(needle)) if haystack is not None else False
if Operation.ENDS_WITH == operation:
return str(haystack).endswith(str(needle)) if haystack is not None else False
# Unknown operation, default to equality
return haystack == needle
except (TypeError, AttributeError):
# Comparison failed (e.g., comparing incompatible types)
return False
def matches_condition(key: str, value: tuple | str | float | bool, data: dict) -> bool:
"""
Check if a field in a dictionary matches the given condition.
This is a helper function that extracts values from a dictionary and uses the generic
matches() function to perform the comparison.
Args:
key: The field name to check in the data dictionary
value: Either:
- A direct value for equality check: "test"
- A tuple of (Operation, value): (Operation.CONTAIN, "test")
- A tuple of (str, value): ("in", "test") for backward compatibility
data: Dictionary containing the data to check against
Returns:
bool: True if the condition matches, False otherwise
Examples:
>>> data = {"title": "Python Tutorial", "size": 1000}
>>> matches_condition("title", "Python Tutorial", data)
True
>>> matches_condition("title", (Operation.CONTAIN, "Python"), data)
True
>>> matches_condition("size", (Operation.GREATER_THAN, 500), data)
True
>>> matches_condition("missing", "value", data)
False
"""
if key not in data:
return False
field_value: Any = data[key]
# Parse value to extract operation and comparison value
if isinstance(value, tuple) and len(value) == 2:
operation, compare_value = value
else:
operation = Operation.EQUAL
compare_value = value
return matches(operation, field_value, compare_value)
def matches_all(data: dict, **conditions) -> bool:
"""
Check if all conditions match (AND logic).
Args:
data: Dictionary containing the data to check against
**conditions: Keyword arguments representing conditions to check
Returns:
bool: True if all conditions match, False otherwise
Examples:
>>> data = {"title": "Python Tutorial", "size": 1000, "status": "active"}
>>> matches_all(data, title=(Operation.CONTAIN, "Python"), size=(Operation.GREATER_THAN, 500))
True
>>> matches_all(data, title="Python Tutorial", status="active")
True
"""
if not conditions:
return True
return all(matches_condition(key, value, data) for key, value in conditions.items())
def matches_any(data: dict, **conditions) -> bool:
"""
Check if any condition matches (OR logic).
Args:
data: Dictionary containing the data to check against
**conditions: Keyword arguments representing conditions to check
Returns:
bool: True if any condition matches, False if none match
Examples:
>>> data = {"title": "Python Tutorial", "size": 1000}
>>> matches_any(data, title=(Operation.CONTAIN, "Java"), size=(Operation.GREATER_THAN, 500))
True
>>> matches_any(data, title="Wrong", status="Wrong")
False
"""
if not conditions:
return False
return any(matches_condition(key, value, data) for key, value in conditions.items())
def filter_items(items: list[dict], **conditions) -> list[dict]:
"""
Filter a list of dictionaries based on conditions (AND logic).
Args:
items: List of dictionaries to filter
**conditions: Keyword arguments representing conditions to check
Returns:
list[dict]: Filtered list of dictionaries that match all conditions
Examples:
>>> items = [
... {"title": "Python Tutorial", "size": 1000},
... {"title": "JavaScript Course", "size": 2000},
... {"title": "Python Advanced", "size": 1500}
... ]
>>> filter_items(items, title=(Operation.CONTAIN, "Python"))
[{"title": "Python Tutorial", "size": 1000}, {"title": "Python Advanced", "size": 1500}]
>>> filter_items(items, size=(Operation.GREATER_THAN, 1200))
[{"title": "JavaScript Course", "size": 2000}, {"title": "Python Advanced", "size": 1500}]
"""
if not conditions:
return items
return [item for item in items if matches_all(item, **conditions)]
def find_first(items: list[dict], **conditions) -> dict | None:
"""
Find the first dictionary that matches all conditions (AND logic).
Args:
items: List of dictionaries to search
**conditions: Keyword arguments representing conditions to check
Returns:
dict | None: First matching dictionary or None if no match found
Examples:
>>> items = [
... {"title": "Python Tutorial", "size": 1000},
... {"title": "JavaScript Course", "size": 2000}
... ]
>>> find_first(items, title=(Operation.CONTAIN, "Python"))
{"title": "Python Tutorial", "size": 1000}
>>> find_first(items, title="Nonexistent")
None
"""
for item in items:
if matches_all(item, **conditions):
return item
return None
def find_all(items: list[dict], **conditions) -> list[dict]:
"""
Alias for filter_items() - find all dictionaries matching conditions.
Args:
items: List of dictionaries to search
**conditions: Keyword arguments representing conditions to check
Returns:
list[dict]: List of matching dictionaries
"""
return filter_items(items, **conditions)