Compare commits

..

22 Commits

Author SHA1 Message Date
2abc1adcf4 module 10 ex0 WIP 2026-03-12 18:18:18 +01:00
616258b4f5 module 9 finished 2026-03-12 17:46:56 +01:00
ab6fc8ed90 module 9 ex1 2026-03-12 17:11:34 +01:00
384ad04a08 Module 9 ex0 done + ex1 WIP 2026-03-09 16:35:41 +01:00
e460d7507e module 8 finnish 2026-03-09 12:06:30 +01:00
76206c8ac8 module 8 ex1 + ex2 WIP 2026-03-07 18:52:18 +01:00
819075d5d9 module 08 ex0 2026-03-07 15:29:04 +01:00
6e6df73007 module 7 finished 2026-03-05 15:21:29 +01:00
516ef290a7 module 7 ex 3 2026-03-03 16:00:55 +01:00
e9daee357a module 7 ex3 WIP 2026-03-02 18:32:31 +01:00
164445bb42 module 7 ex 0 to 2 2026-03-02 15:49:13 +01:00
2cb7f24a8e module 7 01 WIP 2026-02-28 14:45:21 +01:00
4ca187647c 01/01 WIP 2026-02-19 11:20:59 +01:00
ddb56264ae module 7 ex1 + 0 2026-02-17 15:27:24 +01:00
222b212162 module 06 2026-02-15 13:33:04 +01:00
bcd9e4cca0 WIP: part 4 of module 6 2026-02-13 18:09:56 +01:00
545da90c08 part 3 of module 06 2026-02-13 17:50:38 +01:00
aa8767f46d part 2 of module 6 2026-02-13 17:10:07 +01:00
d5978b8f3c part one of module 06 2026-02-13 16:31:10 +01:00
b68b508a04 Module 05 finish + mypy strict + flake 2026-02-13 15:10:02 +01:00
c44a72413e 05 WIP 2026-02-12 11:48:09 +01:00
161ab4339c Rework of module 03 2026-02-12 11:35:37 +01:00
78 changed files with 5409 additions and 258 deletions

View File

@@ -1,14 +1,14 @@
import sys import sys
if __name__ == "__main__": if __name__ == "__main__":
try:
args = sys.argv args = sys.argv
if len(args) <= 1: if len(args) <= 1:
print("No scores provided. Usage: python3\ raise Exception("No scores provided. Usage: python3\
ft_score_analytics.py <score1> <score2> ...") ft_score_analytics.py <score1> <score2> ...")
else: else:
scores = [0] * (len(args) - 1) scores = [0] * (len(args) - 1)
i = 0 i = 0
try:
while i < len(args) - 1: while i < len(args) - 1:
scores[i] = int(args[i + 1]) scores[i] = int(args[i + 1])
i = i + 1 i = i + 1
@@ -21,3 +21,5 @@ if __name__ == "__main__":
print("Score range", max(scores) - min(scores)) print("Score range", max(scores) - min(scores))
except ValueError: except ValueError:
print("Invalid input, only numbers are accepted") print("Invalid input, only numbers are accepted")
except Exception as err:
print(err)

View File

@@ -14,16 +14,17 @@ if __name__ == "__main__":
try: try:
if len(argv) != 2: if len(argv) != 2:
raise Exception("Invalid number of args") raise Exception("Invalid number of args")
print(f'Parsing coordinates: "{argv[1]}"')
args = argv[1].split(",") args = argv[1].split(",")
if len(args) != 3: if len(args) != 3:
raise Exception( raise Exception(
"Invalid argument format." + 'Try like this : "15,64,78"' "Invalid argument format." + 'Try like this : "15,64,78"'
) )
int_args = (int(args[0]), int(args[1]), int(args[2])) int_args = (int(args[0]), int(args[1]), int(args[2]))
print("Parsing coordinates:", args[1]) print(f"Parsed position: {int_args}")
print_distance((0, 0, 0), int_args) print_distance((0, 0, 0), int_args)
except ValueError as err: except ValueError as err:
print(f'Parsing invalid coordinates: "{argv[1]}"') print(f'Invalid coordinates: "{argv[1]}"')
print(err) print(err)
except Exception as err: except Exception as err:
print(err) print(err)

View File

@@ -1,5 +1,6 @@
def tracker_system(players: dict[str, list[str]]) -> None: def tracker_system(players: dict[str, list[str]]) -> None:
print("=== Achievement Tracker System ===\n") print("=== Achievement Tracker System ===\n")
try:
for player in players: for player in players:
print(f"Player {player} achievements: {set((players[player]))}") print(f"Player {player} achievements: {set((players[player]))}")
print("\n=== Achievement Analytics ===") print("\n=== Achievement Analytics ===")
@@ -12,6 +13,9 @@ def tracker_system(players: dict[str, list[str]]) -> None:
for player in players: for player in players:
common_achievements = common_achievements & set((players[player])) common_achievements = common_achievements & set((players[player]))
print(f"\nCommon to all players: {common_achievements}") print(f"\nCommon to all players: {common_achievements}")
except Exception as err:
print(err)
try:
player_rare: dict[str, set[str]] = {} player_rare: dict[str, set[str]] = {}
for player in players: for player in players:
temp = set((players[player])) temp = set((players[player]))
@@ -23,24 +27,68 @@ def tracker_system(players: dict[str, list[str]]) -> None:
for n in player_rare: for n in player_rare:
rare_achievements = rare_achievements | player_rare[n] rare_achievements = rare_achievements | player_rare[n]
print(f"Rare achievements (1 player): {rare_achievements}\n") print(f"Rare achievements (1 player): {rare_achievements}\n")
a_vs_b_common = set((players["Alice"])) & set((players["Bob"])) a_vs_b_common = set((players["alice"])) & set((players["bob"]))
print(f"Alice vs Bob common: {a_vs_b_common}") print(f"Alice vs Bob common: {a_vs_b_common}")
alice_unique = set((players["Alice"])) - set((players["Bob"])) alice_unique = set((players["alice"])) - set((players["bob"]))
print(f"Alice unique: {alice_unique}") print(f"Alice unique: {alice_unique}")
bob_unique = set((players["Bob"])) - set((players["Alice"])) bob_unique = set((players["bob"])) - set((players["alice"]))
print(f"Bob unique: {bob_unique}") print(f"Bob unique: {bob_unique}")
except Exception as err:
print(err)
if __name__ == "__main__": if __name__ == "__main__":
players = { data = {
"Alice": ["first_kill", "level_10", "treasure_hunter", "speed_demon"], "alice": [
"Bob": ["first_kill", "level_10", "boss_slayer", "collector"], "first_blood",
"Charlie": [ "pixel_perfect",
"level_10", "speed_runner",
"treasure_hunter", "first_blood",
"boss_slayer", "first_blood",
"speed_demon", "boss_hunter",
"perfectionist", ],
"bob": [
"level_master",
"boss_hunter",
"treasure_seeker",
"level_master",
"level_master",
],
"charlie": [
"treasure_seeker",
"boss_hunter",
"combo_king",
"first_blood",
"boss_hunter",
"first_blood",
"boss_hunter",
"first_blood",
],
"diana": [
"first_blood",
"combo_king",
"level_master",
"treasure_seeker",
"speed_runner",
"combo_king",
"combo_king",
"level_master",
],
"eve": [
"level_master",
"treasure_seeker",
"first_blood",
"treasure_seeker",
"first_blood",
"treasure_seeker",
],
"frank": [
"explorer",
"boss_hunter",
"first_blood",
"explorer",
"first_blood",
"boss_hunter",
], ],
} }
tracker_system(players) tracker_system(data)

View File

@@ -72,6 +72,7 @@ def dictionnary_properties_demo(inventory: dict[str, int]) -> None:
def inventory_report(inventory: dict[str, int]) -> None: def inventory_report(inventory: dict[str, int]) -> None:
nb_unique_items = len(inventory) nb_unique_items = len(inventory)
nb_item_in_inventory = 0 nb_item_in_inventory = 0
try:
for n in inventory: for n in inventory:
nb_item_in_inventory += inventory[n] nb_item_in_inventory += inventory[n]
if nb_item_in_inventory == 0: if nb_item_in_inventory == 0:
@@ -85,6 +86,8 @@ def inventory_report(inventory: dict[str, int]) -> None:
item_categories(inventory) item_categories(inventory)
management_suggestions(inventory) management_suggestions(inventory)
dictionnary_properties_demo(inventory) dictionnary_properties_demo(inventory)
except Exception as err:
print(err)
def main(argv: list[str]) -> None: def main(argv: list[str]) -> None:

View File

@@ -83,10 +83,19 @@ def comprehension_tester(
try: try:
print("=== game Analytics Dashboard ===\n") print("=== game Analytics Dashboard ===\n")
list_comprehension_example(data) list_comprehension_example(data)
except Exception as err:
print(err)
try:
print() print()
dict_comprehension_example(data) dict_comprehension_example(data)
except Exception as err:
print(err)
try:
print() print()
set_comprehension_example(data) set_comprehension_example(data)
except Exception as err:
print(err)
try:
print() print()
combined_analysis(data) combined_analysis(data)
except Exception as err: except Exception as err:

View File

@@ -9,6 +9,8 @@ def read_ancient_text() -> None:
print("\nData recovery complete. Storage unit disconnected.") print("\nData recovery complete. Storage unit disconnected.")
except FileNotFoundError: except FileNotFoundError:
print("ERROR: Storage vault not found.") print("ERROR: Storage vault not found.")
except Exception as err:
print(err)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -2,8 +2,8 @@ import sys
def communication_system() -> None: def communication_system() -> None:
print("=== CYBER ARCHIVES - COMMUNICATION SYSTEM ===") print("=== CYBER ARCHIVES - COMMUNICATION SYSTEM ===\n")
sys.stdout.write("\nInput Stream active. Enter archivist ID: ") sys.stdout.write("Input Stream active. Enter archivist ID: ")
archivist_id = input() archivist_id = input()
sys.stdout.write("Input Stream active. Enter status report: ") sys.stdout.write("Input Stream active. Enter status report: ")
status_report = input() status_report = input()

View File

@@ -1,5 +1,5 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Any from typing import Any, Union
from typing_extensions import override from typing_extensions import override
@@ -111,8 +111,8 @@ class LogProcessor(DataProcessor):
def class_tester() -> None: def class_tester() -> None:
print("=== CODE NEXUS - DATA PROCESSOR FOUNDATION ===\n") print("=== CODE NEXUS - DATA PROCESSOR FOUNDATION ===\n")
print("Initializing Numeric Processor...") print("Initializing Numeric Processor...")
data = [1, 2, 3, 4, 5] data: Union[list[int | str], str] = [1, 2, 3, 4, 5]
processor = NumericProcessor() processor: DataProcessor = NumericProcessor()
res = processor.process(data) res = processor.process(data)
is_valid = processor.validate(data) is_valid = processor.validate(data)
formatted_output = processor.format_output(res) formatted_output = processor.format_output(res)

View File

@@ -1,6 +1,4 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from re import error
import re
from typing import Any, List, Optional, Dict, Union from typing import Any, List, Optional, Dict, Union
from typing_extensions import override from typing_extensions import override
@@ -19,6 +17,8 @@ class DataStream(ABC):
self, data_batch: List[Any], criteria: Optional[str] = None self, data_batch: List[Any], criteria: Optional[str] = None
) -> List[Any]: ) -> List[Any]:
res = [] res = []
if not criteria:
return data_batch
for data in data_batch: for data in data_batch:
if data.split(":", 1)[0] in criteria: if data.split(":", 1)[0] in criteria:
res += [data] res += [data]
@@ -31,7 +31,9 @@ class DataStream(ABC):
class SensorStream(DataStream): class SensorStream(DataStream):
def __init__(self, stream_id: str, stream_type: str) -> None: def __init__(
self, stream_id: str, stream_type: str = "Environmental Data"
) -> None:
super().__init__(stream_id, stream_type) super().__init__(stream_id, stream_type)
def process_batch(self, data_batch: List[Any]) -> str: def process_batch(self, data_batch: List[Any]) -> str:
@@ -72,7 +74,9 @@ class SensorStream(DataStream):
class TransactionStream(DataStream): class TransactionStream(DataStream):
def __init__(self, stream_id: str, stream_type: str) -> None: def __init__(
self, stream_id: str, stream_type: str = "Financial Data"
) -> None:
super().__init__(stream_id, stream_type) super().__init__(stream_id, stream_type)
def process_batch(self, data_batch: List[Any]) -> str: def process_batch(self, data_batch: List[Any]) -> str:
@@ -125,7 +129,9 @@ class TransactionStream(DataStream):
class EventStream(DataStream): class EventStream(DataStream):
def __init__(self, stream_id: str, stream_type: str) -> None: def __init__(
self, stream_id: str, stream_type: str = "System Events"
) -> None:
super().__init__(stream_id, stream_type) super().__init__(stream_id, stream_type)
def process_batch(self, data_batch: List[Any]) -> str: def process_batch(self, data_batch: List[Any]) -> str:
@@ -160,15 +166,17 @@ class EventStream(DataStream):
class StreamProcessor: class StreamProcessor:
def __init__(self) -> None: def __init__(self) -> None:
self.streams: Dict[ self.streams: Dict[str, List[DataStream]] = {
str, List[Union[SensorStream, TransactionStream, EventStream]] "sensor": [],
] = {"sensor": [], "transaction": [], "event": []} "transaction": [],
"event": [],
}
def process_batch( def process_batch(
self, self,
data_batch: List[Any], data_batch: List[Any],
stream: Union[SensorStream, TransactionStream, EventStream], stream: DataStream,
) -> Union[SensorStream, TransactionStream, EventStream]: ) -> DataStream:
try: try:
stream.process_batch(data_batch) stream.process_batch(data_batch)
if isinstance(stream, SensorStream): if isinstance(stream, SensorStream):
@@ -205,7 +213,7 @@ class StreamProcessor:
return res return res
def data_stream_tester(): def data_stream_tester() -> None:
print("=== CODE NEXUS - POLYMORPHIC STREAM SYSTEM ===\n") print("=== CODE NEXUS - POLYMORPHIC STREAM SYSTEM ===\n")
sensor_stream = SensorStream("SENSOR_001", "Environmental Data") sensor_stream = SensorStream("SENSOR_001", "Environmental Data")
print(f"Stream ID: {sensor_stream.stream_id},\ print(f"Stream ID: {sensor_stream.stream_id},\
@@ -235,7 +243,7 @@ def data_stream_tester():
events, {event_analysis['nb_error']} error detected") events, {event_analysis['nb_error']} error detected")
def stream_processor_tester(): def stream_processor_tester() -> None:
print("=== Polymorphic Stream Processing ===\n") print("=== Polymorphic Stream Processing ===\n")
processor = StreamProcessor() processor = StreamProcessor()
data_batch = [ data_batch = [
@@ -243,7 +251,7 @@ def stream_processor_tester():
["buy:100", "sell:150"], ["buy:100", "sell:150"],
["login", "error", "logout", "login", "error"], ["login", "error", "logout", "login", "error"],
] ]
streams = [ streams: list[DataStream] = [
SensorStream("SENSOR_001", "Environmental Data"), SensorStream("SENSOR_001", "Environmental Data"),
TransactionStream("TRANS_001", "Financial Data"), TransactionStream("TRANS_001", "Financial Data"),
EventStream("EVENT_001", "System Events"), EventStream("EVENT_001", "System Events"),

View File

@@ -1,5 +1,5 @@
from abc import ABC from abc import ABC
from typing import Any, List, Protocol, Union from typing import Any, Dict, List, Protocol, Union
from typing_extensions import override from typing_extensions import override
@@ -9,18 +9,35 @@ class ProcessingStage(Protocol):
class InputStage: class InputStage:
def process(self, data: Any) -> Dict: def process(self, data: Any) -> Dict[int, float]:
pass res: Dict[int, float] = {}
i = 0
for n in data:
try:
if not isinstance(n, float):
n = float(n)
res[i] = n
i += 1
except ValueError:
continue
return res
class TransformStage: class TransformStage:
def process(self, data: Any) -> Dict: def process(self, data: Any) -> Dict[str, float]:
pass res: Dict[str, Union[int, float]] = {}
res["processed_data"] = len(data)
try:
res["avg"] = sum(data.values()) / len(data)
except ZeroDivisionError:
res["avg"] = 0
return res
class OutputStage: class OutputStage:
def process(self, data: Any) -> str: def process(self, data: Any) -> str:
pass return f"Summary:\n\t\
- Processed_data: {data['processed_data']}\n\t- avg temp: {data['avg']:.1f}°C"
class ProcessingPipeline(ABC): class ProcessingPipeline(ABC):
@@ -30,10 +47,12 @@ class ProcessingPipeline(ABC):
def add_stage( def add_stage(
self, stage: Union[InputStage, TransformStage, OutputStage] self, stage: Union[InputStage, TransformStage, OutputStage]
) -> None: ) -> None:
pass self.stages += [stage]
def process(self, data: Any) -> Any: def process(self, data: Any) -> Any:
pass for stage in self.stages:
data = stage.process(data)
return data
class JSONAdapter(ProcessingPipeline): class JSONAdapter(ProcessingPipeline):
@@ -43,7 +62,10 @@ class JSONAdapter(ProcessingPipeline):
@override @override
def process(self, data: Any) -> Any: def process(self, data: Any) -> Any:
return super().process(data) res: List[Any] = []
for n in data:
res += [data[n]]
return super().process(res)
class CSVAdapter(ProcessingPipeline): class CSVAdapter(ProcessingPipeline):
@@ -53,7 +75,7 @@ class CSVAdapter(ProcessingPipeline):
@override @override
def process(self, data: Any) -> Any: def process(self, data: Any) -> Any:
return super().process(data) return super().process(data.split(","))
class StreamAdapter(ProcessingPipeline): class StreamAdapter(ProcessingPipeline):
@@ -63,12 +85,65 @@ class StreamAdapter(ProcessingPipeline):
@override @override
def process(self, data: Any) -> Any: def process(self, data: Any) -> Any:
if not isinstance(data, List):
raise Exception
return super().process(data) return super().process(data)
def tester(): class NexusManager:
pass def __init__(self, pipelines: List[ProcessingPipeline]) -> None:
self.pipelines: List[ProcessingPipeline] = pipelines
def add_pipeline(self, pipeline: ProcessingPipeline) -> None:
self.pipelines += [pipeline]
def process_data(self, data: Any) -> str:
res: str | None = None
for pipeline in self.pipelines:
pipeline.add_stage(InputStage())
pipeline.add_stage(TransformStage())
pipeline.add_stage(OutputStage())
try:
res = pipeline.process(data)
break
except Exception:
continue
if res is None:
return "[ERROR] Unknown format, incompatible with pipelines"
return res
def tester() -> None:
print("=== CODE NEXUS - ENTERPRISE PIPELINE SYSTEM ===\n")
print("Initializing Nexus Manager...")
manager = NexusManager(
[
JSONAdapter("JSON_01"),
CSVAdapter("CSV_01"),
StreamAdapter("Stream_01"),
]
)
print("\n=== test JSONAdapter ===")
data: Any = {"temp": 10, "est": "10.3", "t": 10.6, "p": "Hello"}
print(f"input: {data}")
res = manager.process_data(data)
print(f"Output: {res}\n")
print("\n=== test CSVAdapter ===")
data = "10,20,40,30"
print(f'input: "{data}"')
res = manager.process_data(data)
print(f"Output: {res}\n")
print("\n=== test StreamAdapter ===")
data = [10, 30, 0, "100"]
print(f"input: {data}")
res = manager.process_data(data)
print(f"Output: {res}\n")
print("\n=== test Invalid input ===")
data = 10
print(f"input: {data}")
res = manager.process_data(data)
print(f"Output: {res}\n")
if __name__ == "__main__": if __name__ == "__main__":
pass tester()

571
05/main.py Normal file
View File

@@ -0,0 +1,571 @@
#!/usr/bin/env python3
"""
Code Nexus - Polymorphism Testing Suite with Type Checking
This testing suite validates your implementation of polymorphic data processing systems.
It checks for proper method overriding, inheritance relationships, polymorphic behavior,
and type annotations across all three exercises.
Usage:
python3 main.py [OPTIONS]
Options:
-h, --help Show this help message and exit
-v, --verbose Enable verbose output with detailed test information
Requirements:
- Python 3.10 or later
- Your exercise files must be in ex0/, ex1/, and ex2/ directories
- All code must include proper type annotations
Expected Directory Structure:
your-project/
├── ex0/
│ └── stream_processor.py
├── ex1/
│ └── data_stream.py
├── ex2/
│ └── nexus_pipeline.py
└── main.py (this file)
Examples:
python3 main.py # Run all tests
python3 main.py --help # Show this help message
python3 main.py --verbose # Run with detailed output
"""
import sys
import importlib.util
import ast
from typing import List, Dict, Any, Optional, Tuple # noqa: F401
from pathlib import Path
class TestResult:
"""Encapsulates test results with detailed feedback."""
def __init__(self, name: str) -> None:
self.name: str = name
self.passed: bool = False
self.errors: List[str] = []
self.warnings: List[str] = []
def add_error(self, error: str) -> None:
"""Add an error message."""
self.errors.append(error)
def add_warning(self, warning: str) -> None:
"""Add a warning message."""
self.warnings.append(warning)
def mark_passed(self) -> None:
"""Mark test as passed."""
self.passed = True
class TypeChecker:
"""Validates type annotations in Python code."""
def __init__(self) -> None:
self.required_imports = [
"typing", "Any", "List", "Dict", "Union", "Optional"
]
def check_file_typing(self, file_path: str) -> Tuple[bool, List[str]]:
"""Check if file has proper type annotations."""
try:
with open(file_path, 'r') as f:
content = f.read()
tree = ast.parse(content)
issues = []
has_typing_imports = False
# Check for typing imports
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom) and node.module == "typing":
has_typing_imports = True
elif isinstance(node, ast.Import):
for alias in node.names:
if alias.name == "typing":
has_typing_imports = True
if not has_typing_imports:
issues.append("Missing typing imports")
# Check function definitions for type annotations
function_count = 0
typed_functions = 0
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef):
function_count += 1
# Check return type annotation
if node.returns is not None:
typed_functions += 1
# Check parameter type annotations
for arg in node.args.args:
if arg.annotation is None and arg.arg != "self":
issues.append(
f"Function '{node.name}' parameter "
f"'{arg.arg}' missing type annotation"
)
if function_count > 0:
typing_coverage = (typed_functions / function_count) * 100
if typing_coverage < 80:
issues.append(
f"Low typing coverage: {typing_coverage:.1f}% of "
f"functions have return type annotations"
)
return len(issues) == 0, issues
except Exception as e:
return False, [f"Error checking types: {str(e)}"]
class PolymorphismTester:
"""Main testing class for polymorphic implementations with type checking."""
def __init__(self) -> None:
self.results: List[TestResult] = []
self.type_checker = TypeChecker()
def load_module(self, file_path: str, module_name: str) -> Optional[Any]:
"""Dynamically load a Python module from file path."""
try:
spec = importlib.util.spec_from_file_location(module_name, file_path)
if spec is None or spec.loader is None:
return None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
except Exception as e:
print(f"Error loading module {module_name}: {e}")
return None
def run_all_tests(self, verbose: bool = False) -> bool:
"""Execute all test suites and return overall success status."""
print("CODE NEXUS - POLYMORPHISM TESTING SUITE WITH TYPE CHECKING")
print("=" * 60)
print("Testing polymorphic implementations with type annotations.")
if verbose:
print("Verbose mode enabled - detailed output will be shown.")
print()
# Run individual test suites
self._test_exercise_0()
self._test_exercise_1()
self._test_exercise_2()
# Display summary
self._display_summary()
return all(result.passed for result in self.results)
def _test_exercise_0(self) -> None:
"""Test Exercise 0: Data Processor Foundation with type checking."""
result = TestResult("Exercise 0: Data Processor Foundation")
print("=== Testing Exercise 0: Data Processor Foundation ===")
try:
file_path = "ex0/stream_processor.py"
# Check type annotations first
has_proper_typing, typing_issues = self.type_checker.check_file_typing(file_path)
if not has_proper_typing:
result.add_error("Type annotation issues found:")
for issue in typing_issues:
result.add_error(f"{issue}")
else:
print("✓ Type annotations properly implemented")
# Import your implementation
stream_processor = self.load_module(file_path, "stream_processor")
if not stream_processor:
result.add_error("Could not load stream_processor.py module")
else:
# Verify required classes exist
required_classes = [
'DataProcessor', 'NumericProcessor',
'TextProcessor', 'LogProcessor'
]
missing_classes = []
for class_name in required_classes:
if not hasattr(stream_processor, class_name):
missing_classes.append(class_name)
if missing_classes:
result.add_error(f"Missing classes: {', '.join(missing_classes)}")
else:
print("✓ All required classes found")
# Test inheritance relationships
DataProcessor = getattr(stream_processor, 'DataProcessor')
NumericProcessor = getattr(stream_processor, 'NumericProcessor')
TextProcessor = getattr(stream_processor, 'TextProcessor')
LogProcessor = getattr(stream_processor, 'LogProcessor')
if not issubclass(NumericProcessor, DataProcessor):
result.add_error(
"NumericProcessor must inherit from DataProcessor"
)
if not issubclass(TextProcessor, DataProcessor):
result.add_error(
"TextProcessor must inherit from DataProcessor"
)
if not issubclass(LogProcessor, DataProcessor):
result.add_error(
"LogProcessor must inherit from DataProcessor"
)
if not result.errors:
print("✓ Inheritance relationships verified")
# Test method overriding
try:
numeric_proc = NumericProcessor()
text_proc = TextProcessor()
log_proc = LogProcessor()
# Verify methods exist and are callable
for processor in [numeric_proc, text_proc, log_proc]:
for method in ['process', 'validate',
'format_output']:
if not hasattr(processor, method) or \
not callable(getattr(processor, method)):
result.add_error(
f"{processor.__class__.__name__} "
f"missing method: {method}"
)
if not result.errors:
print("✓ Method overriding implemented correctly")
if has_proper_typing:
result.mark_passed()
except Exception as e:
result.add_error(f"Error testing method overriding: {str(e)}")
except Exception as e:
result.add_error(f"Unexpected error: {str(e)}")
if not result.passed:
print("✗ Exercise 0 failed - check implementation and type annotations")
self.results.append(result)
def _test_exercise_1(self) -> None:
"""Test Exercise 1: Polymorphic Streams with type checking."""
result = TestResult("Exercise 1: Polymorphic Streams")
print("\n=== Testing Exercise 1: Polymorphic Streams ===")
try:
file_path = "ex1/data_stream.py"
# Check type annotations first
has_proper_typing, typing_issues = self.type_checker.check_file_typing(file_path)
if not has_proper_typing:
result.add_error("Type annotation issues found:")
for issue in typing_issues:
result.add_error(f"{issue}")
else:
print("✓ Type annotations properly implemented")
# Import your implementation
data_stream = self.load_module(file_path, "data_stream")
if not data_stream:
result.add_error("Could not load data_stream.py module")
else:
# Verify required classes exist
required_classes = [
'DataStream', 'SensorStream', 'TransactionStream',
'EventStream', 'StreamProcessor'
]
missing_classes = []
for class_name in required_classes:
if not hasattr(data_stream, class_name):
missing_classes.append(class_name)
if missing_classes:
result.add_error(f"Missing classes: {', '.join(missing_classes)}")
else:
print("✓ All required classes found")
# Test inheritance and polymorphism
DataStream = getattr(data_stream, 'DataStream')
SensorStream = getattr(data_stream, 'SensorStream')
TransactionStream = getattr(data_stream, 'TransactionStream')
EventStream = getattr(data_stream, 'EventStream')
# Verify inheritance
stream_classes = [
SensorStream, TransactionStream, EventStream
]
for stream_class in stream_classes:
if not issubclass(stream_class, DataStream):
result.add_error(
f"{stream_class.__name__} must inherit "
f"from DataStream"
)
if not result.errors:
print("✓ Inheritance relationships verified")
# Test polymorphic behavior
try:
sensor = SensorStream("SENSOR_001")
transaction = TransactionStream("TRANS_001")
event = EventStream("EVENT_001")
# Verify polymorphic processing
streams = [sensor, transaction, event]
for stream in streams:
if not hasattr(stream, 'process_batch') or \
not callable(getattr(stream, 'process_batch')):
result.add_error(
f"{stream.__class__.__name__} "
f"missing process_batch method"
)
if not result.errors:
print("✓ Polymorphic behavior implemented correctly")
if has_proper_typing:
result.mark_passed()
except Exception as e:
result.add_error(f"Error testing polymorphic behavior: {str(e)}")
except Exception as e:
result.add_error(f"Unexpected error: {str(e)}")
if not result.passed:
print("✗ Exercise 1 failed - check implementation and type annotations")
self.results.append(result)
def _test_exercise_2(self) -> None:
"""Test Exercise 2: Nexus Integration with type checking."""
result = TestResult("Exercise 2: Nexus Integration")
print("\n=== Testing Exercise 2: Nexus Integration ===")
try:
file_path = "ex2/nexus_pipeline.py"
# Check type annotations first
has_proper_typing, typing_issues = self.type_checker.check_file_typing(file_path)
if not has_proper_typing:
result.add_error("Type annotation issues found:")
for issue in typing_issues:
result.add_error(f"{issue}")
else:
print("✓ Type annotations properly implemented")
# Import your implementation
nexus_pipeline = self.load_module(file_path, "nexus_pipeline")
if not nexus_pipeline:
result.add_error("Could not load nexus_pipeline.py module")
else:
# Verify required classes exist
required_classes = [
'ProcessingPipeline', 'InputStage', 'TransformStage',
'OutputStage', 'JSONAdapter', 'CSVAdapter',
'StreamAdapter', 'NexusManager'
]
missing_classes = []
for class_name in required_classes:
if not hasattr(nexus_pipeline, class_name):
missing_classes.append(class_name)
if missing_classes:
result.add_error(f"Missing classes: {', '.join(missing_classes)}")
else:
print("✓ All required classes found")
# Test complex inheritance hierarchy
ProcessingPipeline = getattr(nexus_pipeline, 'ProcessingPipeline')
JSONAdapter = getattr(nexus_pipeline, 'JSONAdapter')
CSVAdapter = getattr(nexus_pipeline, 'CSVAdapter')
StreamAdapter = getattr(nexus_pipeline, 'StreamAdapter')
# Verify inheritance
adapter_classes = [
JSONAdapter, CSVAdapter, StreamAdapter
]
for adapter_class in adapter_classes:
if not issubclass(adapter_class, ProcessingPipeline):
result.add_error(
f"{adapter_class.__name__} must inherit "
f"from ProcessingPipeline"
)
if not result.errors:
print("✓ Complex inheritance hierarchy verified")
# Test enterprise-level polymorphism
try:
# Verify manager can handle different pipeline types
json_adapter = JSONAdapter("JSON_001")
csv_adapter = CSVAdapter("CSV_001")
stream_adapter = StreamAdapter("STREAM_001")
adapters = [
json_adapter, csv_adapter, stream_adapter
]
for adapter in adapters:
if not hasattr(adapter, 'process') or \
not callable(getattr(adapter, 'process')):
result.add_error(
f"{adapter.__class__.__name__} "
f"missing process method"
)
if not result.errors:
print("✓ Enterprise-level polymorphism implemented correctly")
if has_proper_typing:
result.mark_passed()
except Exception as e:
result.add_error(f"Error testing enterprise polymorphism: {str(e)}")
except Exception as e:
result.add_error(f"Unexpected error: {str(e)}")
if not result.passed:
print("✗ Exercise 2 failed - check implementation and type annotations")
self.results.append(result)
def _display_summary(self) -> None:
"""Display comprehensive test summary."""
print("\n" + "=" * 60)
print("TESTING SUMMARY")
print("=" * 60)
passed_count = sum(1 for result in self.results if result.passed)
total_count = len(self.results)
print(f"Exercises passed: {passed_count}/{total_count}")
if passed_count == total_count:
print("🎉 All tests passed! Your polymorphic implementations "
"with type annotations are working correctly.")
else:
print("⚠️ Some exercises need work. "
"Check the error messages above.")
print("Make sure you've implemented all required classes, "
"methods, and type annotations.")
# Display detailed errors if any
for result in self.results:
if result.errors:
print(f"\n{result.name}:")
for error in result.errors:
print(f"{error}")
print("\nRemember: This tests basic functionality "
"and type annotations.")
print("Make sure your code demonstrates proper polymorphic behavior "
"with complete typing!")
def print_help() -> None:
"""Display help message."""
help_text = """
CODE NEXUS - POLYMORPHISM TESTING SUITE
Usage:
python3 main.py [OPTIONS]
Options:
-h, --help Show this help message and exit
-v, --verbose Enable verbose output with detailed test information
Description:
This testing suite validates your polymorphic data processing
implementations across three exercises. It checks for:
- Proper method overriding and inheritance relationships
- Polymorphic behavior using abstract base classes (ABC)
- Comprehensive type annotations throughout all code
- Correct implementation of required classes and methods
Requirements:
- Python 3.10 or later
- Exercise files in ex0/, ex1/, and ex2/ directories
- All code must include proper type annotations
Expected Directory Structure:
your-project/
├── ex0/
│ └── stream_processor.py
├── ex1/
│ └── data_stream.py
├── ex2/
│ └── nexus_pipeline.py
└── main.py (this file)
Examples:
python3 main.py # Run all tests
python3 main.py --help # Show this help message
python3 main.py --verbose # Run with detailed output
For more information, refer to the project subject.
"""
print(help_text)
def main() -> None:
"""Main entry point for the testing suite."""
# Parse command line arguments
verbose = False
if len(sys.argv) > 1:
if sys.argv[1] in ['-h', '--help']:
print_help()
sys.exit(0)
elif sys.argv[1] in ['-v', '--verbose']:
verbose = True
else:
print(f"Unknown option: {sys.argv[1]}")
print("Use --help for usage information")
sys.exit(1)
# Verify exercise directories exist
ex0_dir = Path("ex0")
ex1_dir = Path("ex1")
ex2_dir = Path("ex2")
missing_dirs = []
if not ex0_dir.exists():
missing_dirs.append("ex0/")
if not ex1_dir.exists():
missing_dirs.append("ex1/")
if not ex2_dir.exists():
missing_dirs.append("ex2/")
if missing_dirs:
print("❌ Exercise directories not found!")
print(f"Missing: {', '.join(missing_dirs)}")
print("Please ensure your exercise files are in "
"ex0/, ex1/, and ex2/ directories")
print("\nUse --help for more information")
sys.exit(1)
tester = PolymorphismTester()
success = tester.run_all_tests(verbose=verbose)
# Exit with appropriate code
sys.exit(0 if success else 1)
if __name__ == "__main__":
main()

5
06/alchemy/__init__.py Normal file
View File

@@ -0,0 +1,5 @@
from .elements import create_fire, create_water
__version__ = "1.0.0"
__author__ = "Master Pythonicus"
__all__ = ["create_fire", "create_water"]

14
06/alchemy/elements.py Normal file
View File

@@ -0,0 +1,14 @@
def create_fire() -> str:
return "Fire element created"
def create_water() -> str:
return "Water element created"
def create_earth() -> str:
return "Earth element created"
def create_air() -> str:
return "Air element created"

View File

@@ -0,0 +1,6 @@
from .spellbook import record_spell
from .validator import validate_ingredients
__version__ = "1.0.0"
__author__ = "Master Pythonicus"
__all__ = ["record_spell", "validate_ingredients"]

View File

@@ -0,0 +1,8 @@
def record_spell(spell_name: str, ingredients: str) -> str:
from .validator import validate_ingredients
validation_res = validate_ingredients(ingredients)
if validation_res == f"{ingredients} - VALID":
return f"Spell recorded: {spell_name} ({validation_res})"
else:
return f"Spell rejected: {spell_name} ({validation_res})"

View File

@@ -0,0 +1,8 @@
def validate_ingredients(ingredients: str) -> str:
try:
for ingredient in ingredients.split(" "):
if ingredient not in ["fire", "water", "earth", "air"]:
raise ValueError
return f"{ingredients} - VALID"
except ValueError:
return f"{ingredients} - INVALID"

27
06/alchemy/potions.py Normal file
View File

@@ -0,0 +1,27 @@
def healing_potion() -> str:
from .elements import create_water, create_fire
return f"Healing potion brewed with {create_fire()} and {create_water()}"
def strength_potion() -> str:
from .elements import create_earth, create_fire
return f"Strength potion brewed with {create_earth()} and {create_fire()}"
def invisibility_potion() -> str:
from .elements import create_air, create_water
return (
f"Invisibility potion brewed with {create_air()} and {create_water()}"
)
def wisdom_potion() -> str:
from .elements import create_water, create_fire, create_air, create_earth
res = ""
for fn in (create_water(), create_fire(), create_air(), create_earth()):
res += fn
return f"Wisdom potion brewed with all elements: {res}"

View File

@@ -0,0 +1,11 @@
from .basic import lead_to_gold, stone_to_gem
from .advanced import philosophers_stone, elixir_of_life
__version__ = "1.0.0"
__author__ = "Master Pythonicus"
__all__ = [
"lead_to_gold",
"stone_to_gem",
"philosophers_stone",
"elixir_of_life",
]

View File

@@ -0,0 +1,11 @@
from .basic import lead_to_gold
from ..potions import healing_potion
def philosophers_stone() -> str:
return f"Philosophers stone created using {lead_to_gold()}\
and {healing_potion()}"
def elixir_of_life() -> str:
return f"Elixir of life: eternal youth achieved!"

View File

@@ -0,0 +1,9 @@
from alchemy.elements import create_fire, create_earth
def lead_to_gold() -> str:
return f"Lead transmuted to gold using {create_fire()}"
def stone_to_gem() -> str:
return f"Stone transmuted to gem using {create_earth()}"

44
06/ft_circular_curse.py Normal file
View File

@@ -0,0 +1,44 @@
def ingredients_validation() -> None:
from alchemy.grimoire.validator import validate_ingredients
print(
f'validate_ingredients("fire air"): {validate_ingredients("fire air")}'
)
print(
f'validate_ingredients("dragon scale"): {validate_ingredients("dragon scale")}'
)
def spell_recording_test() -> None:
from alchemy.grimoire.spellbook import record_spell
print(
f'record_spell("Fireball", "fire air"): {record_spell("Fireball", "fire air")}'
)
print(
f'record_spell("Dark Magic", "shadow"): {record_spell("Dark Magic", "shadow")}'
)
def late_import_test() -> None:
from alchemy.grimoire.spellbook import record_spell
print(
f'record_spell("Lightning", "air"): {record_spell("Lightning", "air")}'
)
def main() -> None:
print("=== Circular Curse Breaking ===\n")
print("Testing ingredient validation:")
ingredients_validation()
print("\nTesting spell recording with validation:")
spell_recording_test()
print("\nTesting late import technique:")
late_import_test()
print("\nCircular dependency curse avoided using late imports!")
print("All spells processed safely!")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,54 @@
def full_module_import() -> None:
import alchemy.elements
print(f"alchemy.elements.create_fire(): {alchemy.elements.create_fire()}")
def specific_function_import() -> None:
from alchemy.elements import create_water
print(f"create_water(): {create_water()}")
def aliased_import() -> None:
from alchemy.potions import healing_potion as heal
print(f"heal(): {heal()}")
def multiple_import() -> None:
from alchemy.elements import create_fire, create_earth
from alchemy.potions import strength_potion
print(f"create_earth(): {create_earth()}")
print(f"create_fire(): {create_fire()}")
print(f"strength_potion(): {strength_potion()}")
def main() -> None:
print("=== Import Transmutation Mastery ===\n")
print("Method 1 - Full module import:")
try:
full_module_import()
except Exception as err:
print(err)
print("\nMethod 2 - Specific function import:")
try:
specific_function_import()
except Exception as err:
print(err)
print("\nMethod 3 - Aliased import:")
try:
aliased_import()
except Exception as err:
print(err)
print("\nMethod 4 - Multiple imports:")
try:
multiple_import()
except Exception as err:
print(err)
print("\nAll import transmutation methods mastered!")
if __name__ == "__main__":
main()

48
06/ft_pathway_debate.py Normal file
View File

@@ -0,0 +1,48 @@
def absolute_import() -> None:
from alchemy.transmutation import lead_to_gold, stone_to_gem
print(f"lead_to_gold(): {lead_to_gold()}")
print(f"stone_to_gem(): {stone_to_gem()}")
def relative_import() -> None:
from alchemy.transmutation.advanced import (
philosophers_stone,
elixir_of_life,
)
print(f"philosophers_stone(): {philosophers_stone()}")
print(f"elixir_of_life(): {elixir_of_life()}")
def package_import() -> None:
import alchemy.transmutation
print(f"alchemy.transmutation.lead_to_gold():\
{alchemy.transmutation.lead_to_gold()}")
print(f"alchemy.transmutation.philosophers_stone():\
{alchemy.transmutation.philosophers_stone()}")
def main() -> None:
print("=== Pathway Debate Mastery ===\n")
print("Testing Absolute Imports (from basic.py):")
try:
absolute_import()
except Exception as err:
print(err)
print("\nTesting Relative Imports (from advanced.py):")
try:
relative_import()
except Exception as err:
print(err)
print("\nTesting Package Access:")
try:
package_import()
except Exception as err:
print(err)
print("\nBoth pathways work! Absolute: clear, Relative: concise")
if __name__ == "__main__":
main()

32
06/ft_sacred_scroll.py Normal file
View File

@@ -0,0 +1,32 @@
def main() -> None:
import alchemy
print("=== Sacred Scroll Mastery ===\n")
print("Testing direct module access:")
print(f"alchemy.elements.create_fire(): {alchemy.elements.create_fire()}")
print(
f"alchemy.elements.create_water(): {alchemy.elements.create_water()}"
)
print(
f"alchemy.elements.create_earth(): {alchemy.elements.create_earth()}"
)
print(f"alchemy.elements.create_air(): {alchemy.elements.create_air()}")
print("\nTesting package-level access (controlled by __init__.py):")
print(f"alchemy.create_fire(): {alchemy.create_fire()}")
print(f"alchemy.create_water(): {alchemy.create_water()}")
print("alchemy.create_earth(): ", end="")
try:
print(f"{alchemy.create_earth()}")
except AttributeError:
print("AttributeError - not exposed")
print("alchemy.create_air(): ", end="")
try:
print(f"{alchemy.create_air()}")
except AttributeError:
print("AttributeError - not exposed")
if __name__ == "__main__":
main()

19
07/ex0/Card.py Normal file
View File

@@ -0,0 +1,19 @@
from abc import ABC, abstractmethod
class Card(ABC):
def __init__(self, name: str, cost: int, rarity: str) -> None:
self.name = name
self.cost = cost
self.rarity = rarity
@abstractmethod
def play(self, game_state: dict) -> dict:
pass
def get_card_info(self) -> dict:
res = dict(self.__dict__)
return res
def is_playable(self, available_mana: int) -> bool:
return available_mana > 5

32
07/ex0/CreatureCard.py Normal file
View File

@@ -0,0 +1,32 @@
from .Card import Card
from typing import Dict, Union
class CreatureCard(Card):
def __init__(
self, name: str, cost: int, rarity: str, attack: int, health: int
) -> None:
super().__init__(name, cost, rarity)
self.attack = attack
self.health = health
def play(self, game_state: dict) -> dict:
try:
res: dict[str, Union[int, str]] = {}
if game_state["mana"] < 5:
raise Exception("Not enough mana")
res["card_played"] = self.name
res["mana_used"] = 5
res["effect"] = "Creature summoned to battlefield"
return res
except Exception as err:
print(err)
return {}
def attack_target(self, target: str) -> dict:
res: Dict[str, Union[int, str, bool]] = {}
res["attacker"] = self.name
res["target"] = target
res["damage_dealt"] = self.attack
res["combat_resolved"] = True
return res

6
07/ex0/__init__.py Normal file
View File

@@ -0,0 +1,6 @@
from .Card import Card
from .CreatureCard import CreatureCard
__version__ = "1.0.0"
__author__ = "moi"
__all__ = ["Card", "CreatureCard"]

22
07/ex0/main.py Normal file
View File

@@ -0,0 +1,22 @@
def main() -> None:
from .CreatureCard import CreatureCard
game_state = {"player": "michel", "mana": 6}
print("=== DataDeck Card Foundation ===\n")
print("Testing Abstract Base Class Design:\n")
creature_card = CreatureCard("Fire Dragon", 5, "Legendary", 7, 5)
print(f"CreatureCard info:\n{creature_card.get_card_info()}")
print("\nPlaying Fire Dragon with 6 mana available:")
playable = creature_card.is_playable(game_state["mana"])
print(f"Playable: {playable}")
if playable:
print(f"Play result: {creature_card.play(game_state)}")
print("\nFire Dragon attacks Goblin Warrior:")
print(f"Attack result: {creature_card.attack_target('Goblin Warrior')}")
print("\nTesting insufficient mana (3 available):")
print(f"Playable: {creature_card.is_playable(3)}")
print("\nAbstract pattern successfully demonstrated!")
if __name__ == "__main__":
main()

34
07/ex1/ArtifactCard.py Normal file
View File

@@ -0,0 +1,34 @@
from ex0 import Card
from typing import Union
class ArtifactCard(Card):
def __init__(
self, name: str, cost: int, rarity: str, durability: int, effect: str
) -> None:
super().__init__(name, cost, rarity)
self.durability = durability
self.effect = effect
def play(self, game_state: dict) -> dict:
try:
res: dict[str, Union[int, str]] = {}
if game_state["mana"] < 2:
raise Exception("Not enough mana")
res["card_played"] = self.name
res["mana_used"] = 2
res["effect"] = self.effect
return res
except Exception as err:
print(err)
return {}
def activate_ability(self) -> dict:
if self.durability <= 0:
return {
"name": self.name,
"durability": self.durability,
"destroyed": True,
}
self.durability -= 1
return self.get_card_info()

49
07/ex1/Deck.py Normal file
View File

@@ -0,0 +1,49 @@
from ex0 import Card, CreatureCard
class Deck:
def __init__(self) -> None:
self.cards: list[Card] = []
def add_card(self, card: Card) -> None:
self.cards += [card]
def remove_card(self, card_name: Card) -> None:
for n in range(len(self.cards)):
if self.cards[n].name == card_name:
self.cards.pop(n)
return
print("{card_name} not found")
def shuffle(self) -> None:
from random import shuffle
shuffle(self.cards)
def draw_card(self) -> Card:
self.shuffle()
return self.cards.pop()
def get_deck_stats(self) -> dict:
from .ArtifactCard import ArtifactCard
from .SpellCard import SpellCard
creatures = 0
spells = 0
artifacts = 0
total_cost = 0.0
for card in self.cards:
if isinstance(card, CreatureCard):
creatures += 1
elif isinstance(card, ArtifactCard):
artifacts += 1
elif isinstance(card, SpellCard):
spells += 1
total_cost += card.cost
return {
"total_card": len(self.cards),
"creatures": creatures,
"spells": spells,
"artifacts": artifacts,
"avg_cost": total_cost / len(self.cards),
}

27
07/ex1/SpellCard.py Normal file
View File

@@ -0,0 +1,27 @@
from ex0 import Card
from typing import Union
class SpellCard(Card):
def __init__(
self, name: str, cost: int, rarity: str, effect_type: str, mana: int
) -> None:
super().__init__(name, cost, rarity)
self.effect_type = effect_type
self.mana = mana
def play(self, game_state: dict) -> dict:
try:
res: dict[str, Union[int, str]] = {}
if game_state["mana"] < 3:
raise Exception("Not enough mana")
res["card_played"] = self.name
res["mana_used"] = 3
res["effect"] = self.effect_type
return res
except Exception as err:
print(err)
return {}
def resolve_effect(self, targets: list) -> dict:
return {"card": self.name, "targets": targets, "resolved": True}

7
07/ex1/__init__.py Normal file
View File

@@ -0,0 +1,7 @@
from .ArtifactCard import ArtifactCard
from .SpellCard import SpellCard
from .Deck import Deck
__version__ = "1.0.0"
__author__ = "David GAILLETON"
__all__ = ["ArtifactCard", "Deck", "SpellCard"]

32
07/ex1/main.py Normal file
View File

@@ -0,0 +1,32 @@
def main() -> None:
from .Deck import Deck
from .SpellCard import SpellCard
from .ArtifactCard import ArtifactCard
from ex0 import CreatureCard
print("=== DataDeck Deck Builder ===\n")
deck = Deck()
print("Building deck with different card types...")
deck.add_card(
SpellCard("Lightning Bolt", 5, "Common", "Deal 3 dammage to target", 5)
)
deck.add_card(
ArtifactCard(
"Mana Crystal", 7, "Medium", 1, "Permanent: +1 mana per turn"
)
)
deck.add_card(CreatureCard("Fire dragon", 10, "Rare", 15, 20))
print(f"Deck stats: {deck.get_deck_stats()}")
print("\nDrawing and playing cards:\n")
for _ in range(deck.get_deck_stats()["total_card"]):
try:
card = deck.draw_card()
print(f"Drew: {card.name} ({card.__class__.__name__})")
print(f"{card.play({'mana': 5})}\n")
except Exception as err:
print(err)
print("Polymorphism in action: Same interface, different card behaviors!")
if __name__ == "__main__":
main()

12
07/ex2/Combatable.py Normal file
View File

@@ -0,0 +1,12 @@
from abc import ABC, abstractmethod
class Combatable(ABC):
@abstractmethod
def attack(self, target: str) -> dict: ...
@abstractmethod
def defend(self, incoming_damage: int) -> dict: ...
@abstractmethod
def get_combate_stats(self) -> dict: ...

87
07/ex2/EliteCard.py Normal file
View File

@@ -0,0 +1,87 @@
from ex0.Card import Card
from .Combatable import Combatable
from .Magical import Magical
from typing import Union
class EliteCard(Card, Combatable, Magical):
def __init__(
self,
name: str,
cost: int,
rarity: str,
damage: int,
health: int,
shield: int,
initial_mana: int,
spell_cost: int,
) -> None:
super().__init__(name, cost, rarity)
self.damage = damage
self.health = health
self.shield = shield
self.mana = initial_mana
self.spell_cost = spell_cost
def play(self, game_state: dict) -> dict:
try:
res: dict[str, int | str] = {}
if game_state["mana"] < 5:
raise Exception("Not enough mana")
res["card_played"] = self.name
res["mana_used"] = 5
res["effect"] = "Creature summoned to battlefield"
return res
except Exception as err:
print(err)
return {}
def attack(self, target: str) -> dict:
return {
"attacker": self.name,
"target": target,
"damage_dealt": self.damage,
"combat_type": "melee",
}
def defend(self, incoming_damage: int) -> dict:
res: dict[str, Union[str, int, bool]] = {}
res["defender"] = self.name
if incoming_damage <= self.shield:
res["damage_blocked"] = incoming_damage
res["damage_taken"] = 0
else:
res["damage_taken"] = incoming_damage - self.shield
res["damage_blocked"] = self.shield
self.health -= incoming_damage - self.shield
res["still_alive"] = self.health > 0
return res
def get_combate_stats(self) -> dict:
return {
"damage": self.damage,
"health": self.health,
"shield": self.shield,
}
def cast_spell(self, spell_name: str, targets: list) -> dict:
try:
if self.mana < self.spell_cost:
raise Exception("Not enough mana")
self.mana -= self.spell_cost
return {
"caster": self.name,
"spell": spell_name,
"targets": targets,
"mana_used": self.spell_cost,
}
except Exception as err:
print(err)
return {}
def channel_mana(self, amount: int) -> dict:
self.mana += amount
return {"channeled": amount, "total_mana": self.mana}
def get_magic_stats(self) -> dict:
return {"mana": self.mana, "spell cost": self.spell_cost}

12
07/ex2/Magical.py Normal file
View File

@@ -0,0 +1,12 @@
from abc import ABC, abstractmethod
class Magical(ABC):
@abstractmethod
def cast_spell(self, spell_name: str, targets: list) -> dict: ...
@abstractmethod
def channel_mana(self, amount: int) -> dict: ...
@abstractmethod
def get_magic_stats(self) -> dict: ...

7
07/ex2/__init__.py Normal file
View File

@@ -0,0 +1,7 @@
from .Combatable import Combatable
from .Magical import Magical
from .EliteCard import EliteCard
__version__ = "1.0.0"
__author__ = "dgaillet"
__all__ = ["Combatable", "Magical", "EliteCard"]

21
07/ex2/main.py Normal file
View File

@@ -0,0 +1,21 @@
def main() -> None:
from .EliteCard import EliteCard
print("=== DataDeck Ability System ===\n")
card = EliteCard("Arcane Warrior", 25, "Legendary", 5, 20, 3, 4, 4)
print("EliteCard capabilities:")
print("- Card: ['play', 'get_card_info', 'is_playable']")
print("- Combatable: ['attack', 'defend', 'get_combat_stats']")
print("- Magical: ['cast_spell', 'channel_mana', 'get_magic_stats']")
print("\nPlaying Arcane Warrior (Elite Card):\n")
print("Combat phase:")
print(f"Attack result: {card.attack('Enemy')}")
print(f"Defense result: {card.defend(5)}")
print("\nMagic phase:")
print(f"Spell cast: {card.cast_spell('Fireball', ['Enemy1', 'Enemy2'])}")
print(f"Mana channel: {card.channel_mana(3)}")
print("Multiple interface implementation successful!")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,22 @@
from .GameStrategy import GameStrategy
from operator import attrgetter
class AgressiveStrategy(GameStrategy):
def execute_turn(self, hand: list, battlefield: list) -> dict:
return {
"cards_played": [card.name for card in hand],
"mana_used": 5,
"targets_attacked": battlefield,
"damage_dealt": 8,
}
def get_strategy_name(self) -> str:
return "Aggressive"
def prioritize_targets(self, available_targets: list) -> list:
try:
return sorted(available_targets, key=attrgetter("health"))
except Exception as err:
print(err)
return available_targets

23
07/ex3/CardFactory.py Normal file
View File

@@ -0,0 +1,23 @@
from abc import ABC, abstractmethod
from ex0 import Card
class CardFactory(ABC):
@abstractmethod
def create_creature(
self, name_or_power: str | int | None = None
) -> Card: ...
@abstractmethod
def create_spell(self, name_or_power: str | int | None = None) -> Card: ...
@abstractmethod
def create_artifact(
self, name_or_power: str | int | None = None
) -> Card: ...
@abstractmethod
def create_themed_deck(self, size: int) -> dict: ...
@abstractmethod
def get_supported_types(self) -> dict: ...

View File

@@ -0,0 +1,76 @@
from .CardFactory import CardFactory
from random import choice
from ex0 import Card, CreatureCard
from ex1 import SpellCard, ArtifactCard
from copy import deepcopy
creature_cards = [
CreatureCard("Fire Dragon", 20, "Rare", 7, 20),
CreatureCard("Goblin Warrior", 12, "Common", 4, 10),
]
spell_cards = [
SpellCard("Fire Ball", 5, "Rare", "Decrase health by 1 for 3 round", 5),
SpellCard("Ice Spike", 3, "Common", "Reduce damage by 2 for 1 round", 3),
SpellCard(
"Lightning bolt", 10, "Legendary", "Card can't play for 3 round", 10
),
]
artifact_cards = [
ArtifactCard(
"Mana Ring", 2, "Common", 3, "Increase mana by 1 for each round"
),
ArtifactCard(
"Witch Staff", 6, "Legendary", 5, "Decrease 5 mana of a card"
),
]
class FantasyCardFactory(CardFactory):
def create_creature(self, name_or_power: str | int | None = None) -> Card:
card = deepcopy(choice(creature_cards))
if isinstance(name_or_power, str):
card.name = name_or_power
elif isinstance(name_or_power, int):
card.attack = name_or_power
return card
def create_spell(self, name_or_power: str | int | None = None) -> Card:
card = deepcopy(choice(spell_cards))
if isinstance(name_or_power, str):
card.name = name_or_power
elif isinstance(name_or_power, int):
card.mana = name_or_power
return card
def create_artifact(self, name_or_power: str | int | None = None) -> Card:
card = deepcopy(choice(artifact_cards))
if isinstance(name_or_power, str):
card.name = name_or_power
elif isinstance(name_or_power, int):
card.durability = name_or_power
return card
def create_themed_deck(self, size: int) -> dict:
deck: dict[str, list[Card]] = {}
for _ in range(size):
card = choice(
[
self.create_creature(),
self.create_artifact(),
self.create_spell(),
]
)
if card.name in deck:
deck[card.name] += [card]
else:
deck[card.name] = [card]
return deck
def get_supported_types(self) -> dict:
return {
"creatures": [card.name for card in creature_cards],
"spells": [card.name for card in spell_cards],
"artifacts": [card.name for card in artifact_cards],
}

41
07/ex3/GameEngine.py Normal file
View File

@@ -0,0 +1,41 @@
from .CardFactory import CardFactory
from .GameStrategy import GameStrategy
class GameEngine:
def __init__(self) -> None:
self.turns_simulated = 0
self.total_damage = 0
self.created_cards = 0
def configure_engine(
self, factory: CardFactory, strategy: GameStrategy
) -> None:
self.factory = factory
self.strategy = strategy
def simulate_turn(self) -> dict:
try:
cards = []
deck = self.factory.create_themed_deck(3).values()
for card_list in deck:
for card in card_list:
cards += [card]
hand = [f"{card.name} ({card.cost})" for card in cards]
print(f"Hand: {hand}")
turn = self.strategy.execute_turn(cards, ["Enemy player"])
self.total_damage += turn["damage_dealt"]
self.created_cards += 3
self.turns_simulated += 1
return turn
except Exception as err:
print(err)
return {}
def get_engine_status(self) -> dict:
return {
"turns_simulated": self.turns_simulated,
"strategy_used": self.strategy.get_strategy_name(),
"total_damage": self.total_damage,
"cards_created": self.created_cards,
}

12
07/ex3/GameStrategy.py Normal file
View File

@@ -0,0 +1,12 @@
from abc import ABC, abstractmethod
class GameStrategy(ABC):
@abstractmethod
def execute_turn(self, hand: list, battlefield: list) -> dict: ...
@abstractmethod
def get_strategy_name(self) -> str: ...
@abstractmethod
def prioritize_targets(self, available_targets: list) -> list: ...

15
07/ex3/__init__.py Normal file
View File

@@ -0,0 +1,15 @@
from .AggresiveStrategy import AgressiveStrategy
from .CardFactory import CardFactory
from .FantasyCardFactory import FantasyCardFactory
from .GameEngine import GameEngine
from .GameStrategy import GameStrategy
__version__ = "1.0.0"
__author__ = "dgaillet"
__all__ = [
"AgressiveStrategy",
"CardFactory",
"FantasyCardFactory",
"GameEngine",
"GameStrategy",
]

27
07/ex3/main.py Normal file
View File

@@ -0,0 +1,27 @@
from .GameEngine import GameEngine
from .FantasyCardFactory import FantasyCardFactory
from .AggresiveStrategy import AgressiveStrategy
def main() -> None:
print("=== DataDeck Game Engine ===\n")
print("Configuring Fantasy Card Game...")
engine = GameEngine()
engine.configure_engine(FantasyCardFactory(), AgressiveStrategy())
print(f"Factory: {engine.factory.__class__.__name__}")
print(f"Strategy: {engine.strategy.__class__.__name__}")
print(f"Available types: {engine.factory.get_supported_types()}")
print("\nSimulating aggressive turn...")
actions = engine.simulate_turn()
print("\nTurn execution:")
print(f"Strategy: {engine.strategy.get_strategy_name()}")
print(f"Actions: {actions}")
print("\nGame Report:")
print(f"{engine.get_engine_status()}")
print(
"\nAbstract Factory + Strategy Pattern: Maximum flexibility achieved!"
)
if __name__ == "__main__":
main()

15
07/ex4/Rankable.py Normal file
View File

@@ -0,0 +1,15 @@
from abc import ABC, abstractmethod
class Rankable(ABC):
@abstractmethod
def calculate_rating(self) -> int: ...
@abstractmethod
def update_wins(self, wins: int) -> None: ...
@abstractmethod
def update_losses(self, losses: int) -> None: ...
@abstractmethod
def get_rank_info(self) -> dict: ...

97
07/ex4/TournamentCard.py Normal file
View File

@@ -0,0 +1,97 @@
from ex0 import Card
from ex2 import Combatable
from .Rankable import Rankable
from typing import Union
class TournamentCard(Card, Combatable, Rankable):
def __init__(
self,
id: str,
name: str,
cost: int,
rarity: str,
damage: int,
health: int,
shield: int,
rank: int,
) -> None:
super().__init__(name, cost, rarity)
self.id = id
self.looses = 0
self.wins = 0
self.damage = damage
self.health = health
self.shield = shield
self.rank = rank
def play(self, game_state: dict) -> dict:
try:
res: dict[str, int | str] = {}
if game_state["mana"] < 2:
raise Exception("Not enough mana")
res["card_played"] = self.name
res["mana_used"] = 2
return res
except Exception as err:
print(err)
return {}
def attack(self, target: str) -> dict:
return {
"attacker": self.name,
"target": target,
"damage_dealt": self.damage,
"combat_type": "melee",
}
def defend(self, incoming_damage: int) -> dict:
res: dict[str, Union[str, int, bool]] = {}
res["defender"] = self.name
if incoming_damage <= self.shield:
res["damage_blocked"] = incoming_damage
res["damage_taken"] = 0
else:
res["damage_taken"] = incoming_damage - self.shield
res["damage_blocked"] = self.shield
self.health -= incoming_damage - self.shield
res["still_alive"] = self.health > 0
return res
def get_combate_stats(self) -> dict:
return {
"damage": self.damage,
"health": self.health,
"shield": self.shield,
}
def calculate_rating(self) -> int:
try:
if self.wins == 0:
return self.rank - self.looses * 10
return self.rank + (int(self.wins / self.looses) * 10)
except ZeroDivisionError:
return self.rank + self.wins * 10
except Exception as err:
print(err)
return self.rank
def get_tournament_stats(self) -> dict:
return {
"Interfaces": "[Card, Combatable, Rankable]",
"Rating": self.calculate_rating(),
"Record": f"{self.wins}-{self.looses}",
}
def update_wins(self, wins: int) -> None:
self.wins = wins
def update_losses(self, losses: int) -> None:
self.looses = losses
def get_rank_info(self) -> dict:
return {
"wins": self.wins,
"looses": self.looses,
"rank": self.rank,
}

View File

@@ -0,0 +1,65 @@
from .TournamentCard import TournamentCard
class TournamentPlatform:
def __init__(self) -> None:
self.cards: list[TournamentCard] = []
self.math_played = 0
def register_card(self, card: TournamentCard) -> str:
try:
stats = card.get_tournament_stats()
res = f"{card.name} (ID: {card.id}):\n\
- Interfaces: {stats['Interfaces']}\n\
- Rating: {stats['Rating']}\n\
- Record: {stats['Record']}\n"
self.cards += [card]
return res
except Exception:
print("Invalid card")
return "error"
def create_match(self, card1_id: str, card2_id: str) -> dict:
from random import shuffle
try:
cards = [
card
for card in self.cards
if card.id == card1_id or card.id == card2_id
]
if len(cards) != 2:
raise Exception(
"At least once of cards id provide isn't in platform"
)
shuffle(cards)
winner = cards.pop()
loser = cards.pop()
winner.update_wins(winner.wins + 1)
loser.update_losses(loser.looses + 1)
self.math_played += 1
return {
"winner": winner.id,
"loser": loser.id,
"winner_rating": winner.calculate_rating(),
"loser_rating": loser.calculate_rating(),
}
except Exception as err:
print(err)
return {}
def get_leaderboard(self) -> list:
return sorted(
self.cards, key=lambda x: x.calculate_rating(), reverse=True
)
def generate_tournament_report(self) -> dict:
return {
"total_cards": len(self.cards),
"mathes_played": self.math_played,
"avg_rating": int(
sum([card.calculate_rating() for card in self.cards])
/ len(self.cards)
),
"platform_status": "active",
}

7
07/ex4/__init__.py Normal file
View File

@@ -0,0 +1,7 @@
from .Rankable import Rankable
from .TournamentCard import TournamentCard
from .TournamentPlatform import TournamentPlatform
__version__ = "1.0.0"
__author__ = "dgaillet"
__all__ = ["Rankable", "TournamentCard", "TournamentPlatform"]

39
07/ex4/main.py Normal file
View File

@@ -0,0 +1,39 @@
from .TournamentCard import TournamentCard
from .TournamentPlatform import TournamentPlatform
def main() -> None:
print("=== DataDeck Tournament Platform ===\n")
print("Registering Tournament Cards...\n")
platform = TournamentPlatform()
print(
platform.register_card(
TournamentCard(
"dragon_001", "Fire Dragon", 20, "Rare", 15, 25, 5, 1200
)
)
)
print(
platform.register_card(
TournamentCard(
"wizard_001", "Ice Wizard", 15, "common", 10, 30, 4, 1150
)
)
)
print("Creating tournament match...")
match_res = platform.create_match("dragon_001", "wizard_001")
print(f"Math result: {match_res}\n")
print("Tournament Leaderboard:")
leaderboard = platform.get_leaderboard()
for i in range(len(leaderboard)):
print(f"{i + 1}. {leaderboard[i].name} - Rating:\
{leaderboard[i].calculate_rating()}\
({leaderboard[i].get_tournament_stats()['Record']})")
print("\nPlatform Report:")
print(platform.generate_tournament_report())
print("\n=== Tournament Platform Successfully Deployed! ===")
print("All abstract patterns working together harmoniously!")
if __name__ == "__main__":
main()

41
08/ex0/construct.py Normal file
View File

@@ -0,0 +1,41 @@
import site
import os
import sys
def not_in_venv() -> None:
print("\nMATRIX STATUS: You're still plugged in\n")
print(f"Current Python: {sys.executable}")
print("Virtual Environment: None detected\n")
print("WARNING: You're in the global environment!\n\
The machines can see everything you install.\n")
print("To enter the construct, run:\n\
python -m venv matrix_env\n\
source matrix_env/bin/activate # On Unix\n\
matrix_env\n\
Scripts\n\
activate # On Windows\n")
print("Then run this program again.")
def in_venv() -> None:
print("\nMATRIX STATUS: Welcome to the construct\n")
print(f"Current Python: {sys.executable}")
print(f"Virtual Environment: {sys.prefix.split('/')[-1]}")
print(f"Environment Path: {sys.prefix}")
print("\nSUCCESS: You're in an isolated environment!\n\
Safe to install packages without affecting\n\
the global system.\n")
print("Package installation path:")
print(site.getsitepackages()[0])
def main() -> None:
if "VIRTUAL_ENV" in os.environ:
in_venv()
else:
not_in_venv()
if __name__ == "__main__":
main()

60
08/ex1/loading.py Normal file
View File

@@ -0,0 +1,60 @@
from importlib.metadata import PackageNotFoundError, version
from numpy import ndarray
from pandas import pandas as pd
import matplotlib.pyplot as plt
from os import mkdir
def check_package(packages: dict[str, str]) -> bool:
print("Checking dependencies:")
imported = 0
for package in packages:
try:
print(f"[\u001b[32mOK\u001b[0m] {package}\
({version(package)}) - {packages[package]} ready")
imported += 1
except PackageNotFoundError:
print(f"[\u001b[31mKO\u001b[0m] {package} -\
{packages[package]} not ready")
return imported == len(packages)
def analyze(data: ndarray) -> None:
print("Processing 1000 data points...")
x = data[:, 0]
y = data[:, 1].astype(float)
plt.figure(figsize=(8, 8), dpi=1200)
plt.plot(x, y)
try:
mkdir("matrix")
except Exception:
pass
print("Generating visualization...\n")
plt.savefig("matrix/_analysis.png")
print("Analysis complete!")
print("Results saved to: matrix/_analysis.png}")
def main() -> None:
print("\nLOADING STATUS: Loading programs...\n")
if check_package(
{
"pandas": "Data manipulation",
"numpy": "mathematics manipulation",
"matplotlib": "Visualization",
}
):
try:
print("\nAnalyzing Matrix data...")
data = pd.read_csv("meteo.csv", parse_dates=["time"]).to_numpy()
analyze(data)
except FileNotFoundError:
print("File not found")
except Exception as err:
print(err)
else:
print("\nPackages not installed")
if __name__ == "__main__":
main()

BIN
08/ex1/matrix/_analysis.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

193
08/ex1/meteo.csv Normal file
View File

@@ -0,0 +1,193 @@
time,temperature
2026-02-28T00:00,9.7
2026-02-28T01:00,9.2
2026-02-28T02:00,9.3
2026-02-28T03:00,9.0
2026-02-28T04:00,8.5
2026-02-28T05:00,8.0
2026-02-28T06:00,7.3
2026-02-28T07:00,8.6
2026-02-28T08:00,10.0
2026-02-28T09:00,11.4
2026-02-28T10:00,13.7
2026-02-28T11:00,15.3
2026-02-28T12:00,16.7
2026-02-28T13:00,17.1
2026-02-28T14:00,17.2
2026-02-28T15:00,17.1
2026-02-28T16:00,16.6
2026-02-28T17:00,15.8
2026-02-28T18:00,14.6
2026-02-28T19:00,14.0
2026-02-28T20:00,13.4
2026-02-28T21:00,12.4
2026-02-28T22:00,11.9
2026-02-28T23:00,11.4
2026-03-01T00:00,10.8
2026-03-01T01:00,10.0
2026-03-01T02:00,8.9
2026-03-01T03:00,8.2
2026-03-01T04:00,7.6
2026-03-01T05:00,7.0
2026-03-01T06:00,6.5
2026-03-01T07:00,6.4
2026-03-01T08:00,6.5
2026-03-01T09:00,7.1
2026-03-01T10:00,7.9
2026-03-01T11:00,8.7
2026-03-01T12:00,9.6
2026-03-01T13:00,10.4
2026-03-01T14:00,10.4
2026-03-01T15:00,10.8
2026-03-01T16:00,10.4
2026-03-01T17:00,9.7
2026-03-01T18:00,8.3
2026-03-01T19:00,7.0
2026-03-01T20:00,5.8
2026-03-01T21:00,4.7
2026-03-01T22:00,4.3
2026-03-01T23:00,3.6
2026-03-02T00:00,3.3
2026-03-02T01:00,3.5
2026-03-02T02:00,3.9
2026-03-02T03:00,3.2
2026-03-02T04:00,3.0
2026-03-02T05:00,2.6
2026-03-02T06:00,3.0
2026-03-02T07:00,3.8
2026-03-02T08:00,5.5
2026-03-02T09:00,7.7
2026-03-02T10:00,9.8
2026-03-02T11:00,11.8
2026-03-02T12:00,13.0
2026-03-02T13:00,13.6
2026-03-02T14:00,13.8
2026-03-02T15:00,13.4
2026-03-02T16:00,12.9
2026-03-02T17:00,11.9
2026-03-02T18:00,10.1
2026-03-02T19:00,8.7
2026-03-02T20:00,7.5
2026-03-02T21:00,7.0
2026-03-02T22:00,6.0
2026-03-02T23:00,5.3
2026-03-03T00:00,4.7
2026-03-03T01:00,4.1
2026-03-03T02:00,3.4
2026-03-03T03:00,3.0
2026-03-03T04:00,2.4
2026-03-03T05:00,1.7
2026-03-03T06:00,2.3
2026-03-03T07:00,4.0
2026-03-03T08:00,6.0
2026-03-03T09:00,8.0
2026-03-03T10:00,10.5
2026-03-03T11:00,12.5
2026-03-03T12:00,14.0
2026-03-03T13:00,15.1
2026-03-03T14:00,15.3
2026-03-03T15:00,15.4
2026-03-03T16:00,14.6
2026-03-03T17:00,13.3
2026-03-03T18:00,11.8
2026-03-03T19:00,10.5
2026-03-03T20:00,8.7
2026-03-03T21:00,8.5
2026-03-03T22:00,7.8
2026-03-03T23:00,7.5
2026-03-04T00:00,6.8
2026-03-04T01:00,5.9
2026-03-04T02:00,5.1
2026-03-04T03:00,4.7
2026-03-04T04:00,5.1
2026-03-04T05:00,5.0
2026-03-04T06:00,5.4
2026-03-04T07:00,6.0
2026-03-04T08:00,6.6
2026-03-04T09:00,7.9
2026-03-04T10:00,8.4
2026-03-04T11:00,9.4
2026-03-04T12:00,10.0
2026-03-04T13:00,10.8
2026-03-04T14:00,11.2
2026-03-04T15:00,11.0
2026-03-04T16:00,10.4
2026-03-04T17:00,9.1
2026-03-04T18:00,7.6
2026-03-04T19:00,6.8
2026-03-04T20:00,6.1
2026-03-04T21:00,5.6
2026-03-04T22:00,3.8
2026-03-04T23:00,3.4
2026-03-05T00:00,3.0
2026-03-05T01:00,2.2
2026-03-05T02:00,1.5
2026-03-05T03:00,1.3
2026-03-05T04:00,0.7
2026-03-05T05:00,0.3
2026-03-05T06:00,0.7
2026-03-05T07:00,2.0
2026-03-05T08:00,4.4
2026-03-05T09:00,6.8
2026-03-05T10:00,8.6
2026-03-05T11:00,10.2
2026-03-05T12:00,11.3
2026-03-05T13:00,12.3
2026-03-05T14:00,12.6
2026-03-05T15:00,12.6
2026-03-05T16:00,11.6
2026-03-05T17:00,10.1
2026-03-05T18:00,8.9
2026-03-05T19:00,7.4
2026-03-05T20:00,6.3
2026-03-05T21:00,5.3
2026-03-05T22:00,4.6
2026-03-05T23:00,3.7
2026-03-06T00:00,3.2
2026-03-06T01:00,2.9
2026-03-06T02:00,2.5
2026-03-06T03:00,2.1
2026-03-06T04:00,1.6
2026-03-06T05:00,1.0
2026-03-06T06:00,1.3
2026-03-06T07:00,2.5
2026-03-06T08:00,4.7
2026-03-06T09:00,7.7
2026-03-06T10:00,10.5
2026-03-06T11:00,12.7
2026-03-06T12:00,14.1
2026-03-06T13:00,15.0
2026-03-06T14:00,15.0
2026-03-06T15:00,14.9
2026-03-06T16:00,13.9
2026-03-06T17:00,12.4
2026-03-06T18:00,10.6
2026-03-06T19:00,8.8
2026-03-06T20:00,8.1
2026-03-06T21:00,7.3
2026-03-06T22:00,6.5
2026-03-06T23:00,5.9
2026-03-07T00:00,5.1
2026-03-07T01:00,4.6
2026-03-07T02:00,4.2
2026-03-07T03:00,3.6
2026-03-07T04:00,2.8
2026-03-07T05:00,2.9
2026-03-07T06:00,3.3
2026-03-07T07:00,4.2
2026-03-07T08:00,6.1
2026-03-07T09:00,9.0
2026-03-07T10:00,11.6
2026-03-07T11:00,13.7
2026-03-07T12:00,15.1
2026-03-07T13:00,16.2
2026-03-07T14:00,16.4
2026-03-07T15:00,16.4
2026-03-07T16:00,16.4
2026-03-07T17:00,14.7
2026-03-07T18:00,12.8
2026-03-07T19:00,11.5
2026-03-07T20:00,10.2
2026-03-07T21:00,9.0
2026-03-07T22:00,8.1
2026-03-07T23:00,7.3
1 time temperature
2 2026-02-28T00:00 9.7
3 2026-02-28T01:00 9.2
4 2026-02-28T02:00 9.3
5 2026-02-28T03:00 9.0
6 2026-02-28T04:00 8.5
7 2026-02-28T05:00 8.0
8 2026-02-28T06:00 7.3
9 2026-02-28T07:00 8.6
10 2026-02-28T08:00 10.0
11 2026-02-28T09:00 11.4
12 2026-02-28T10:00 13.7
13 2026-02-28T11:00 15.3
14 2026-02-28T12:00 16.7
15 2026-02-28T13:00 17.1
16 2026-02-28T14:00 17.2
17 2026-02-28T15:00 17.1
18 2026-02-28T16:00 16.6
19 2026-02-28T17:00 15.8
20 2026-02-28T18:00 14.6
21 2026-02-28T19:00 14.0
22 2026-02-28T20:00 13.4
23 2026-02-28T21:00 12.4
24 2026-02-28T22:00 11.9
25 2026-02-28T23:00 11.4
26 2026-03-01T00:00 10.8
27 2026-03-01T01:00 10.0
28 2026-03-01T02:00 8.9
29 2026-03-01T03:00 8.2
30 2026-03-01T04:00 7.6
31 2026-03-01T05:00 7.0
32 2026-03-01T06:00 6.5
33 2026-03-01T07:00 6.4
34 2026-03-01T08:00 6.5
35 2026-03-01T09:00 7.1
36 2026-03-01T10:00 7.9
37 2026-03-01T11:00 8.7
38 2026-03-01T12:00 9.6
39 2026-03-01T13:00 10.4
40 2026-03-01T14:00 10.4
41 2026-03-01T15:00 10.8
42 2026-03-01T16:00 10.4
43 2026-03-01T17:00 9.7
44 2026-03-01T18:00 8.3
45 2026-03-01T19:00 7.0
46 2026-03-01T20:00 5.8
47 2026-03-01T21:00 4.7
48 2026-03-01T22:00 4.3
49 2026-03-01T23:00 3.6
50 2026-03-02T00:00 3.3
51 2026-03-02T01:00 3.5
52 2026-03-02T02:00 3.9
53 2026-03-02T03:00 3.2
54 2026-03-02T04:00 3.0
55 2026-03-02T05:00 2.6
56 2026-03-02T06:00 3.0
57 2026-03-02T07:00 3.8
58 2026-03-02T08:00 5.5
59 2026-03-02T09:00 7.7
60 2026-03-02T10:00 9.8
61 2026-03-02T11:00 11.8
62 2026-03-02T12:00 13.0
63 2026-03-02T13:00 13.6
64 2026-03-02T14:00 13.8
65 2026-03-02T15:00 13.4
66 2026-03-02T16:00 12.9
67 2026-03-02T17:00 11.9
68 2026-03-02T18:00 10.1
69 2026-03-02T19:00 8.7
70 2026-03-02T20:00 7.5
71 2026-03-02T21:00 7.0
72 2026-03-02T22:00 6.0
73 2026-03-02T23:00 5.3
74 2026-03-03T00:00 4.7
75 2026-03-03T01:00 4.1
76 2026-03-03T02:00 3.4
77 2026-03-03T03:00 3.0
78 2026-03-03T04:00 2.4
79 2026-03-03T05:00 1.7
80 2026-03-03T06:00 2.3
81 2026-03-03T07:00 4.0
82 2026-03-03T08:00 6.0
83 2026-03-03T09:00 8.0
84 2026-03-03T10:00 10.5
85 2026-03-03T11:00 12.5
86 2026-03-03T12:00 14.0
87 2026-03-03T13:00 15.1
88 2026-03-03T14:00 15.3
89 2026-03-03T15:00 15.4
90 2026-03-03T16:00 14.6
91 2026-03-03T17:00 13.3
92 2026-03-03T18:00 11.8
93 2026-03-03T19:00 10.5
94 2026-03-03T20:00 8.7
95 2026-03-03T21:00 8.5
96 2026-03-03T22:00 7.8
97 2026-03-03T23:00 7.5
98 2026-03-04T00:00 6.8
99 2026-03-04T01:00 5.9
100 2026-03-04T02:00 5.1
101 2026-03-04T03:00 4.7
102 2026-03-04T04:00 5.1
103 2026-03-04T05:00 5.0
104 2026-03-04T06:00 5.4
105 2026-03-04T07:00 6.0
106 2026-03-04T08:00 6.6
107 2026-03-04T09:00 7.9
108 2026-03-04T10:00 8.4
109 2026-03-04T11:00 9.4
110 2026-03-04T12:00 10.0
111 2026-03-04T13:00 10.8
112 2026-03-04T14:00 11.2
113 2026-03-04T15:00 11.0
114 2026-03-04T16:00 10.4
115 2026-03-04T17:00 9.1
116 2026-03-04T18:00 7.6
117 2026-03-04T19:00 6.8
118 2026-03-04T20:00 6.1
119 2026-03-04T21:00 5.6
120 2026-03-04T22:00 3.8
121 2026-03-04T23:00 3.4
122 2026-03-05T00:00 3.0
123 2026-03-05T01:00 2.2
124 2026-03-05T02:00 1.5
125 2026-03-05T03:00 1.3
126 2026-03-05T04:00 0.7
127 2026-03-05T05:00 0.3
128 2026-03-05T06:00 0.7
129 2026-03-05T07:00 2.0
130 2026-03-05T08:00 4.4
131 2026-03-05T09:00 6.8
132 2026-03-05T10:00 8.6
133 2026-03-05T11:00 10.2
134 2026-03-05T12:00 11.3
135 2026-03-05T13:00 12.3
136 2026-03-05T14:00 12.6
137 2026-03-05T15:00 12.6
138 2026-03-05T16:00 11.6
139 2026-03-05T17:00 10.1
140 2026-03-05T18:00 8.9
141 2026-03-05T19:00 7.4
142 2026-03-05T20:00 6.3
143 2026-03-05T21:00 5.3
144 2026-03-05T22:00 4.6
145 2026-03-05T23:00 3.7
146 2026-03-06T00:00 3.2
147 2026-03-06T01:00 2.9
148 2026-03-06T02:00 2.5
149 2026-03-06T03:00 2.1
150 2026-03-06T04:00 1.6
151 2026-03-06T05:00 1.0
152 2026-03-06T06:00 1.3
153 2026-03-06T07:00 2.5
154 2026-03-06T08:00 4.7
155 2026-03-06T09:00 7.7
156 2026-03-06T10:00 10.5
157 2026-03-06T11:00 12.7
158 2026-03-06T12:00 14.1
159 2026-03-06T13:00 15.0
160 2026-03-06T14:00 15.0
161 2026-03-06T15:00 14.9
162 2026-03-06T16:00 13.9
163 2026-03-06T17:00 12.4
164 2026-03-06T18:00 10.6
165 2026-03-06T19:00 8.8
166 2026-03-06T20:00 8.1
167 2026-03-06T21:00 7.3
168 2026-03-06T22:00 6.5
169 2026-03-06T23:00 5.9
170 2026-03-07T00:00 5.1
171 2026-03-07T01:00 4.6
172 2026-03-07T02:00 4.2
173 2026-03-07T03:00 3.6
174 2026-03-07T04:00 2.8
175 2026-03-07T05:00 2.9
176 2026-03-07T06:00 3.3
177 2026-03-07T07:00 4.2
178 2026-03-07T08:00 6.1
179 2026-03-07T09:00 9.0
180 2026-03-07T10:00 11.6
181 2026-03-07T11:00 13.7
182 2026-03-07T12:00 15.1
183 2026-03-07T13:00 16.2
184 2026-03-07T14:00 16.4
185 2026-03-07T15:00 16.4
186 2026-03-07T16:00 16.4
187 2026-03-07T17:00 14.7
188 2026-03-07T18:00 12.8
189 2026-03-07T19:00 11.5
190 2026-03-07T20:00 10.2
191 2026-03-07T21:00 9.0
192 2026-03-07T22:00 8.1
193 2026-03-07T23:00 7.3

984
08/ex1/poetry.lock generated Normal file
View File

@@ -0,0 +1,984 @@
# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand.
[[package]]
name = "contourpy"
version = "1.3.2"
description = "Python library for calculating contours of 2D quadrilateral grids"
optional = false
python-versions = ">=3.10"
groups = ["main"]
markers = "python_version == \"3.10\""
files = [
{file = "contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934"},
{file = "contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989"},
{file = "contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d"},
{file = "contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9"},
{file = "contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512"},
{file = "contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631"},
{file = "contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f"},
{file = "contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2"},
{file = "contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0"},
{file = "contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a"},
{file = "contourpy-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a37a2fb93d4df3fc4c0e363ea4d16f83195fc09c891bc8ce072b9d084853445"},
{file = "contourpy-1.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7cd50c38f500bbcc9b6a46643a40e0913673f869315d8e70de0438817cb7773"},
{file = "contourpy-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6658ccc7251a4433eebd89ed2672c2ed96fba367fd25ca9512aa92a4b46c4f1"},
{file = "contourpy-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70771a461aaeb335df14deb6c97439973d253ae70660ca085eec25241137ef43"},
{file = "contourpy-1.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a887a6e8c4cd0897507d814b14c54a8c2e2aa4ac9f7686292f9769fcf9a6ab"},
{file = "contourpy-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3859783aefa2b8355697f16642695a5b9792e7a46ab86da1118a4a23a51a33d7"},
{file = "contourpy-1.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eab0f6db315fa4d70f1d8ab514e527f0366ec021ff853d7ed6a2d33605cf4b83"},
{file = "contourpy-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d91a3ccc7fea94ca0acab82ceb77f396d50a1f67412efe4c526f5d20264e6ecd"},
{file = "contourpy-1.3.2-cp311-cp311-win32.whl", hash = "sha256:1c48188778d4d2f3d48e4643fb15d8608b1d01e4b4d6b0548d9b336c28fc9b6f"},
{file = "contourpy-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5ebac872ba09cb8f2131c46b8739a7ff71de28a24c869bcad554477eb089a878"},
{file = "contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2"},
{file = "contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15"},
{file = "contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92"},
{file = "contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87"},
{file = "contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415"},
{file = "contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe"},
{file = "contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441"},
{file = "contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e"},
{file = "contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912"},
{file = "contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73"},
{file = "contourpy-1.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:de39db2604ae755316cb5967728f4bea92685884b1e767b7c24e983ef5f771cb"},
{file = "contourpy-1.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f9e896f447c5c8618f1edb2bafa9a4030f22a575ec418ad70611450720b5b08"},
{file = "contourpy-1.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71e2bd4a1c4188f5c2b8d274da78faab884b59df20df63c34f74aa1813c4427c"},
{file = "contourpy-1.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de425af81b6cea33101ae95ece1f696af39446db9682a0b56daaa48cfc29f38f"},
{file = "contourpy-1.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:977e98a0e0480d3fe292246417239d2d45435904afd6d7332d8455981c408b85"},
{file = "contourpy-1.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:434f0adf84911c924519d2b08fc10491dd282b20bdd3fa8f60fd816ea0b48841"},
{file = "contourpy-1.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c66c4906cdbc50e9cba65978823e6e00b45682eb09adbb78c9775b74eb222422"},
{file = "contourpy-1.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8b7fc0cd78ba2f4695fd0a6ad81a19e7e3ab825c31b577f384aa9d7817dc3bef"},
{file = "contourpy-1.3.2-cp313-cp313-win32.whl", hash = "sha256:15ce6ab60957ca74cff444fe66d9045c1fd3e92c8936894ebd1f3eef2fff075f"},
{file = "contourpy-1.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e1578f7eafce927b168752ed7e22646dad6cd9bca673c60bff55889fa236ebf9"},
{file = "contourpy-1.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0475b1f6604896bc7c53bb070e355e9321e1bc0d381735421a2d2068ec56531f"},
{file = "contourpy-1.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c85bb486e9be652314bb5b9e2e3b0d1b2e643d5eec4992c0fbe8ac71775da739"},
{file = "contourpy-1.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:745b57db7758f3ffc05a10254edd3182a2a83402a89c00957a8e8a22f5582823"},
{file = "contourpy-1.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:970e9173dbd7eba9b4e01aab19215a48ee5dd3f43cef736eebde064a171f89a5"},
{file = "contourpy-1.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6c4639a9c22230276b7bffb6a850dfc8258a2521305e1faefe804d006b2e532"},
{file = "contourpy-1.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc829960f34ba36aad4302e78eabf3ef16a3a100863f0d4eeddf30e8a485a03b"},
{file = "contourpy-1.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d32530b534e986374fc19eaa77fcb87e8a99e5431499949b828312bdcd20ac52"},
{file = "contourpy-1.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e298e7e70cf4eb179cc1077be1c725b5fd131ebc81181bf0c03525c8abc297fd"},
{file = "contourpy-1.3.2-cp313-cp313t-win32.whl", hash = "sha256:d0e589ae0d55204991450bb5c23f571c64fe43adaa53f93fc902a84c96f52fe1"},
{file = "contourpy-1.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:78e9253c3de756b3f6a5174d024c4835acd59eb3f8e2ca13e775dbffe1558f69"},
{file = "contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c"},
{file = "contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16"},
{file = "contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad"},
{file = "contourpy-1.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f5964cdad279256c084b69c3f412b7801e15356b16efa9d78aa974041903da0"},
{file = "contourpy-1.3.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b65a95d642d4efa8f64ba12558fcb83407e58a2dfba9d796d77b63ccfcaff5"},
{file = "contourpy-1.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8c5acb8dddb0752bf252e01a3035b21443158910ac16a3b0d20e7fed7d534ce5"},
{file = "contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54"},
]
[package.dependencies]
numpy = ">=1.23"
[package.extras]
bokeh = ["bokeh", "selenium"]
docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"]
mypy = ["bokeh", "contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.15.0)", "types-Pillow"]
test = ["Pillow", "contourpy[test-no-images]", "matplotlib"]
test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"]
[[package]]
name = "contourpy"
version = "1.3.3"
description = "Python library for calculating contours of 2D quadrilateral grids"
optional = false
python-versions = ">=3.11"
groups = ["main"]
markers = "python_version >= \"3.11\""
files = [
{file = "contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1"},
{file = "contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381"},
{file = "contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7"},
{file = "contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1"},
{file = "contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a"},
{file = "contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db"},
{file = "contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620"},
{file = "contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f"},
{file = "contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff"},
{file = "contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42"},
{file = "contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470"},
{file = "contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb"},
{file = "contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6"},
{file = "contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7"},
{file = "contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8"},
{file = "contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea"},
{file = "contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1"},
{file = "contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7"},
{file = "contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411"},
{file = "contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69"},
{file = "contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b"},
{file = "contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc"},
{file = "contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5"},
{file = "contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1"},
{file = "contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286"},
{file = "contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5"},
{file = "contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67"},
{file = "contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9"},
{file = "contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659"},
{file = "contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7"},
{file = "contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d"},
{file = "contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263"},
{file = "contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9"},
{file = "contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d"},
{file = "contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216"},
{file = "contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae"},
{file = "contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20"},
{file = "contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99"},
{file = "contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b"},
{file = "contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a"},
{file = "contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e"},
{file = "contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3"},
{file = "contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8"},
{file = "contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301"},
{file = "contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a"},
{file = "contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77"},
{file = "contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5"},
{file = "contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4"},
{file = "contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36"},
{file = "contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3"},
{file = "contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b"},
{file = "contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36"},
{file = "contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d"},
{file = "contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd"},
{file = "contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339"},
{file = "contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772"},
{file = "contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77"},
{file = "contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13"},
{file = "contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe"},
{file = "contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f"},
{file = "contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0"},
{file = "contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4"},
{file = "contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f"},
{file = "contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae"},
{file = "contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc"},
{file = "contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b"},
{file = "contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497"},
{file = "contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8"},
{file = "contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e"},
{file = "contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989"},
{file = "contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77"},
{file = "contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880"},
]
[package.dependencies]
numpy = ">=1.25"
[package.extras]
bokeh = ["bokeh", "selenium"]
docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"]
mypy = ["bokeh", "contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.17.0)", "types-Pillow"]
test = ["Pillow", "contourpy[test-no-images]", "matplotlib"]
test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"]
[[package]]
name = "cycler"
version = "0.12.1"
description = "Composable style cycles"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"},
{file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"},
]
[package.extras]
docs = ["ipython", "matplotlib", "numpydoc", "sphinx"]
tests = ["pytest", "pytest-cov", "pytest-xdist"]
[[package]]
name = "fonttools"
version = "4.61.1"
description = "Tools to manipulate font files"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "fonttools-4.61.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c7db70d57e5e1089a274cbb2b1fd635c9a24de809a231b154965d415d6c6d24"},
{file = "fonttools-4.61.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5fe9fd43882620017add5eabb781ebfbc6998ee49b35bd7f8f79af1f9f99a958"},
{file = "fonttools-4.61.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8db08051fc9e7d8bc622f2112511b8107d8f27cd89e2f64ec45e9825e8288da"},
{file = "fonttools-4.61.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a76d4cb80f41ba94a6691264be76435e5f72f2cb3cab0b092a6212855f71c2f6"},
{file = "fonttools-4.61.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a13fc8aeb24bad755eea8f7f9d409438eb94e82cf86b08fe77a03fbc8f6a96b1"},
{file = "fonttools-4.61.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b846a1fcf8beadeb9ea4f44ec5bdde393e2f1569e17d700bfc49cd69bde75881"},
{file = "fonttools-4.61.1-cp310-cp310-win32.whl", hash = "sha256:78a7d3ab09dc47ac1a363a493e6112d8cabed7ba7caad5f54dbe2f08676d1b47"},
{file = "fonttools-4.61.1-cp310-cp310-win_amd64.whl", hash = "sha256:eff1ac3cc66c2ac7cda1e64b4e2f3ffef474b7335f92fc3833fc632d595fcee6"},
{file = "fonttools-4.61.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c6604b735bb12fef8e0efd5578c9fb5d3d8532d5001ea13a19cddf295673ee09"},
{file = "fonttools-4.61.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5ce02f38a754f207f2f06557523cd39a06438ba3aafc0639c477ac409fc64e37"},
{file = "fonttools-4.61.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77efb033d8d7ff233385f30c62c7c79271c8885d5c9657d967ede124671bbdfb"},
{file = "fonttools-4.61.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:75c1a6dfac6abd407634420c93864a1e274ebc1c7531346d9254c0d8f6ca00f9"},
{file = "fonttools-4.61.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0de30bfe7745c0d1ffa2b0b7048fb7123ad0d71107e10ee090fa0b16b9452e87"},
{file = "fonttools-4.61.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:58b0ee0ab5b1fc9921eccfe11d1435added19d6494dde14e323f25ad2bc30c56"},
{file = "fonttools-4.61.1-cp311-cp311-win32.whl", hash = "sha256:f79b168428351d11e10c5aeb61a74e1851ec221081299f4cf56036a95431c43a"},
{file = "fonttools-4.61.1-cp311-cp311-win_amd64.whl", hash = "sha256:fe2efccb324948a11dd09d22136fe2ac8a97d6c1347cf0b58a911dcd529f66b7"},
{file = "fonttools-4.61.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f3cb4a569029b9f291f88aafc927dd53683757e640081ca8c412781ea144565e"},
{file = "fonttools-4.61.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41a7170d042e8c0024703ed13b71893519a1a6d6e18e933e3ec7507a2c26a4b2"},
{file = "fonttools-4.61.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10d88e55330e092940584774ee5e8a6971b01fc2f4d3466a1d6c158230880796"},
{file = "fonttools-4.61.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15acc09befd16a0fb8a8f62bc147e1a82817542d72184acca9ce6e0aeda9fa6d"},
{file = "fonttools-4.61.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e6bcdf33aec38d16508ce61fd81838f24c83c90a1d1b8c68982857038673d6b8"},
{file = "fonttools-4.61.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5fade934607a523614726119164ff621e8c30e8fa1ffffbbd358662056ba69f0"},
{file = "fonttools-4.61.1-cp312-cp312-win32.whl", hash = "sha256:75da8f28eff26defba42c52986de97b22106cb8f26515b7c22443ebc9c2d3261"},
{file = "fonttools-4.61.1-cp312-cp312-win_amd64.whl", hash = "sha256:497c31ce314219888c0e2fce5ad9178ca83fe5230b01a5006726cdf3ac9f24d9"},
{file = "fonttools-4.61.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c56c488ab471628ff3bfa80964372fc13504ece601e0d97a78ee74126b2045c"},
{file = "fonttools-4.61.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dc492779501fa723b04d0ab1f5be046797fee17d27700476edc7ee9ae535a61e"},
{file = "fonttools-4.61.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:64102ca87e84261419c3747a0d20f396eb024bdbeb04c2bfb37e2891f5fadcb5"},
{file = "fonttools-4.61.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c1b526c8d3f615a7b1867f38a9410849c8f4aef078535742198e942fba0e9bd"},
{file = "fonttools-4.61.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:41ed4b5ec103bd306bb68f81dc166e77409e5209443e5773cb4ed837bcc9b0d3"},
{file = "fonttools-4.61.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b501c862d4901792adaec7c25b1ecc749e2662543f68bb194c42ba18d6eec98d"},
{file = "fonttools-4.61.1-cp313-cp313-win32.whl", hash = "sha256:4d7092bb38c53bbc78e9255a59158b150bcdc115a1e3b3ce0b5f267dc35dd63c"},
{file = "fonttools-4.61.1-cp313-cp313-win_amd64.whl", hash = "sha256:21e7c8d76f62ab13c9472ccf74515ca5b9a761d1bde3265152a6dc58700d895b"},
{file = "fonttools-4.61.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fff4f534200a04b4a36e7ae3cb74493afe807b517a09e99cb4faa89a34ed6ecd"},
{file = "fonttools-4.61.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d9203500f7c63545b4ce3799319fe4d9feb1a1b89b28d3cb5abd11b9dd64147e"},
{file = "fonttools-4.61.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa646ecec9528bef693415c79a86e733c70a4965dd938e9a226b0fc64c9d2e6c"},
{file = "fonttools-4.61.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f35ad7805edba3aac1a3710d104592df59f4b957e30108ae0ba6c10b11dd75"},
{file = "fonttools-4.61.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b931ae8f62db78861b0ff1ac017851764602288575d65b8e8ff1963fed419063"},
{file = "fonttools-4.61.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b148b56f5de675ee16d45e769e69f87623a4944f7443850bf9a9376e628a89d2"},
{file = "fonttools-4.61.1-cp314-cp314-win32.whl", hash = "sha256:9b666a475a65f4e839d3d10473fad6d47e0a9db14a2f4a224029c5bfde58ad2c"},
{file = "fonttools-4.61.1-cp314-cp314-win_amd64.whl", hash = "sha256:4f5686e1fe5fce75d82d93c47a438a25bf0d1319d2843a926f741140b2b16e0c"},
{file = "fonttools-4.61.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:e76ce097e3c57c4bcb67c5aa24a0ecdbd9f74ea9219997a707a4061fbe2707aa"},
{file = "fonttools-4.61.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9cfef3ab326780c04d6646f68d4b4742aae222e8b8ea1d627c74e38afcbc9d91"},
{file = "fonttools-4.61.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a75c301f96db737e1c5ed5fd7d77d9c34466de16095a266509e13da09751bd19"},
{file = "fonttools-4.61.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:91669ccac46bbc1d09e9273546181919064e8df73488ea087dcac3e2968df9ba"},
{file = "fonttools-4.61.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c33ab3ca9d3ccd581d58e989d67554e42d8d4ded94ab3ade3508455fe70e65f7"},
{file = "fonttools-4.61.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:664c5a68ec406f6b1547946683008576ef8b38275608e1cee6c061828171c118"},
{file = "fonttools-4.61.1-cp314-cp314t-win32.whl", hash = "sha256:aed04cabe26f30c1647ef0e8fbb207516fd40fe9472e9439695f5c6998e60ac5"},
{file = "fonttools-4.61.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2180f14c141d2f0f3da43f3a81bc8aa4684860f6b0e6f9e165a4831f24e6a23b"},
{file = "fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371"},
{file = "fonttools-4.61.1.tar.gz", hash = "sha256:6675329885c44657f826ef01d9e4fb33b9158e9d93c537d84ad8399539bc6f69"},
]
[package.extras]
all = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\"", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.45.0)", "unicodedata2 (>=17.0.0) ; python_version <= \"3.14\"", "xattr ; sys_platform == \"darwin\"", "zopfli (>=0.1.4)"]
graphite = ["lz4 (>=1.7.4.2)"]
interpolatable = ["munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\""]
lxml = ["lxml (>=4.0)"]
pathops = ["skia-pathops (>=0.5.0)"]
plot = ["matplotlib"]
repacker = ["uharfbuzz (>=0.45.0)"]
symfont = ["sympy"]
type1 = ["xattr ; sys_platform == \"darwin\""]
unicode = ["unicodedata2 (>=17.0.0) ; python_version <= \"3.14\""]
woff = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "zopfli (>=0.1.4)"]
[[package]]
name = "kiwisolver"
version = "1.4.9"
description = "A fast implementation of the Cassowary constraint solver"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b"},
{file = "kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f"},
{file = "kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf"},
{file = "kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9"},
{file = "kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415"},
{file = "kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b"},
{file = "kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154"},
{file = "kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48"},
{file = "kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220"},
{file = "kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586"},
{file = "kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634"},
{file = "kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611"},
{file = "kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536"},
{file = "kiwisolver-1.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eb14a5da6dc7642b0f3a18f13654847cd8b7a2550e2645a5bda677862b03ba16"},
{file = "kiwisolver-1.4.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39a219e1c81ae3b103643d2aedb90f1ef22650deb266ff12a19e7773f3e5f089"},
{file = "kiwisolver-1.4.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2405a7d98604b87f3fc28b1716783534b1b4b8510d8142adca34ee0bc3c87543"},
{file = "kiwisolver-1.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dc1ae486f9abcef254b5618dfb4113dd49f94c68e3e027d03cf0143f3f772b61"},
{file = "kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a1f570ce4d62d718dce3f179ee78dac3b545ac16c0c04bb363b7607a949c0d1"},
{file = "kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb27e7b78d716c591e88e0a09a2139c6577865d7f2e152488c2cc6257f460872"},
{file = "kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:15163165efc2f627eb9687ea5f3a28137217d217ac4024893d753f46bce9de26"},
{file = "kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bdee92c56a71d2b24c33a7d4c2856bd6419d017e08caa7802d2963870e315028"},
{file = "kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:412f287c55a6f54b0650bd9b6dce5aceddb95864a1a90c87af16979d37c89771"},
{file = "kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2c93f00dcba2eea70af2be5f11a830a742fe6b579a1d4e00f47760ef13be247a"},
{file = "kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f117e1a089d9411663a3207ba874f31be9ac8eaa5b533787024dc07aeb74f464"},
{file = "kiwisolver-1.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:be6a04e6c79819c9a8c2373317d19a96048e5a3f90bec587787e86a1153883c2"},
{file = "kiwisolver-1.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:0ae37737256ba2de764ddc12aed4956460277f00c4996d51a197e72f62f5eec7"},
{file = "kiwisolver-1.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ac5a486ac389dddcc5bef4f365b6ae3ffff2c433324fb38dd35e3fab7c957999"},
{file = "kiwisolver-1.4.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2ba92255faa7309d06fe44c3a4a97efe1c8d640c2a79a5ef728b685762a6fd2"},
{file = "kiwisolver-1.4.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a2899935e724dd1074cb568ce7ac0dce28b2cd6ab539c8e001a8578eb106d14"},
{file = "kiwisolver-1.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f6008a4919fdbc0b0097089f67a1eb55d950ed7e90ce2cc3e640abadd2757a04"},
{file = "kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67bb8b474b4181770f926f7b7d2f8c0248cbcb78b660fdd41a47054b28d2a752"},
{file = "kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2327a4a30d3ee07d2fbe2e7933e8a37c591663b96ce42a00bc67461a87d7df77"},
{file = "kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a08b491ec91b1d5053ac177afe5290adacf1f0f6307d771ccac5de30592d198"},
{file = "kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8fc5c867c22b828001b6a38d2eaeb88160bf5783c6cb4a5e440efc981ce286d"},
{file = "kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3b3115b2581ea35bb6d1f24a4c90af37e5d9b49dcff267eeed14c3893c5b86ab"},
{file = "kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858e4c22fb075920b96a291928cb7dea5644e94c0ee4fcd5af7e865655e4ccf2"},
{file = "kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed0fecd28cc62c54b262e3736f8bb2512d8dcfdc2bcf08be5f47f96bf405b145"},
{file = "kiwisolver-1.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:f68208a520c3d86ea51acf688a3e3002615a7f0238002cccc17affecc86a8a54"},
{file = "kiwisolver-1.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:2c1a4f57df73965f3f14df20b80ee29e6a7930a57d2d9e8491a25f676e197c60"},
{file = "kiwisolver-1.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5d0432ccf1c7ab14f9949eec60c5d1f924f17c037e9f8b33352fa05799359b8"},
{file = "kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efb3a45b35622bb6c16dbfab491a8f5a391fe0e9d45ef32f4df85658232ca0e2"},
{file = "kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a12cf6398e8a0a001a059747a1cbf24705e18fe413bc22de7b3d15c67cffe3f"},
{file = "kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098"},
{file = "kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5656aa670507437af0207645273ccdfee4f14bacd7f7c67a4306d0dcaeaf6eed"},
{file = "kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bfc08add558155345129c7803b3671cf195e6a56e7a12f3dde7c57d9b417f525"},
{file = "kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:40092754720b174e6ccf9e845d0d8c7d8e12c3d71e7fc35f55f3813e96376f78"},
{file = "kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:497d05f29a1300d14e02e6441cf0f5ee81c1ff5a304b0d9fb77423974684e08b"},
{file = "kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdd1a81a1860476eb41ac4bc1e07b3f07259e6d55bbf739b79c8aaedcf512799"},
{file = "kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e6b93f13371d341afee3be9f7c5964e3fe61d5fa30f6a30eb49856935dfe4fc3"},
{file = "kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d75aa530ccfaa593da12834b86a0724f58bff12706659baa9227c2ccaa06264c"},
{file = "kiwisolver-1.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:dd0a578400839256df88c16abddf9ba14813ec5f21362e1fe65022e00c883d4d"},
{file = "kiwisolver-1.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:d4188e73af84ca82468f09cadc5ac4db578109e52acb4518d8154698d3a87ca2"},
{file = "kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5a0f2724dfd4e3b3ac5a82436a8e6fd16baa7d507117e4279b660fe8ca38a3a1"},
{file = "kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b11d6a633e4ed84fc0ddafd4ebfd8ea49b3f25082c04ad12b8315c11d504dc1"},
{file = "kiwisolver-1.4.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61874cdb0a36016354853593cffc38e56fc9ca5aa97d2c05d3dcf6922cd55a11"},
{file = "kiwisolver-1.4.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:60c439763a969a6af93b4881db0eed8fadf93ee98e18cbc35bc8da868d0c4f0c"},
{file = "kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92a2f997387a1b79a75e7803aa7ded2cfbe2823852ccf1ba3bcf613b62ae3197"},
{file = "kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31d512c812daea6d8b3be3b2bfcbeb091dbb09177706569bcfc6240dcf8b41c"},
{file = "kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:52a15b0f35dad39862d376df10c5230155243a2c1a436e39eb55623ccbd68185"},
{file = "kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a30fd6fdef1430fd9e1ba7b3398b5ee4e2887783917a687d86ba69985fb08748"},
{file = "kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cc9617b46837c6468197b5945e196ee9ca43057bb7d9d1ae688101e4e1dddf64"},
{file = "kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:0ab74e19f6a2b027ea4f845a78827969af45ce790e6cb3e1ebab71bdf9f215ff"},
{file = "kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dba5ee5d3981160c28d5490f0d1b7ed730c22470ff7f6cc26cfcfaacb9896a07"},
{file = "kiwisolver-1.4.9-cp313-cp313t-win_arm64.whl", hash = "sha256:0749fd8f4218ad2e851e11cc4dc05c7cbc0cbc4267bdfdb31782e65aace4ee9c"},
{file = "kiwisolver-1.4.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9928fe1eb816d11ae170885a74d074f57af3a0d65777ca47e9aeb854a1fba386"},
{file = "kiwisolver-1.4.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d0005b053977e7b43388ddec89fa567f43d4f6d5c2c0affe57de5ebf290dc552"},
{file = "kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2635d352d67458b66fd0667c14cb1d4145e9560d503219034a18a87e971ce4f3"},
{file = "kiwisolver-1.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:767c23ad1c58c9e827b649a9ab7809fd5fd9db266a9cf02b0e926ddc2c680d58"},
{file = "kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72d0eb9fba308b8311685c2268cf7d0a0639a6cd027d8128659f72bdd8a024b4"},
{file = "kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f68e4f3eeca8fb22cc3d731f9715a13b652795ef657a13df1ad0c7dc0e9731df"},
{file = "kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d84cd4061ae292d8ac367b2c3fa3aad11cb8625a95d135fe93f286f914f3f5a6"},
{file = "kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a60ea74330b91bd22a29638940d115df9dc00af5035a9a2a6ad9399ffb4ceca5"},
{file = "kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ce6a3a4e106cf35c2d9c4fa17c05ce0b180db622736845d4315519397a77beaf"},
{file = "kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:77937e5e2a38a7b48eef0585114fe7930346993a88060d0bf886086d2aa49ef5"},
{file = "kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:24c175051354f4a28c5d6a31c93906dc653e2bf234e8a4bbfb964892078898ce"},
{file = "kiwisolver-1.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:0763515d4df10edf6d06a3c19734e2566368980d21ebec439f33f9eb936c07b7"},
{file = "kiwisolver-1.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:0e4e2bf29574a6a7b7f6cb5fa69293b9f96c928949ac4a53ba3f525dffb87f9c"},
{file = "kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d976bbb382b202f71c67f77b0ac11244021cfa3f7dfd9e562eefcea2df711548"},
{file = "kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2489e4e5d7ef9a1c300a5e0196e43d9c739f066ef23270607d45aba368b91f2d"},
{file = "kiwisolver-1.4.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e2ea9f7ab7fbf18fffb1b5434ce7c69a07582f7acc7717720f1d69f3e806f90c"},
{file = "kiwisolver-1.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b34e51affded8faee0dfdb705416153819d8ea9250bbbf7ea1b249bdeb5f1122"},
{file = "kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8aacd3d4b33b772542b2e01beb50187536967b514b00003bdda7589722d2a64"},
{file = "kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7cf974dd4e35fa315563ac99d6287a1024e4dc2077b8a7d7cd3d2fb65d283134"},
{file = "kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85bd218b5ecfbee8c8a82e121802dcb519a86044c9c3b2e4aef02fa05c6da370"},
{file = "kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0856e241c2d3df4efef7c04a1e46b1936b6120c9bcf36dd216e3acd84bc4fb21"},
{file = "kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9af39d6551f97d31a4deebeac6f45b156f9755ddc59c07b402c148f5dbb6482a"},
{file = "kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:bb4ae2b57fc1d8cbd1cf7b1d9913803681ffa903e7488012be5b76dedf49297f"},
{file = "kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aedff62918805fb62d43a4aa2ecd4482c380dc76cd31bd7c8878588a61bd0369"},
{file = "kiwisolver-1.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:1fa333e8b2ce4d9660f2cda9c0e1b6bafcfb2457a9d259faa82289e73ec24891"},
{file = "kiwisolver-1.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:4a48a2ce79d65d363597ef7b567ce3d14d68783d2b2263d98db3d9477805ba32"},
{file = "kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527"},
{file = "kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771"},
{file = "kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e"},
{file = "kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9"},
{file = "kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb"},
{file = "kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:720e05574713db64c356e86732c0f3c5252818d05f9df320f0ad8380641acea5"},
{file = "kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:17680d737d5335b552994a2008fab4c851bcd7de33094a82067ef3a576ff02fa"},
{file = "kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85b5352f94e490c028926ea567fc569c52ec79ce131dadb968d3853e809518c2"},
{file = "kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:464415881e4801295659462c49461a24fb107c140de781d55518c4b80cb6790f"},
{file = "kiwisolver-1.4.9-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fb940820c63a9590d31d88b815e7a3aa5915cad3ce735ab45f0c730b39547de1"},
{file = "kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d"},
]
[[package]]
name = "matplotlib"
version = "3.10.8"
description = "Python plotting package"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7"},
{file = "matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656"},
{file = "matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df"},
{file = "matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17"},
{file = "matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933"},
{file = "matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a"},
{file = "matplotlib-3.10.8-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6be43b667360fef5c754dda5d25a32e6307a03c204f3c0fc5468b78fa87b4160"},
{file = "matplotlib-3.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2b336e2d91a3d7006864e0990c83b216fcdca64b5a6484912902cef87313d78"},
{file = "matplotlib-3.10.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efb30e3baaea72ce5928e32bab719ab4770099079d66726a62b11b1ef7273be4"},
{file = "matplotlib-3.10.8-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d56a1efd5bfd61486c8bc968fa18734464556f0fb8e51690f4ac25d85cbbbbc2"},
{file = "matplotlib-3.10.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238b7ce5717600615c895050239ec955d91f321c209dd110db988500558e70d6"},
{file = "matplotlib-3.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:18821ace09c763ec93aef5eeff087ee493a24051936d7b9ebcad9662f66501f9"},
{file = "matplotlib-3.10.8-cp311-cp311-win_arm64.whl", hash = "sha256:bab485bcf8b1c7d2060b4fcb6fc368a9e6f4cd754c9c2fea281f4be21df394a2"},
{file = "matplotlib-3.10.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:64fcc24778ca0404ce0cb7b6b77ae1f4c7231cdd60e6778f999ee05cbd581b9a"},
{file = "matplotlib-3.10.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9a5ca4ac220a0cdd1ba6bcba3608547117d30468fefce49bb26f55c1a3d5c58"},
{file = "matplotlib-3.10.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ab4aabc72de4ff77b3ec33a6d78a68227bf1123465887f9905ba79184a1cc04"},
{file = "matplotlib-3.10.8-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24d50994d8c5816ddc35411e50a86ab05f575e2530c02752e02538122613371f"},
{file = "matplotlib-3.10.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99eefd13c0dc3b3c1b4d561c1169e65fe47aab7b8158754d7c084088e2329466"},
{file = "matplotlib-3.10.8-cp312-cp312-win_amd64.whl", hash = "sha256:dd80ecb295460a5d9d260df63c43f4afbdd832d725a531f008dad1664f458adf"},
{file = "matplotlib-3.10.8-cp312-cp312-win_arm64.whl", hash = "sha256:3c624e43ed56313651bc18a47f838b60d7b8032ed348911c54906b130b20071b"},
{file = "matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3f2e409836d7f5ac2f1c013110a4d50b9f7edc26328c108915f9075d7d7a91b6"},
{file = "matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56271f3dac49a88d7fca5060f004d9d22b865f743a12a23b1e937a0be4818ee1"},
{file = "matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0a7f52498f72f13d4a25ea70f35f4cb60642b466cbb0a9be951b5bc3f45a486"},
{file = "matplotlib-3.10.8-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:646d95230efb9ca614a7a594d4fcacde0ac61d25e37dd51710b36477594963ce"},
{file = "matplotlib-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f89c151aab2e2e23cb3fe0acad1e8b82841fd265379c4cecd0f3fcb34c15e0f6"},
{file = "matplotlib-3.10.8-cp313-cp313-win_amd64.whl", hash = "sha256:e8ea3e2d4066083e264e75c829078f9e149fa119d27e19acd503de65e0b13149"},
{file = "matplotlib-3.10.8-cp313-cp313-win_arm64.whl", hash = "sha256:c108a1d6fa78a50646029cb6d49808ff0fc1330fda87fa6f6250c6b5369b6645"},
{file = "matplotlib-3.10.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ad3d9833a64cf48cc4300f2b406c3d0f4f4724a91c0bd5640678a6ba7c102077"},
{file = "matplotlib-3.10.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:eb3823f11823deade26ce3b9f40dcb4a213da7a670013929f31d5f5ed1055b22"},
{file = "matplotlib-3.10.8-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d9050fee89a89ed57b4fb2c1bfac9a3d0c57a0d55aed95949eedbc42070fea39"},
{file = "matplotlib-3.10.8-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b44d07310e404ba95f8c25aa5536f154c0a8ec473303535949e52eb71d0a1565"},
{file = "matplotlib-3.10.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0a33deb84c15ede243aead39f77e990469fff93ad1521163305095b77b72ce4a"},
{file = "matplotlib-3.10.8-cp313-cp313t-win_amd64.whl", hash = "sha256:3a48a78d2786784cc2413e57397981fb45c79e968d99656706018d6e62e57958"},
{file = "matplotlib-3.10.8-cp313-cp313t-win_arm64.whl", hash = "sha256:15d30132718972c2c074cd14638c7f4592bd98719e2308bccea40e0538bc0cb5"},
{file = "matplotlib-3.10.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b53285e65d4fa4c86399979e956235deb900be5baa7fc1218ea67fbfaeaadd6f"},
{file = "matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32f8dce744be5569bebe789e46727946041199030db8aeb2954d26013a0eb26b"},
{file = "matplotlib-3.10.8-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf267add95b1c88300d96ca837833d4112756045364f5c734a2276038dae27d"},
{file = "matplotlib-3.10.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2cf5bd12cecf46908f286d7838b2abc6c91cda506c0445b8223a7c19a00df008"},
{file = "matplotlib-3.10.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:41703cc95688f2516b480f7f339d8851a6035f18e100ee6a32bc0b8536a12a9c"},
{file = "matplotlib-3.10.8-cp314-cp314-win_amd64.whl", hash = "sha256:83d282364ea9f3e52363da262ce32a09dfe241e4080dcedda3c0db059d3c1f11"},
{file = "matplotlib-3.10.8-cp314-cp314-win_arm64.whl", hash = "sha256:2c1998e92cd5999e295a731bcb2911c75f597d937341f3030cc24ef2733d78a8"},
{file = "matplotlib-3.10.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b5a2b97dbdc7d4f353ebf343744f1d1f1cca8aa8bfddb4262fcf4306c3761d50"},
{file = "matplotlib-3.10.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f5c3e4da343bba819f0234186b9004faba952cc420fbc522dc4e103c1985908"},
{file = "matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f62550b9a30afde8c1c3ae450e5eb547d579dd69b25c2fc7a1c67f934c1717a"},
{file = "matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:495672de149445ec1b772ff2c9ede9b769e3cb4f0d0aa7fa730d7f59e2d4e1c1"},
{file = "matplotlib-3.10.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:595ba4d8fe983b88f0eec8c26a241e16d6376fe1979086232f481f8f3f67494c"},
{file = "matplotlib-3.10.8-cp314-cp314t-win_amd64.whl", hash = "sha256:25d380fe8b1dc32cf8f0b1b448470a77afb195438bafdf1d858bfb876f3edf7b"},
{file = "matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f"},
{file = "matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8"},
{file = "matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7"},
{file = "matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3"},
{file = "matplotlib-3.10.8-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6da7c2ce169267d0d066adcf63758f0604aa6c3eebf67458930f9d9b79ad1db1"},
{file = "matplotlib-3.10.8-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9153c3292705be9f9c64498a8872118540c3f4123d1a1c840172edf262c8be4a"},
{file = "matplotlib-3.10.8-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ae029229a57cd1e8fe542485f27e7ca7b23aa9e8944ddb4985d0bc444f1eca2"},
{file = "matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3"},
]
[package.dependencies]
contourpy = ">=1.0.1"
cycler = ">=0.10"
fonttools = ">=4.22.0"
kiwisolver = ">=1.3.1"
numpy = ">=1.23"
packaging = ">=20.0"
pillow = ">=8"
pyparsing = ">=3"
python-dateutil = ">=2.7"
[package.extras]
dev = ["meson-python (>=0.13.1,<0.17.0)", "pybind11 (>=2.13.2,!=2.13.3)", "setuptools (>=64)", "setuptools_scm (>=7)"]
[[package]]
name = "numpy"
version = "2.2.6"
description = "Fundamental package for array computing in Python"
optional = false
python-versions = ">=3.10"
groups = ["main"]
markers = "python_version == \"3.10\""
files = [
{file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"},
{file = "numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90"},
{file = "numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163"},
{file = "numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf"},
{file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83"},
{file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915"},
{file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680"},
{file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289"},
{file = "numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d"},
{file = "numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3"},
{file = "numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae"},
{file = "numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a"},
{file = "numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42"},
{file = "numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491"},
{file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a"},
{file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf"},
{file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1"},
{file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab"},
{file = "numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47"},
{file = "numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303"},
{file = "numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff"},
{file = "numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c"},
{file = "numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3"},
{file = "numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282"},
{file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87"},
{file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249"},
{file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49"},
{file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de"},
{file = "numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4"},
{file = "numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2"},
{file = "numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84"},
{file = "numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b"},
{file = "numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d"},
{file = "numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566"},
{file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f"},
{file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f"},
{file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868"},
{file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d"},
{file = "numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd"},
{file = "numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c"},
{file = "numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6"},
{file = "numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda"},
{file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40"},
{file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8"},
{file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f"},
{file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa"},
{file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571"},
{file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1"},
{file = "numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff"},
{file = "numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06"},
{file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d"},
{file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db"},
{file = "numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543"},
{file = "numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00"},
{file = "numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd"},
]
[[package]]
name = "numpy"
version = "2.4.2"
description = "Fundamental package for array computing in Python"
optional = false
python-versions = ">=3.11"
groups = ["main"]
markers = "python_version >= \"3.11\""
files = [
{file = "numpy-2.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7e88598032542bd49af7c4747541422884219056c268823ef6e5e89851c8825"},
{file = "numpy-2.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7edc794af8b36ca37ef5fcb5e0d128c7e0595c7b96a2318d1badb6fcd8ee86b1"},
{file = "numpy-2.4.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:6e9f61981ace1360e42737e2bae58b27bf28a1b27e781721047d84bd754d32e7"},
{file = "numpy-2.4.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cb7bbb88aa74908950d979eeaa24dbdf1a865e3c7e45ff0121d8f70387b55f73"},
{file = "numpy-2.4.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f069069931240b3fc703f1e23df63443dbd6390614c8c44a87d96cd0ec81eb1"},
{file = "numpy-2.4.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c02ef4401a506fb60b411467ad501e1429a3487abca4664871d9ae0b46c8ba32"},
{file = "numpy-2.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2653de5c24910e49c2b106499803124dde62a5a1fe0eedeaecf4309a5f639390"},
{file = "numpy-2.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1ae241bbfc6ae276f94a170b14785e561cb5e7f626b6688cf076af4110887413"},
{file = "numpy-2.4.2-cp311-cp311-win32.whl", hash = "sha256:df1b10187212b198dd45fa943d8985a3c8cf854aed4923796e0e019e113a1bda"},
{file = "numpy-2.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:b9c618d56a29c9cb1c4da979e9899be7578d2e0b3c24d52079c166324c9e8695"},
{file = "numpy-2.4.2-cp311-cp311-win_arm64.whl", hash = "sha256:47c5a6ed21d9452b10227e5e8a0e1c22979811cad7dcc19d8e3e2fb8fa03f1a3"},
{file = "numpy-2.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:21982668592194c609de53ba4933a7471880ccbaadcc52352694a59ecc860b3a"},
{file = "numpy-2.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40397bda92382fcec844066efb11f13e1c9a3e2a8e8f318fb72ed8b6db9f60f1"},
{file = "numpy-2.4.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b3a24467af63c67829bfaa61eecf18d5432d4f11992688537be59ecd6ad32f5e"},
{file = "numpy-2.4.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:805cc8de9fd6e7a22da5aed858e0ab16be5a4db6c873dde1d7451c541553aa27"},
{file = "numpy-2.4.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d82351358ffbcdcd7b686b90742a9b86632d6c1c051016484fa0b326a0a1548"},
{file = "numpy-2.4.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e35d3e0144137d9fdae62912e869136164534d64a169f86438bc9561b6ad49f"},
{file = "numpy-2.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adb6ed2ad29b9e15321d167d152ee909ec73395901b70936f029c3bc6d7f4460"},
{file = "numpy-2.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8906e71fd8afcb76580404e2a950caef2685df3d2a57fe82a86ac8d33cc007ba"},
{file = "numpy-2.4.2-cp312-cp312-win32.whl", hash = "sha256:ec055f6dae239a6299cace477b479cca2fc125c5675482daf1dd886933a1076f"},
{file = "numpy-2.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:209fae046e62d0ce6435fcfe3b1a10537e858249b3d9b05829e2a05218296a85"},
{file = "numpy-2.4.2-cp312-cp312-win_arm64.whl", hash = "sha256:fbde1b0c6e81d56f5dccd95dd4a711d9b95df1ae4009a60887e56b27e8d903fa"},
{file = "numpy-2.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25f2059807faea4b077a2b6837391b5d830864b3543627f381821c646f31a63c"},
{file = "numpy-2.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bd3a7a9f5847d2fb8c2c6d1c862fa109c31a9abeca1a3c2bd5a64572955b2979"},
{file = "numpy-2.4.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8e4549f8a3c6d13d55041925e912bfd834285ef1dd64d6bc7d542583355e2e98"},
{file = "numpy-2.4.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:aea4f66ff44dfddf8c2cffd66ba6538c5ec67d389285292fe428cb2c738c8aef"},
{file = "numpy-2.4.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3cd545784805de05aafe1dde61752ea49a359ccba9760c1e5d1c88a93bbf2b7"},
{file = "numpy-2.4.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0d9b7c93578baafcbc5f0b83eaf17b79d345c6f36917ba0c67f45226911d499"},
{file = "numpy-2.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f74f0f7779cc7ae07d1810aab8ac6b1464c3eafb9e283a40da7309d5e6e48fbb"},
{file = "numpy-2.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7ac672d699bf36275c035e16b65539931347d68b70667d28984c9fb34e07fa7"},
{file = "numpy-2.4.2-cp313-cp313-win32.whl", hash = "sha256:8e9afaeb0beff068b4d9cd20d322ba0ee1cecfb0b08db145e4ab4dd44a6b5110"},
{file = "numpy-2.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:7df2de1e4fba69a51c06c28f5a3de36731eb9639feb8e1cf7e4a7b0daf4cf622"},
{file = "numpy-2.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:0fece1d1f0a89c16b03442eae5c56dc0be0c7883b5d388e0c03f53019a4bfd71"},
{file = "numpy-2.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5633c0da313330fd20c484c78cdd3f9b175b55e1a766c4a174230c6b70ad8262"},
{file = "numpy-2.4.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d9f64d786b3b1dd742c946c42d15b07497ed14af1a1f3ce840cce27daa0ce913"},
{file = "numpy-2.4.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:b21041e8cb6a1eb5312dd1d2f80a94d91efffb7a06b70597d44f1bd2dfc315ab"},
{file = "numpy-2.4.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00ab83c56211a1d7c07c25e3217ea6695e50a3e2f255053686b081dc0b091a82"},
{file = "numpy-2.4.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fb882da679409066b4603579619341c6d6898fc83a8995199d5249f986e8e8f"},
{file = "numpy-2.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66cb9422236317f9d44b67b4d18f44efe6e9c7f8794ac0462978513359461554"},
{file = "numpy-2.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0f01dcf33e73d80bd8dc0f20a71303abbafa26a19e23f6b68d1aa9990af90257"},
{file = "numpy-2.4.2-cp313-cp313t-win32.whl", hash = "sha256:52b913ec40ff7ae845687b0b34d8d93b60cb66dcee06996dd5c99f2fc9328657"},
{file = "numpy-2.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:5eea80d908b2c1f91486eb95b3fb6fab187e569ec9752ab7d9333d2e66bf2d6b"},
{file = "numpy-2.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fd49860271d52127d61197bb50b64f58454e9f578cb4b2c001a6de8b1f50b0b1"},
{file = "numpy-2.4.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:444be170853f1f9d528428eceb55f12918e4fda5d8805480f36a002f1415e09b"},
{file = "numpy-2.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d1240d50adff70c2a88217698ca844723068533f3f5c5fa6ee2e3220e3bdb000"},
{file = "numpy-2.4.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:7cdde6de52fb6664b00b056341265441192d1291c130e99183ec0d4b110ff8b1"},
{file = "numpy-2.4.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:cda077c2e5b780200b6b3e09d0b42205a3d1c68f30c6dceb90401c13bff8fe74"},
{file = "numpy-2.4.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d30291931c915b2ab5717c2974bb95ee891a1cf22ebc16a8006bd59cd210d40a"},
{file = "numpy-2.4.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bba37bc29d4d85761deed3954a1bc62be7cf462b9510b51d367b769a8c8df325"},
{file = "numpy-2.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b2f0073ed0868db1dcd86e052d37279eef185b9c8db5bf61f30f46adac63c909"},
{file = "numpy-2.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7f54844851cdb630ceb623dcec4db3240d1ac13d4990532446761baede94996a"},
{file = "numpy-2.4.2-cp314-cp314-win32.whl", hash = "sha256:12e26134a0331d8dbd9351620f037ec470b7c75929cb8a1537f6bfe411152a1a"},
{file = "numpy-2.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:068cdb2d0d644cdb45670810894f6a0600797a69c05f1ac478e8d31670b8ee75"},
{file = "numpy-2.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:6ed0be1ee58eef41231a5c943d7d1375f093142702d5723ca2eb07db9b934b05"},
{file = "numpy-2.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:98f16a80e917003a12c0580f97b5f875853ebc33e2eaa4bccfc8201ac6869308"},
{file = "numpy-2.4.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:20abd069b9cda45874498b245c8015b18ace6de8546bf50dfa8cea1696ed06ef"},
{file = "numpy-2.4.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:e98c97502435b53741540a5717a6749ac2ada901056c7db951d33e11c885cc7d"},
{file = "numpy-2.4.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da6cad4e82cb893db4b69105c604d805e0c3ce11501a55b5e9f9083b47d2ffe8"},
{file = "numpy-2.4.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4424677ce4b47fe73c8b5556d876571f7c6945d264201180db2dc34f676ab5"},
{file = "numpy-2.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2b8f157c8a6f20eb657e240f8985cc135598b2b46985c5bccbde7616dc9c6b1e"},
{file = "numpy-2.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5daf6f3914a733336dab21a05cdec343144600e964d2fcdabaac0c0269874b2a"},
{file = "numpy-2.4.2-cp314-cp314t-win32.whl", hash = "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443"},
{file = "numpy-2.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236"},
{file = "numpy-2.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181"},
{file = "numpy-2.4.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:89f7268c009bc492f506abd6f5265defa7cb3f7487dc21d357c3d290add45082"},
{file = "numpy-2.4.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6dee3bb76aa4009d5a912180bf5b2de012532998d094acee25d9cb8dee3e44a"},
{file = "numpy-2.4.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:cd2bd2bbed13e213d6b55dc1d035a4f91748a7d3edc9480c13898b0353708920"},
{file = "numpy-2.4.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:cf28c0c1d4c4bf00f509fa7eb02c58d7caf221b50b467bcb0d9bbf1584d5c821"},
{file = "numpy-2.4.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e04ae107ac591763a47398bb45b568fc38f02dbc4aa44c063f67a131f99346cb"},
{file = "numpy-2.4.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:602f65afdef699cda27ec0b9224ae5dc43e328f4c24c689deaf77133dbee74d0"},
{file = "numpy-2.4.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be71bf1edb48ebbbf7f6337b5bfd2f895d1902f6335a5830b20141fc126ffba0"},
{file = "numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae"},
]
[[package]]
name = "packaging"
version = "26.0"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529"},
{file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"},
]
[[package]]
name = "pandas"
version = "2.3.3"
description = "Powerful data structures for data analysis, time series, and statistics"
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "python_version == \"3.10\""
files = [
{file = "pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c"},
{file = "pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a"},
{file = "pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1"},
{file = "pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838"},
{file = "pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250"},
{file = "pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4"},
{file = "pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826"},
{file = "pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523"},
{file = "pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45"},
{file = "pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66"},
{file = "pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b"},
{file = "pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791"},
{file = "pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151"},
{file = "pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c"},
{file = "pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53"},
{file = "pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35"},
{file = "pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908"},
{file = "pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89"},
{file = "pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98"},
{file = "pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084"},
{file = "pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b"},
{file = "pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713"},
{file = "pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8"},
{file = "pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d"},
{file = "pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac"},
{file = "pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c"},
{file = "pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493"},
{file = "pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee"},
{file = "pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5"},
{file = "pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21"},
{file = "pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78"},
{file = "pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110"},
{file = "pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86"},
{file = "pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc"},
{file = "pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0"},
{file = "pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593"},
{file = "pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c"},
{file = "pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b"},
{file = "pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6"},
{file = "pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3"},
{file = "pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5"},
{file = "pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec"},
{file = "pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7"},
{file = "pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450"},
{file = "pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5"},
{file = "pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788"},
{file = "pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87"},
{file = "pandas-2.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c503ba5216814e295f40711470446bc3fd00f0faea8a086cbc688808e26f92a2"},
{file = "pandas-2.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a637c5cdfa04b6d6e2ecedcb81fc52ffb0fd78ce2ebccc9ea964df9f658de8c8"},
{file = "pandas-2.3.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:854d00d556406bffe66a4c0802f334c9ad5a96b4f1f868adf036a21b11ef13ff"},
{file = "pandas-2.3.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf1f8a81d04ca90e32a0aceb819d34dbd378a98bf923b6398b9a3ec0bf44de29"},
{file = "pandas-2.3.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:23ebd657a4d38268c7dfbdf089fbc31ea709d82e4923c5ffd4fbd5747133ce73"},
{file = "pandas-2.3.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5554c929ccc317d41a5e3d1234f3be588248e61f08a74dd17c9eabb535777dc9"},
{file = "pandas-2.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:d3e28b3e83862ccf4d85ff19cf8c20b2ae7e503881711ff2d534dc8f761131aa"},
{file = "pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b"},
]
[package.dependencies]
numpy = {version = ">=1.22.4", markers = "python_version < \"3.11\""}
python-dateutil = ">=2.8.2"
pytz = ">=2020.1"
tzdata = ">=2022.7"
[package.extras]
all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"]
aws = ["s3fs (>=2022.11.0)"]
clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"]
compression = ["zstandard (>=0.19.0)"]
computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"]
consortium-standard = ["dataframe-api-compat (>=0.1.7)"]
excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"]
feather = ["pyarrow (>=10.0.1)"]
fss = ["fsspec (>=2022.11.0)"]
gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"]
hdf5 = ["tables (>=3.8.0)"]
html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"]
mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"]
output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"]
parquet = ["pyarrow (>=10.0.1)"]
performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"]
plot = ["matplotlib (>=3.6.3)"]
postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"]
pyarrow = ["pyarrow (>=10.0.1)"]
spss = ["pyreadstat (>=1.2.0)"]
sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"]
test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"]
xml = ["lxml (>=4.9.2)"]
[[package]]
name = "pandas"
version = "3.0.1"
description = "Powerful data structures for data analysis, time series, and statistics"
optional = false
python-versions = ">=3.11"
groups = ["main"]
markers = "python_version >= \"3.11\""
files = [
{file = "pandas-3.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de09668c1bf3b925c07e5762291602f0d789eca1b3a781f99c1c78f6cac0e7ea"},
{file = "pandas-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:24ba315ba3d6e5806063ac6eb717504e499ce30bd8c236d8693a5fd3f084c796"},
{file = "pandas-3.0.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:406ce835c55bac912f2a0dcfaf27c06d73c6b04a5dde45f1fd3169ce31337389"},
{file = "pandas-3.0.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:830994d7e1f31dd7e790045235605ab61cff6c94defc774547e8b7fdfbff3dc7"},
{file = "pandas-3.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a64ce8b0f2de1d2efd2ae40b0abe7f8ae6b29fbfb3812098ed5a6f8e235ad9bf"},
{file = "pandas-3.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9832c2c69da24b602c32e0c7b1b508a03949c18ba08d4d9f1c1033426685b447"},
{file = "pandas-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:84f0904a69e7365f79a0c77d3cdfccbfb05bf87847e3a51a41e1426b0edb9c79"},
{file = "pandas-3.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:4a68773d5a778afb31d12e34f7dd4612ab90de8c6fb1d8ffe5d4a03b955082a1"},
{file = "pandas-3.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:476f84f8c20c9f5bc47252b66b4bb25e1a9fc2fa98cead96744d8116cb85771d"},
{file = "pandas-3.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0ab749dfba921edf641d4036c4c21c0b3ea70fea478165cb98a998fb2a261955"},
{file = "pandas-3.0.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8e36891080b87823aff3640c78649b91b8ff6eea3c0d70aeabd72ea43ab069b"},
{file = "pandas-3.0.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:532527a701281b9dd371e2f582ed9094f4c12dd9ffb82c0c54ee28d8ac9520c4"},
{file = "pandas-3.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:356e5c055ed9b0da1580d465657bc7d00635af4fd47f30afb23025352ba764d1"},
{file = "pandas-3.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9d810036895f9ad6345b8f2a338dd6998a74e8483847403582cab67745bff821"},
{file = "pandas-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:536232a5fe26dd989bd633e7a0c450705fdc86a207fec7254a55e9a22950fe43"},
{file = "pandas-3.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:0f463ebfd8de7f326d38037c7363c6dacb857c5881ab8961fb387804d6daf2f7"},
{file = "pandas-3.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5272627187b5d9c20e55d27caf5f2cd23e286aba25cadf73c8590e432e2b7262"},
{file = "pandas-3.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:661e0f665932af88c7877f31da0dc743fe9c8f2524bdffe23d24fdcb67ef9d56"},
{file = "pandas-3.0.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:75e6e292ff898679e47a2199172593d9f6107fd2dd3617c22c2946e97d5df46e"},
{file = "pandas-3.0.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1ff8cf1d2896e34343197685f432450ec99a85ba8d90cce2030c5eee2ef98791"},
{file = "pandas-3.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eca8b4510f6763f3d37359c2105df03a7a221a508f30e396a51d0713d462e68a"},
{file = "pandas-3.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:06aff2ad6f0b94a17822cf8b83bbb563b090ed82ff4fe7712db2ce57cd50d9b8"},
{file = "pandas-3.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:9fea306c783e28884c29057a1d9baa11a349bbf99538ec1da44c8476563d1b25"},
{file = "pandas-3.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:a8d37a43c52917427e897cb2e429f67a449327394396a81034a4449b99afda59"},
{file = "pandas-3.0.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d54855f04f8246ed7b6fc96b05d4871591143c46c0b6f4af874764ed0d2d6f06"},
{file = "pandas-3.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e1b677accee34a09e0dc2ce5624e4a58a1870ffe56fc021e9caf7f23cd7668f"},
{file = "pandas-3.0.1-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a9cabbdcd03f1b6cd254d6dda8ae09b0252524be1592594c00b7895916cb1324"},
{file = "pandas-3.0.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ae2ab1f166668b41e770650101e7090824fd34d17915dd9cd479f5c5e0065e9"},
{file = "pandas-3.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6bf0603c2e30e2cafac32807b06435f28741135cb8697eae8b28c7d492fc7d76"},
{file = "pandas-3.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6c426422973973cae1f4a23e51d4ae85974f44871b24844e4f7de752dd877098"},
{file = "pandas-3.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b03f91ae8c10a85c1613102c7bef5229b5379f343030a3ccefeca8a33414cf35"},
{file = "pandas-3.0.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:99d0f92ed92d3083d140bf6b97774f9f13863924cf3f52a70711f4e7588f9d0a"},
{file = "pandas-3.0.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3b66857e983208654294bb6477b8a63dee26b37bdd0eb34d010556e91261784f"},
{file = "pandas-3.0.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56cf59638bf24dc9bdf2154c81e248b3289f9a09a6d04e63608c159022352749"},
{file = "pandas-3.0.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1a9f55e0f46951874b863d1f3906dcb57df2d9be5c5847ba4dfb55b2c815249"},
{file = "pandas-3.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1849f0bba9c8a2fb0f691d492b834cc8dadf617e29015c66e989448d58d011ee"},
{file = "pandas-3.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c3d288439e11b5325b02ae6e9cc83e6805a62c40c5a6220bea9beb899c073b1c"},
{file = "pandas-3.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:93325b0fe372d192965f4cca88d97667f49557398bbf94abdda3bf1b591dbe66"},
{file = "pandas-3.0.1-cp314-cp314-win_arm64.whl", hash = "sha256:97ca08674e3287c7148f4858b01136f8bdfe7202ad25ad04fec602dd1d29d132"},
{file = "pandas-3.0.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:58eeb1b2e0fb322befcf2bbc9ba0af41e616abadb3d3414a6bc7167f6cbfce32"},
{file = "pandas-3.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cd9af1276b5ca9e298bd79a26bda32fa9cc87ed095b2a9a60978d2ca058eaf87"},
{file = "pandas-3.0.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94f87a04984d6b63788327cd9f79dda62b7f9043909d2440ceccf709249ca988"},
{file = "pandas-3.0.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85fe4c4df62e1e20f9db6ebfb88c844b092c22cd5324bdcf94bfa2fc1b391221"},
{file = "pandas-3.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:331ca75a2f8672c365ae25c0b29e46f5ac0c6551fdace8eec4cd65e4fac271ff"},
{file = "pandas-3.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:15860b1fdb1973fffade772fdb931ccf9b2f400a3f5665aef94a00445d7d8dd5"},
{file = "pandas-3.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:44f1364411d5670efa692b146c748f4ed013df91ee91e9bec5677fb1fd58b937"},
{file = "pandas-3.0.1-cp314-cp314t-win_arm64.whl", hash = "sha256:108dd1790337a494aa80e38def654ca3f0968cf4f362c85f44c15e471667102d"},
{file = "pandas-3.0.1.tar.gz", hash = "sha256:4186a699674af418f655dbd420ed87f50d56b4cd6603784279d9eef6627823c8"},
]
[package.dependencies]
numpy = [
{version = ">=1.26.0", markers = "python_version < \"3.14\""},
{version = ">=2.3.3", markers = "python_version >= \"3.14\""},
]
python-dateutil = ">=2.8.2"
tzdata = {version = "*", markers = "sys_platform == \"win32\" or sys_platform == \"emscripten\""}
[package.extras]
all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.36)", "adbc-driver-postgresql (>=1.2.0)", "adbc-driver-sqlite (>=1.2.0)", "beautifulsoup4 (>=4.12.3)", "bottleneck (>=1.4.2)", "fastparquet (>=2024.11.0)", "fsspec (>=2024.10.0)", "gcsfs (>=2024.10.0)", "html5lib (>=1.1)", "hypothesis (>=6.116.0)", "jinja2 (>=3.1.5)", "lxml (>=5.3.0)", "matplotlib (>=3.9.3)", "numba (>=0.60.0)", "numexpr (>=2.10.2)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.5)", "psycopg2 (>=2.9.10)", "pyarrow (>=13.0.0)", "pyiceberg (>=0.8.1)", "pymysql (>=1.1.1)", "pyreadstat (>=1.2.8)", "pytest (>=8.3.4)", "pytest-xdist (>=3.6.1)", "python-calamine (>=0.3.0)", "pytz (>=2024.2)", "pyxlsb (>=1.0.10)", "qtpy (>=2.4.2)", "s3fs (>=2024.10.0)", "scipy (>=1.14.1)", "tables (>=3.10.1)", "tabulate (>=0.9.0)", "xarray (>=2024.10.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.2.0)", "zstandard (>=0.23.0)"]
aws = ["s3fs (>=2024.10.0)"]
clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.4.2)"]
compression = ["zstandard (>=0.23.0)"]
computation = ["scipy (>=1.14.1)", "xarray (>=2024.10.0)"]
excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.5)", "python-calamine (>=0.3.0)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.2.0)"]
feather = ["pyarrow (>=13.0.0)"]
fss = ["fsspec (>=2024.10.0)"]
gcp = ["gcsfs (>=2024.10.0)"]
hdf5 = ["tables (>=3.10.1)"]
html = ["beautifulsoup4 (>=4.12.3)", "html5lib (>=1.1)", "lxml (>=5.3.0)"]
iceberg = ["pyiceberg (>=0.8.1)"]
mysql = ["SQLAlchemy (>=2.0.36)", "pymysql (>=1.1.1)"]
output-formatting = ["jinja2 (>=3.1.5)", "tabulate (>=0.9.0)"]
parquet = ["pyarrow (>=13.0.0)"]
performance = ["bottleneck (>=1.4.2)", "numba (>=0.60.0)", "numexpr (>=2.10.2)"]
plot = ["matplotlib (>=3.9.3)"]
postgresql = ["SQLAlchemy (>=2.0.36)", "adbc-driver-postgresql (>=1.2.0)", "psycopg2 (>=2.9.10)"]
pyarrow = ["pyarrow (>=13.0.0)"]
spss = ["pyreadstat (>=1.2.8)"]
sql-other = ["SQLAlchemy (>=2.0.36)", "adbc-driver-postgresql (>=1.2.0)", "adbc-driver-sqlite (>=1.2.0)"]
test = ["hypothesis (>=6.116.0)", "pytest (>=8.3.4)", "pytest-xdist (>=3.6.1)"]
timezone = ["pytz (>=2024.2)"]
xml = ["lxml (>=5.3.0)"]
[[package]]
name = "pillow"
version = "12.1.1"
description = "Python Imaging Library (fork)"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "pillow-12.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f1625b72740fdda5d77b4def688eb8fd6490975d06b909fd19f13f391e077e0"},
{file = "pillow-12.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:178aa072084bd88ec759052feca8e56cbb14a60b39322b99a049e58090479713"},
{file = "pillow-12.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b66e95d05ba806247aaa1561f080abc7975daf715c30780ff92a20e4ec546e1b"},
{file = "pillow-12.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89c7e895002bbe49cdc5426150377cbbc04767d7547ed145473f496dfa40408b"},
{file = "pillow-12.1.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a5cbdcddad0af3da87cb16b60d23648bc3b51967eb07223e9fed77a82b457c4"},
{file = "pillow-12.1.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f51079765661884a486727f0729d29054242f74b46186026582b4e4769918e4"},
{file = "pillow-12.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:99c1506ea77c11531d75e3a412832a13a71c7ebc8192ab9e4b2e355555920e3e"},
{file = "pillow-12.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36341d06738a9f66c8287cf8b876d24b18db9bd8740fa0672c74e259ad408cff"},
{file = "pillow-12.1.1-cp310-cp310-win32.whl", hash = "sha256:6c52f062424c523d6c4db85518774cc3d50f5539dd6eed32b8f6229b26f24d40"},
{file = "pillow-12.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6008de247150668a705a6338156efb92334113421ceecf7438a12c9a12dab23"},
{file = "pillow-12.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:1a9b0ee305220b392e1124a764ee4265bd063e54a751a6b62eff69992f457fa9"},
{file = "pillow-12.1.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e879bb6cd5c73848ef3b2b48b8af9ff08c5b71ecda8048b7dd22d8a33f60be32"},
{file = "pillow-12.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:365b10bb9417dd4498c0e3b128018c4a624dc11c7b97d8cc54effe3b096f4c38"},
{file = "pillow-12.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d4ce8e329c93845720cd2014659ca67eac35f6433fd3050393d85f3ecef0dad5"},
{file = "pillow-12.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc354a04072b765eccf2204f588a7a532c9511e8b9c7f900e1b64e3e33487090"},
{file = "pillow-12.1.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e7976bf1910a8116b523b9f9f58bf410f3e8aa330cd9a2bb2953f9266ab49af"},
{file = "pillow-12.1.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:597bd9c8419bc7c6af5604e55847789b69123bbe25d65cc6ad3012b4f3c98d8b"},
{file = "pillow-12.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c1fc0f2ca5f96a3c8407e41cca26a16e46b21060fe6d5b099d2cb01412222f5"},
{file = "pillow-12.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:578510d88c6229d735855e1f278aa305270438d36a05031dfaae5067cc8eb04d"},
{file = "pillow-12.1.1-cp311-cp311-win32.whl", hash = "sha256:7311c0a0dcadb89b36b7025dfd8326ecfa36964e29913074d47382706e516a7c"},
{file = "pillow-12.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:fbfa2a7c10cc2623f412753cddf391c7f971c52ca40a3f65dc5039b2939e8563"},
{file = "pillow-12.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:b81b5e3511211631b3f672a595e3221252c90af017e399056d0faabb9538aa80"},
{file = "pillow-12.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab323b787d6e18b3d91a72fc99b1a2c28651e4358749842b8f8dfacd28ef2052"},
{file = "pillow-12.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:adebb5bee0f0af4909c30db0d890c773d1a92ffe83da908e2e9e720f8edf3984"},
{file = "pillow-12.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb66b7cc26f50977108790e2456b7921e773f23db5630261102233eb355a3b79"},
{file = "pillow-12.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aee2810642b2898bb187ced9b349e95d2a7272930796e022efaf12e99dccd293"},
{file = "pillow-12.1.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a0b1cd6232e2b618adcc54d9882e4e662a089d5768cd188f7c245b4c8c44a397"},
{file = "pillow-12.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7aac39bcf8d4770d089588a2e1dd111cbaa42df5a94be3114222057d68336bd0"},
{file = "pillow-12.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ab174cd7d29a62dd139c44bf74b698039328f45cb03b4596c43473a46656b2f3"},
{file = "pillow-12.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:339ffdcb7cbeaa08221cd401d517d4b1fe7a9ed5d400e4a8039719238620ca35"},
{file = "pillow-12.1.1-cp312-cp312-win32.whl", hash = "sha256:5d1f9575a12bed9e9eedd9a4972834b08c97a352bd17955ccdebfeca5913fa0a"},
{file = "pillow-12.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:21329ec8c96c6e979cd0dfd29406c40c1d52521a90544463057d2aaa937d66a6"},
{file = "pillow-12.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:af9a332e572978f0218686636610555ae3defd1633597be015ed50289a03c523"},
{file = "pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d242e8ac078781f1de88bf823d70c1a9b3c7950a44cdf4b7c012e22ccbcd8e4e"},
{file = "pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:02f84dfad02693676692746df05b89cf25597560db2857363a208e393429f5e9"},
{file = "pillow-12.1.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:e65498daf4b583091ccbb2556c7000abf0f3349fcd57ef7adc9a84a394ed29f6"},
{file = "pillow-12.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c6db3b84c87d48d0088943bf33440e0c42370b99b1c2a7989216f7b42eede60"},
{file = "pillow-12.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b7e5304e34942bf62e15184219a7b5ad4ff7f3bb5cca4d984f37df1a0e1aee2"},
{file = "pillow-12.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:18e5bddd742a44b7e6b1e773ab5db102bd7a94c32555ba656e76d319d19c3850"},
{file = "pillow-12.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc44ef1f3de4f45b50ccf9136999d71abb99dca7706bc75d222ed350b9fd2289"},
{file = "pillow-12.1.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a8eb7ed8d4198bccbd07058416eeec51686b498e784eda166395a23eb99138e"},
{file = "pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47b94983da0c642de92ced1702c5b6c292a84bd3a8e1d1702ff923f183594717"},
{file = "pillow-12.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:518a48c2aab7ce596d3bf79d0e275661b846e86e4d0e7dec34712c30fe07f02a"},
{file = "pillow-12.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a550ae29b95c6dc13cf69e2c9dc5747f814c54eeb2e32d683e5e93af56caa029"},
{file = "pillow-12.1.1-cp313-cp313-win32.whl", hash = "sha256:a003d7422449f6d1e3a34e3dd4110c22148336918ddbfc6a32581cd54b2e0b2b"},
{file = "pillow-12.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:344cf1e3dab3be4b1fa08e449323d98a2a3f819ad20f4b22e77a0ede31f0faa1"},
{file = "pillow-12.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c0dd1636633e7e6a0afe7bf6a51a14992b7f8e60de5789018ebbdfae55b040a"},
{file = "pillow-12.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0330d233c1a0ead844fc097a7d16c0abff4c12e856c0b325f231820fee1f39da"},
{file = "pillow-12.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dae5f21afb91322f2ff791895ddd8889e5e947ff59f71b46041c8ce6db790bc"},
{file = "pillow-12.1.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e0c664be47252947d870ac0d327fea7e63985a08794758aa8af5b6cb6ec0c9c"},
{file = "pillow-12.1.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:691ab2ac363b8217f7d31b3497108fb1f50faab2f75dfb03284ec2f217e87bf8"},
{file = "pillow-12.1.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9e8064fb1cc019296958595f6db671fba95209e3ceb0c4734c9baf97de04b20"},
{file = "pillow-12.1.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:472a8d7ded663e6162dafdf20015c486a7009483ca671cece7a9279b512fcb13"},
{file = "pillow-12.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:89b54027a766529136a06cfebeecb3a04900397a3590fd252160b888479517bf"},
{file = "pillow-12.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:86172b0831b82ce4f7877f280055892b31179e1576aa00d0df3bb1bbf8c3e524"},
{file = "pillow-12.1.1-cp313-cp313t-win32.whl", hash = "sha256:44ce27545b6efcf0fdbdceb31c9a5bdea9333e664cda58a7e674bb74608b3986"},
{file = "pillow-12.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a285e3eb7a5a45a2ff504e31f4a8d1b12ef62e84e5411c6804a42197c1cf586c"},
{file = "pillow-12.1.1-cp313-cp313t-win_arm64.whl", hash = "sha256:cc7d296b5ea4d29e6570dabeaed58d31c3fea35a633a69679fb03d7664f43fb3"},
{file = "pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:417423db963cb4be8bac3fc1204fe61610f6abeed1580a7a2cbb2fbda20f12af"},
{file = "pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:b957b71c6b2387610f556a7eb0828afbe40b4a98036fc0d2acfa5a44a0c2036f"},
{file = "pillow-12.1.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:097690ba1f2efdeb165a20469d59d8bb03c55fb6621eb2041a060ae8ea3e9642"},
{file = "pillow-12.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2815a87ab27848db0321fb78c7f0b2c8649dee134b7f2b80c6a45c6831d75ccd"},
{file = "pillow-12.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f7ed2c6543bad5a7d5530eb9e78c53132f93dfa44a28492db88b41cdab885202"},
{file = "pillow-12.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:652a2c9ccfb556235b2b501a3a7cf3742148cd22e04b5625c5fe057ea3e3191f"},
{file = "pillow-12.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d6e4571eedf43af33d0fc233a382a76e849badbccdf1ac438841308652a08e1f"},
{file = "pillow-12.1.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b574c51cf7d5d62e9be37ba446224b59a2da26dc4c1bb2ecbe936a4fb1a7cb7f"},
{file = "pillow-12.1.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a37691702ed687799de29a518d63d4682d9016932db66d4e90c345831b02fb4e"},
{file = "pillow-12.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f95c00d5d6700b2b890479664a06e754974848afaae5e21beb4d83c106923fd0"},
{file = "pillow-12.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:559b38da23606e68681337ad74622c4dbba02254fc9cb4488a305dd5975c7eeb"},
{file = "pillow-12.1.1-cp314-cp314-win32.whl", hash = "sha256:03edcc34d688572014ff223c125a3f77fb08091e4607e7745002fc214070b35f"},
{file = "pillow-12.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:50480dcd74fa63b8e78235957d302d98d98d82ccbfac4c7e12108ba9ecbdba15"},
{file = "pillow-12.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:5cb1785d97b0c3d1d1a16bc1d710c4a0049daefc4935f3a8f31f827f4d3d2e7f"},
{file = "pillow-12.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1f90cff8aa76835cba5769f0b3121a22bd4eb9e6884cfe338216e557a9a548b8"},
{file = "pillow-12.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1f1be78ce9466a7ee64bfda57bdba0f7cc499d9794d518b854816c41bf0aa4e9"},
{file = "pillow-12.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:42fc1f4677106188ad9a55562bbade416f8b55456f522430fadab3cef7cd4e60"},
{file = "pillow-12.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98edb152429ab62a1818039744d8fbb3ccab98a7c29fc3d5fcef158f3f1f68b7"},
{file = "pillow-12.1.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d470ab1178551dd17fdba0fef463359c41aaa613cdcd7ff8373f54be629f9f8f"},
{file = "pillow-12.1.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6408a7b064595afcab0a49393a413732a35788f2a5092fdc6266952ed67de586"},
{file = "pillow-12.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5d8c41325b382c07799a3682c1c258469ea2ff97103c53717b7893862d0c98ce"},
{file = "pillow-12.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7697918b5be27424e9ce568193efd13d925c4481dd364e43f5dff72d33e10f8"},
{file = "pillow-12.1.1-cp314-cp314t-win32.whl", hash = "sha256:d2912fd8114fc5545aa3a4b5576512f64c55a03f3ebcca4c10194d593d43ea36"},
{file = "pillow-12.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:4ceb838d4bd9dab43e06c363cab2eebf63846d6a4aeaea283bbdfd8f1a8ed58b"},
{file = "pillow-12.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:7b03048319bfc6170e93bd60728a1af51d3dd7704935feb228c4d4faab35d334"},
{file = "pillow-12.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:600fd103672b925fe62ed08e0d874ea34d692474df6f4bf7ebe148b30f89f39f"},
{file = "pillow-12.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:665e1b916b043cef294bc54d47bf02d87e13f769bc4bc5fa225a24b3a6c5aca9"},
{file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:495c302af3aad1ca67420ddd5c7bd480c8867ad173528767d906428057a11f0e"},
{file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8fd420ef0c52c88b5a035a0886f367748c72147b2b8f384c9d12656678dfdfa9"},
{file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f975aa7ef9684ce7e2c18a3aa8f8e2106ce1e46b94ab713d156b2898811651d3"},
{file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8089c852a56c2966cf18835db62d9b34fef7ba74c726ad943928d494fa7f4735"},
{file = "pillow-12.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:cb9bb857b2d057c6dfc72ac5f3b44836924ba15721882ef103cecb40d002d80e"},
{file = "pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4"},
]
[package.extras]
docs = ["furo", "olefile", "sphinx (>=8.2)", "sphinx-autobuild", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"]
fpx = ["olefile"]
mic = ["olefile"]
test-arrow = ["arro3-compute", "arro3-core", "nanoarrow", "pyarrow"]
tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma (>=5)", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"]
xmp = ["defusedxml"]
[[package]]
name = "pyparsing"
version = "3.3.2"
description = "pyparsing - Classes and methods to define and execute parsing grammars"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d"},
{file = "pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc"},
]
[package.extras]
diagrams = ["jinja2", "railroad-diagrams"]
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
groups = ["main"]
files = [
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
]
[package.dependencies]
six = ">=1.5"
[[package]]
name = "pytz"
version = "2026.1.post1"
description = "World timezone definitions, modern and historical"
optional = false
python-versions = "*"
groups = ["main"]
markers = "python_version == \"3.10\""
files = [
{file = "pytz-2026.1.post1-py2.py3-none-any.whl", hash = "sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a"},
{file = "pytz-2026.1.post1.tar.gz", hash = "sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1"},
]
[[package]]
name = "six"
version = "1.17.0"
description = "Python 2 and 3 compatibility utilities"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
groups = ["main"]
files = [
{file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
{file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
]
[[package]]
name = "tzdata"
version = "2025.3"
description = "Provider of IANA time zone data"
optional = false
python-versions = ">=2"
groups = ["main"]
markers = "sys_platform == \"win32\" or sys_platform == \"emscripten\" or python_version == \"3.10\""
files = [
{file = "tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1"},
{file = "tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7"},
]
[metadata]
lock-version = "2.1"
python-versions = ">=3.10"
content-hash = "870a0670af1ce4f6de547b9d027a5a952b895066c9f91ebea1da2ae3cbb1eddd"

16
08/ex1/pyproject.toml Normal file
View File

@@ -0,0 +1,16 @@
[project]
name = "ex1"
version = "1.0.0"
requires-python = ">=3.10"
dependencies = [
"pandas",
"numpy",
"matplotlib",
]
[tool.poetry]
package-mode = false

3
08/ex1/requirements.txt Normal file
View File

@@ -0,0 +1,3 @@
pandas
numpy
matplotlib

5
08/ex2/.env.example Normal file
View File

@@ -0,0 +1,5 @@
MATRIX_MODE="" # "development" or "production"
DATABASE_URL="" # Connection string for data storage
API_KEY="" # Secret key for external services
LOG_LEVEL="" # Logging verbosity
ZION_ENDPOINT="" # URL for the resistance network

1
08/ex2/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.env

48
08/ex2/oracle.py Normal file
View File

@@ -0,0 +1,48 @@
import os
from dotenv import load_dotenv
def check_env() -> None:
configs = [
"MATRIX_MODE",
"DATABASE_URL",
"API_KEY",
"LOG_LEVEL",
"ZION_ENDPOINT",
]
for config in configs:
if os.getenv(config) is None or os.getenv(config) == "":
raise Exception(f"{config} var isn't instanciate in dotenv")
if (
os.getenv("MATRIX_MODE") != "development"
and os.getenv("MATRIX_MODE") != "production"
):
raise Exception("MATRIX_MODE value isn't manage")
def print_env() -> None:
print("Configuration loaded:")
print(f"Mode: {os.getenv('MATRIX_MODE')}")
print("Database: Connected to local instance")
print("API Access: Authenticated")
print(f"Log Level: {os.getenv('LOG_LEV')}")
print("Zion Network: Online")
def main() -> None:
try:
print("\nORACLE STATUS: Reading the Matrix...\n")
load_dotenv()
check_env()
print_env()
print("\nEnvironment security check:")
print("[OK] No hardcoded secrets detected")
print("[OK] .env file properly configured")
print("[OK] Production overrides available")
print("The Oracle sees all configurations.")
except Exception as err:
print(err)
if __name__ == "__main__":
main()

237
09/data_exporter.py Normal file
View File

@@ -0,0 +1,237 @@
#!/usr/bin/env python3
"""
Data export utilities for the Cosmic Data Observatory
Exports generated data to JSON, CSV, and Python formats for testing
"""
import json
import csv
from pathlib import Path
from typing import List, Dict, Any, Union
from data_generator import SpaceStationGenerator, AlienContactGenerator, CrewMissionGenerator, DataConfig
class DataExporter:
"""Handles data export in multiple formats"""
def __init__(self, output_dir: str = "generated_data"):
self.output_dir = Path(output_dir)
self.output_dir.mkdir(exist_ok=True)
def export_to_json(self, data: List[Dict[str, Any]], filename: str) -> Path:
"""Export data to JSON format"""
filepath = self.output_dir / f"{filename}.json"
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
return filepath
def export_to_csv(self, data: List[Dict[str, Any]], filename: str) -> Path:
"""Export flat data to CSV format"""
if not data:
return None
filepath = self.output_dir / f"{filename}.csv"
# Handle nested data by flattening
flat_data = []
for item in data:
flat_item = self._flatten_dict(item)
flat_data.append(flat_item)
# Get all unique keys for CSV headers
all_keys = set()
for item in flat_data:
all_keys.update(item.keys())
with open(filepath, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=sorted(all_keys))
writer.writeheader()
writer.writerows(flat_data)
return filepath
def export_to_python(self, data: List[Dict[str, Any]], filename: str, variable_name: str) -> Path:
"""Export data as Python variable for direct import"""
filepath = self.output_dir / f"{filename}.py"
with open(filepath, 'w', encoding='utf-8') as f:
f.write(f'"""\nGenerated test data for {filename}\n"""\n\n')
# Use repr() to get proper Python syntax instead of JSON
f.write(f'{variable_name} = {self._format_python_data(data)}\n')
return filepath
def _format_python_data(self, data: Any, indent: int = 0) -> str:
"""Format data as valid Python code with proper True/False/None"""
indent_str = ' ' * indent
next_indent_str = ' ' * (indent + 1)
if data is None:
return 'None'
elif isinstance(data, bool):
return 'True' if data else 'False'
elif isinstance(data, (int, float)):
return str(data)
elif isinstance(data, str):
# Escape quotes and format as Python string
return repr(data)
elif isinstance(data, list):
if not data:
return '[]'
items = []
for item in data:
formatted_item = self._format_python_data(item, indent + 1)
items.append(f'{next_indent_str}{formatted_item}')
return '[\n' + ',\n'.join(items) + f'\n{indent_str}]'
elif isinstance(data, dict):
if not data:
return '{}'
items = []
for key, value in data.items():
formatted_value = self._format_python_data(value, indent + 1)
items.append(f'{next_indent_str}{repr(key)}: {formatted_value}')
return '{\n' + ',\n'.join(items) + f'\n{indent_str}}}'
else:
return repr(data)
def _flatten_dict(self, d: Dict[str, Any], parent_key: str = '', sep: str = '_') -> Dict[str, Any]:
"""Flatten nested dictionary for CSV export"""
items = []
for k, v in d.items():
new_key = f"{parent_key}{sep}{k}" if parent_key else k
if isinstance(v, dict):
items.extend(self._flatten_dict(v, new_key, sep=sep).items())
elif isinstance(v, list) and v and isinstance(v[0], dict):
# Handle list of dictionaries by creating numbered entries
for i, item in enumerate(v):
if isinstance(item, dict):
items.extend(self._flatten_dict(item, f"{new_key}_{i}", sep=sep).items())
else:
items.append((f"{new_key}_{i}", item))
elif isinstance(v, list):
# Simple list - join as string
items.append((new_key, ', '.join(map(str, v))))
else:
items.append((new_key, v))
return dict(items)
def generate_all_datasets():
"""Generate complete datasets for all exercise types"""
config = DataConfig()
exporter = DataExporter()
print("🔄 Generating complete datasets...")
# Generate space station data
station_gen = SpaceStationGenerator(config)
stations = station_gen.generate_station_data(10)
# Generate alien contact data
contact_gen = AlienContactGenerator(config)
contacts = contact_gen.generate_contact_data(15)
# Generate mission data
mission_gen = CrewMissionGenerator(config)
missions = mission_gen.generate_mission_data(5)
# Export in multiple formats
datasets = [
(stations, "space_stations", "SPACE_STATIONS"),
(contacts, "alien_contacts", "ALIEN_CONTACTS"),
(missions, "space_missions", "SPACE_MISSIONS")
]
exported_files = []
for data, filename, var_name in datasets:
# Export to JSON
json_file = exporter.export_to_json(data, filename)
exported_files.append(json_file)
# Export to Python
py_file = exporter.export_to_python(data, filename, var_name)
exported_files.append(py_file)
# Export to CSV (only for non-nested data)
if filename != "space_missions": # Missions have nested crew data
csv_file = exporter.export_to_csv(data, filename)
if csv_file:
exported_files.append(csv_file)
print(f"✅ Generated {len(exported_files)} data files:")
for file_path in exported_files:
print(f" 📄 {file_path}")
return exported_files
def create_test_scenarios():
"""Create specific test scenarios for validation testing"""
config = DataConfig()
exporter = DataExporter()
# Invalid space station data
invalid_stations = [
{
"station_id": "TOOLONG123456", # Too long
"name": "Test Station",
"crew_size": 25, # Too many crew
"power_level": 85.0,
"oxygen_level": 92.0,
"last_maintenance": "2024-01-15T10:30:00",
"is_operational": True
},
{
"station_id": "TS", # Too short
"name": "", # Empty name
"crew_size": 0, # No crew
"power_level": -10.0, # Negative power
"oxygen_level": 150.0, # Over 100%
"last_maintenance": "2024-01-15T10:30:00",
"is_operational": True
}
]
# Invalid alien contacts
invalid_contacts = [
{
"contact_id": "WRONG_FORMAT", # Doesn't start with AC
"timestamp": "2024-01-15T14:30:00",
"location": "Area 51",
"contact_type": "radio",
"signal_strength": 8.5,
"duration_minutes": 45,
"witness_count": 5,
"message_received": None, # Strong signal without message
"is_verified": False
},
{
"contact_id": "AC_2024_002",
"timestamp": "2024-01-16T09:15:00",
"location": "Roswell",
"contact_type": "telepathic",
"signal_strength": 6.2,
"duration_minutes": 30,
"witness_count": 1, # Too few witnesses for telepathic
"message_received": None,
"is_verified": False
}
]
# Export test scenarios
exporter.export_to_json(invalid_stations, "invalid_stations")
exporter.export_to_json(invalid_contacts, "invalid_contacts")
print("🧪 Created validation test scenarios")
if __name__ == "__main__":
generate_all_datasets()
create_test_scenarios()
print("\n🎯 All data files ready for testing!")

302
09/data_generator.py Normal file
View File

@@ -0,0 +1,302 @@
#!/usr/bin/env python3
"""
Cosmic Data Observatory - Data Generator
Generates realistic test data for space station monitoring, alien contacts, and crew missions.
"""
import random
from datetime import datetime, timedelta
from typing import List, Dict, Any
from dataclasses import dataclass
@dataclass
class DataConfig:
"""Configuration parameters for data generation"""
seed: int = 42
base_date: datetime = datetime(2024, 1, 1)
date_range_days: int = 365
class SpaceStationGenerator:
"""Generates space station monitoring data"""
STATION_NAMES = [
"International Space Station", "Lunar Gateway", "Mars Orbital Platform",
"Europa Research Station", "Titan Mining Outpost", "Asteroid Belt Relay",
"Deep Space Observatory", "Solar Wind Monitor", "Quantum Communications Hub"
]
STATION_PREFIXES = ["ISS", "LGW", "MOP", "ERS", "TMO", "ABR", "DSO", "SWM", "QCH"]
def __init__(self, config: DataConfig):
self.config = config
random.seed(config.seed)
def generate_station_data(self, count: int = 5) -> List[Dict[str, Any]]:
"""Generate multiple space station records"""
stations = []
for i in range(count):
station_id = f"{random.choice(self.STATION_PREFIXES)}{random.randint(100, 999)}"
name = random.choice(self.STATION_NAMES)
# Realistic operational parameters
crew_size = random.randint(3, 12)
power_level = round(random.uniform(70.0, 98.5), 1)
oxygen_level = round(random.uniform(85.0, 99.2), 1)
# Recent maintenance date
days_ago = random.randint(1, 180)
maintenance_date = self.config.base_date - timedelta(days=days_ago)
# Operational status based on system health
is_operational = power_level > 75.0 and oxygen_level > 90.0
# Optional maintenance notes
notes = None
if not is_operational:
notes = "System diagnostics required"
elif random.random() < 0.3:
notes = "All systems nominal"
stations.append({
"station_id": station_id,
"name": name,
"crew_size": crew_size,
"power_level": power_level,
"oxygen_level": oxygen_level,
"last_maintenance": maintenance_date.isoformat(),
"is_operational": is_operational,
"notes": notes
})
return stations
class AlienContactGenerator:
"""Generates alien contact report data"""
LOCATIONS = [
"Area 51, Nevada", "Roswell, New Mexico", "SETI Institute, California",
"Arecibo Observatory, Puerto Rico", "Atacama Desert, Chile",
"Antarctic Research Station", "International Space Station",
"Mauna Kea Observatory, Hawaii", "Very Large Array, New Mexico"
]
CONTACT_TYPES = ["radio", "visual", "physical", "telepathic"]
MESSAGES = [
"Greetings from Zeta Reticuli",
"Mathematical sequence detected: prime numbers",
"Coordinates to star system received",
"Warning about solar flare activity",
"Request for peaceful contact",
"Unknown language pattern identified"
]
def __init__(self, config: DataConfig):
self.config = config
random.seed(config.seed + 1)
def generate_contact_data(self, count: int = 8) -> List[Dict[str, Any]]:
"""Generate multiple alien contact records"""
contacts = []
for i in range(count):
contact_id = f"AC_{self.config.base_date.year}_{str(i+1).zfill(3)}"
# Random contact timing within date range
days_offset = random.randint(0, self.config.date_range_days)
contact_time = self.config.base_date + timedelta(days=days_offset)
location = random.choice(self.LOCATIONS)
contact_type = random.choice(self.CONTACT_TYPES)
# Signal characteristics
signal_strength = round(random.uniform(1.0, 10.0), 1)
duration = random.randint(5, 240) # 5 minutes to 4 hours
witnesses = random.randint(1, 15)
# Message content for strong signals
message = None
if signal_strength > 6.0 and random.random() < 0.7:
message = random.choice(self.MESSAGES)
# Verification status
is_verified = False
if contact_type == "physical":
is_verified = True # Physical contacts must be verified
elif contact_type == "radio" and signal_strength > 8.0:
is_verified = random.random() < 0.8
# Adjust witnesses for telepathic contacts
if contact_type == "telepathic" and witnesses < 3:
witnesses = random.randint(3, 8)
contacts.append({
"contact_id": contact_id,
"timestamp": contact_time.isoformat(),
"location": location,
"contact_type": contact_type,
"signal_strength": signal_strength,
"duration_minutes": duration,
"witness_count": witnesses,
"message_received": message,
"is_verified": is_verified
})
return contacts
class CrewMissionGenerator:
"""Generates space crew and mission data"""
FIRST_NAMES = [
"Sarah", "John", "Alice", "Michael", "Emma", "David", "Lisa", "Robert",
"Maria", "James", "Anna", "William", "Elena", "Thomas", "Sofia", "Daniel"
]
LAST_NAMES = [
"Connor", "Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia",
"Miller", "Davis", "Rodriguez", "Martinez", "Hernandez", "Lopez", "Gonzalez"
]
SPECIALIZATIONS = [
"Mission Command", "Navigation", "Engineering", "Life Support",
"Communications", "Medical Officer", "Pilot", "Science Officer",
"Maintenance", "Security", "Research", "Systems Analysis"
]
RANKS = ["cadet", "officer", "lieutenant", "captain", "commander"]
DESTINATIONS = [
"Mars", "Moon", "Europa", "Titan", "Asteroid Belt",
"Jupiter Orbit", "Saturn Rings", "Deep Space", "Solar Observatory"
]
def __init__(self, config: DataConfig):
self.config = config
random.seed(config.seed + 2)
def generate_crew_member(self, member_id: str) -> Dict[str, Any]:
"""Generate a single crew member"""
name = f"{random.choice(self.FIRST_NAMES)} {random.choice(self.LAST_NAMES)}"
rank = random.choice(self.RANKS)
age = random.randint(25, 55)
specialization = random.choice(self.SPECIALIZATIONS)
# Experience correlates with age and rank
base_experience = max(0, age - 22)
rank_bonus = {"cadet": 0, "officer": 2, "lieutenant": 5, "captain": 8, "commander": 12}
years_experience = min(base_experience + rank_bonus[rank] + random.randint(-2, 3), 30)
return {
"member_id": member_id,
"name": name,
"rank": rank,
"age": age,
"specialization": specialization,
"years_experience": max(0, years_experience),
"is_active": True
}
def generate_mission_data(self, count: int = 3) -> List[Dict[str, Any]]:
"""Generate complete mission records with crews"""
missions = []
for i in range(count):
mission_id = f"M{self.config.base_date.year}_{random.choice(['MARS', 'LUNA', 'EUROPA', 'TITAN'])}"
destination = random.choice(self.DESTINATIONS)
mission_name = f"{destination} {'Colony' if random.random() < 0.5 else 'Research'} Mission"
# Mission timing
launch_offset = random.randint(30, 300)
launch_date = self.config.base_date + timedelta(days=launch_offset)
# Mission parameters
duration = random.randint(90, 1200) # 3 months to 3+ years
budget = round(random.uniform(500.0, 5000.0), 1)
# Generate crew (3-8 members)
crew_size = random.randint(3, 8)
crew = []
# Ensure at least one high-ranking officer
has_commander = False
for j in range(crew_size):
member_id = f"CM{str(i*10 + j + 1).zfill(3)}"
member = self.generate_crew_member(member_id)
# First member has higher chance of being high-ranking
if j == 0 and not has_commander:
member["rank"] = random.choice(["captain", "commander"])
has_commander = True
crew.append(member)
# For long missions, ensure experienced crew
if duration > 365:
experienced_needed = len(crew) // 2
experienced_count = sum(1 for member in crew if member["years_experience"] >= 5)
if experienced_count < experienced_needed:
# Boost experience for some crew members
for member in crew[:experienced_needed]:
if member["years_experience"] < 5:
member["years_experience"] = random.randint(5, 15)
missions.append({
"mission_id": mission_id,
"mission_name": mission_name,
"destination": destination,
"launch_date": launch_date.isoformat(),
"duration_days": duration,
"crew": crew,
"mission_status": "planned",
"budget_millions": budget
})
return missions
def main():
"""Generate sample data for all exercise types"""
config = DataConfig()
print("🚀 Cosmic Data Observatory - Sample Data Generator")
print("=" * 60)
# Generate space station data
station_gen = SpaceStationGenerator(config)
stations = station_gen.generate_station_data(5)
print(f"\n📡 Generated {len(stations)} space stations:")
for station in stations:
status = "✅ Operational" if station["is_operational"] else "⚠️ Maintenance"
print(f" {station['station_id']}: {station['name']} - {status}")
# Generate alien contact data
contact_gen = AlienContactGenerator(config)
contacts = contact_gen.generate_contact_data(6)
print(f"\n👽 Generated {len(contacts)} alien contacts:")
for contact in contacts:
verified = "✅ Verified" if contact["is_verified"] else "❓ Unverified"
print(f" {contact['contact_id']}: {contact['contact_type']} at {contact['location']} - {verified}")
# Generate mission data
mission_gen = CrewMissionGenerator(config)
missions = mission_gen.generate_mission_data(3)
print(f"\n🚀 Generated {len(missions)} space missions:")
for mission in missions:
print(f" {mission['mission_id']}: {mission['mission_name']}")
print(f" Crew: {len(mission['crew'])} members, Duration: {mission['duration_days']} days")
print("\n" + "=" * 60)
print("Data generation complete! Use these generators in your exercises.")
if __name__ == "__main__":
main()

194
09/ex0/space_station.py Normal file
View File

@@ -0,0 +1,194 @@
import datetime
from typing import Optional
from pydantic import BaseModel, Field, ValidationError
SPACE_STATIONS = [
{
"station_id": "LGW125",
"name": "Titan Mining Outpost",
"crew_size": 6,
"power_level": 76.4,
"oxygen_level": 95.5,
"last_maintenance": "2023-07-11T00:00:00",
"is_operational": True,
"notes": None,
},
{
"station_id": "QCH189",
"name": "Deep Space Observatory",
"crew_size": 3,
"power_level": 70.8,
"oxygen_level": 88.1,
"last_maintenance": "2023-08-24T00:00:00",
"is_operational": False,
"notes": "System diagnostics required",
},
{
"station_id": "ISS674",
"name": "Europa Research Station",
"crew_size": 11,
"power_level": 82.0,
"oxygen_level": 91.4,
"last_maintenance": "2023-10-21T00:00:00",
"is_operational": True,
"notes": None,
},
{
"station_id": "ISS877",
"name": "Mars Orbital Platform",
"crew_size": 9,
"power_level": 79.7,
"oxygen_level": 87.2,
"last_maintenance": "2023-10-06T00:00:00",
"is_operational": False,
"notes": "System diagnostics required",
},
{
"station_id": "LGW194",
"name": "Deep Space Observatory",
"crew_size": 4,
"power_level": 80.2,
"oxygen_level": 89.9,
"last_maintenance": "2023-10-25T00:00:00",
"is_operational": False,
"notes": "System diagnostics required",
},
{
"station_id": "ISS847",
"name": "Solar Wind Monitor",
"crew_size": 11,
"power_level": 73.6,
"oxygen_level": 98.1,
"last_maintenance": "2023-12-11T00:00:00",
"is_operational": False,
"notes": "System diagnostics required",
},
{
"station_id": "QCH400",
"name": "Asteroid Belt Relay",
"crew_size": 12,
"power_level": 75.5,
"oxygen_level": 86.0,
"last_maintenance": "2023-07-15T00:00:00",
"is_operational": False,
"notes": "System diagnostics required",
},
{
"station_id": "ERS891",
"name": "Titan Mining Outpost",
"crew_size": 4,
"power_level": 94.4,
"oxygen_level": 97.3,
"last_maintenance": "2023-09-25T00:00:00",
"is_operational": True,
"notes": "All systems nominal",
},
{
"station_id": "ABR266",
"name": "Asteroid Belt Relay",
"crew_size": 8,
"power_level": 76.0,
"oxygen_level": 88.8,
"last_maintenance": "2023-07-10T00:00:00",
"is_operational": False,
"notes": "System diagnostics required",
},
{
"station_id": "LGW723",
"name": "Mars Orbital Platform",
"crew_size": 11,
"power_level": 90.8,
"oxygen_level": 87.3,
"last_maintenance": "2023-09-25T00:00:00",
"is_operational": False,
"notes": "System diagnostics required",
},
]
class SpaceStation(BaseModel):
station_id: str = Field(max_length=10, min_length=3)
name: str = Field(min_length=1, max_length=50)
crew_size: int = Field(le=20, ge=1)
power_level: float = Field(ge=0.0, le=100.0)
oxygen_level: float = Field(ge=0.0, le=100.0)
last_maintenance: datetime.datetime
is_operational: bool = Field(default=True)
notes: str | None = Field(default=None, max_length=200)
def main() -> None:
try:
valid_station = {
"station_id": "ISS001",
"name": "International Space Station",
"crew_size": 6,
"power_level": 85.5,
"oxygen_level": 92.3,
"last_maintenance": "2023-09-25T00:00:00",
}
invalid_station = {
"station_id": "ISS001",
"name": "International Space Station",
"crew_size": 26,
"power_level": 85.5,
"oxygen_level": 92.3,
"last_maintenance": "2023-09-25T00:00:00",
}
try:
print("\nValid station:n\n")
ss = SpaceStation(**valid_station)
print("Valid station created")
print(
f"ID: {ss.station_id}\n"
f"Name {ss.name}\n"
f"Crew: {ss.crew_size} people\n"
f"Power: {ss.power_level}%\n"
f"Oxygen: {ss.oxygen_level}%\n"
f"Status: {'operational' if ss.is_operational else 'not operational'}\n"
f"Note: {ss.notes}\n"
)
except ValidationError as err:
print(err)
try:
print("\nInvalid station:\n")
ss = SpaceStation(**invalid_station)
print("Valid station created")
print(
f"ID: {ss.station_id}\n"
f"Name {ss.name}\n"
f"Crew: {ss.crew_size} people\n"
f"Power: {ss.power_level}%\n"
f"Oxygen: {ss.oxygen_level}%\n"
f"Status: {'operational' if ss.is_operational else 'not operational'}\n"
f"Note: {ss.notes}\n"
)
except ValidationError as err:
print(err)
print("\nBatch stations:\n")
for station in SPACE_STATIONS:
try:
ss = SpaceStation(**station)
print("========================================")
print("Valid station created")
print(
f"ID: {ss.station_id}\n"
f"Name {ss.name}\n"
f"Crew: {ss.crew_size} people\n"
f"Power: {ss.power_level}%\n"
f"Oxygen: {ss.oxygen_level}%\n"
f"Status: {'operational' if ss.is_operational else 'not operational'}\n"
f"Note: {ss.notes}\n"
)
except ValidationError as e:
print(e.errors())
except Exception as err:
print(err)
if __name__ == "__main__":
main()

75
09/ex1/alien_contact.py Normal file
View File

@@ -0,0 +1,75 @@
from typing_extensions import Self
import datetime
from enum import Enum
from pydantic import BaseModel, Field, model_validator
import json
class ContactType(Enum):
RADIO = "radio"
VISUAL = "visual"
PHYSICAL = "physical"
TELEPATHIC = "telepathic"
class AlienContact(BaseModel):
contact_id: str = Field(min_length=5, max_length=15)
timestamp: datetime.datetime
location: str = Field(min_length=3, max_length=100)
contact_type: ContactType
signal_strength: float = Field(ge=0.0, le=10.0)
duration_minutes: int = Field(ge=1, le=1440)
witness_count: int = Field(ge=1, le=100)
message_received: str | None = Field(default=None, max_length=500)
is_verified: bool = Field(default=False)
@model_validator(mode="after")
def format_validation(self) -> Self:
if not self.contact_id.startswith("AC"):
raise ValueError('Contact ID must start with "AC"')
if (
self.contact_type == ContactType.PHYSICAL
and self.is_verified == False
):
raise ValueError("Physical contact reports must be verified")
if (
self.contact_type == ContactType.TELEPATHIC
and self.witness_count < 3
):
raise ValueError(
"Telepathic contact requires at least 3 witnesses"
)
if self.signal_strength > 7.0 and not self.message_received:
raise ValueError(
"Strong signals (> 7.0) should include received messages"
)
return self
def main() -> None:
try:
with open("../generated_data/alien_contacts.json") as file:
data = json.load(file)
for contact in data:
try:
ac = AlienContact(**contact)
print(
f"======================================\n"
f"Valid contact report:\n"
f"ID: {ac.contact_id}\n"
f"Type: {ac.contact_type}\n"
f"Location: {ac.location}\n"
f"Signal: {ac.signal_strength:.1f}/10\n"
f"Duration: {ac.duration_minutes} minutes\n"
f"Witness: {ac.witness_count}\n"
f"Message: {ac.message_received}\n"
f"Is verified: {ac.is_verified}\n"
)
except Exception as err:
print(err)
except Exception as err:
print(err)
if __name__ == "__main__":
main()

97
09/ex2/space_crew.py Normal file
View File

@@ -0,0 +1,97 @@
from enum import Enum
from typing_extensions import Self
from pydantic import BaseModel, Field, model_validator
from datetime import datetime
from json import load
class Rank(Enum):
CADET = "cadet"
OFFICER = "officer"
LIEUTENANT = "lieutenant"
CAPTAIN = "captain"
COMMANDER = "commander"
class CrewMember(BaseModel):
member_id: str = Field(min_length=3, max_length=10)
name: str = Field(min_length=2, max_length=50)
rank: Rank
age: int = Field(ge=18, le=80)
specialization: str = Field(min_length=3, max_length=30)
years_experience: int = Field(ge=0, le=50)
is_active: bool = Field(default=True)
class SpaceMission(BaseModel):
mission_id: str = Field(min_length=5, max_length=15)
mission_name: str = Field(min_length=3, max_length=100)
destination: str = Field(min_length=3, max_length=50)
launch_date: datetime
duration_days: int = Field(ge=1, le=3650)
crew: list[CrewMember] = Field(min_length=1, max_length=12)
mission_status: str = Field(default="planned")
budget_millions: float = Field(ge=1.0, le=10000.0)
@model_validator(mode="after")
def validation(self) -> Self:
if not self.mission_id.startswith("M"):
raise ValueError('Mission ID must start with "M"')
try:
crew = {member.rank: member for member in self.crew}
crew[Rank.CAPTAIN]
crew[Rank.COMMANDER]
except KeyError:
raise ValueError("Must have at least one Commander or Captain")
if (
self.duration_days > 365
and len(
[
member
for member in self.crew
if member.years_experience >= 5
]
)
< len(self.crew) / 2
):
raise ValueError(
"Long missions (> 365 days) need 50% "
"experienced crew (5+ years)"
)
for member in self.crew:
if member.is_active == False:
raise ValueError("All crew members must be active")
return self
def main() -> None:
try:
with open("../generated_data/space_missions.json") as file:
data = load(file)
for mission in data:
try:
print("=========================================")
sp = SpaceMission(**mission)
print(
"Valid mission created:\n"
f"Mission: {sp.mission_name}\n"
f"ID: {sp.mission_id}\n"
f"Destination: {sp.destination}\n"
f"Duration: {sp.duration_days} days\n"
f"Budget: ${sp.budget_millions}M\n"
f"Crew size: {len(sp.crew)}\n"
"Crew members:"
)
for member in sp.crew:
print(
f"- {member.name} ({member.rank}) - "
f"{member.specialization}"
)
except Exception as err:
print(err)
except Exception as err:
print(err)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,16 @@
contact_id,contact_type,duration_minutes,is_verified,location,message_received,signal_strength,timestamp,witness_count
AC_2024_001,visual,99,False,"Atacama Desert, Chile",Greetings from Zeta Reticuli,9.6,2024-01-20T00:00:00,11
AC_2024_002,radio,152,False,"Mauna Kea Observatory, Hawaii",,5.6,2024-08-20T00:00:00,6
AC_2024_003,telepathic,19,False,"Very Large Array, New Mexico",,4.5,2024-11-15T00:00:00,14
AC_2024_004,telepathic,46,False,"Roswell, New Mexico",,2.4,2024-02-24T00:00:00,9
AC_2024_005,telepathic,134,False,"SETI Institute, California",Warning about solar flare activity,6.4,2024-09-10T00:00:00,5
AC_2024_006,radio,20,False,"Area 51, Nevada",,2.7,2024-02-02T00:00:00,14
AC_2024_007,physical,138,True,"Atacama Desert, Chile",Request for peaceful contact,9.0,2024-03-25T00:00:00,10
AC_2024_008,radio,122,True,"Area 51, Nevada",Unknown language pattern identified,8.6,2024-11-30T00:00:00,13
AC_2024_009,visual,25,False,"Mauna Kea Observatory, Hawaii",,2.1,2024-09-27T00:00:00,13
AC_2024_010,physical,52,True,"Area 51, Nevada",,4.3,2024-06-12T00:00:00,11
AC_2024_011,radio,235,False,"Roswell, New Mexico",,3.7,2024-11-05T00:00:00,13
AC_2024_012,radio,111,False,International Space Station,,5.3,2024-07-04T00:00:00,10
AC_2024_013,visual,228,False,Antarctic Research Station,,6.8,2024-02-12T00:00:00,11
AC_2024_014,radio,113,False,"Atacama Desert, Chile",Mathematical sequence detected: prime numbers,7.2,2024-10-20T00:00:00,8
AC_2024_015,radio,9,False,"Roswell, New Mexico",,2.1,2024-01-02T00:00:00,13
1 contact_id contact_type duration_minutes is_verified location message_received signal_strength timestamp witness_count
2 AC_2024_001 visual 99 False Atacama Desert, Chile Greetings from Zeta Reticuli 9.6 2024-01-20T00:00:00 11
3 AC_2024_002 radio 152 False Mauna Kea Observatory, Hawaii 5.6 2024-08-20T00:00:00 6
4 AC_2024_003 telepathic 19 False Very Large Array, New Mexico 4.5 2024-11-15T00:00:00 14
5 AC_2024_004 telepathic 46 False Roswell, New Mexico 2.4 2024-02-24T00:00:00 9
6 AC_2024_005 telepathic 134 False SETI Institute, California Warning about solar flare activity 6.4 2024-09-10T00:00:00 5
7 AC_2024_006 radio 20 False Area 51, Nevada 2.7 2024-02-02T00:00:00 14
8 AC_2024_007 physical 138 True Atacama Desert, Chile Request for peaceful contact 9.0 2024-03-25T00:00:00 10
9 AC_2024_008 radio 122 True Area 51, Nevada Unknown language pattern identified 8.6 2024-11-30T00:00:00 13
10 AC_2024_009 visual 25 False Mauna Kea Observatory, Hawaii 2.1 2024-09-27T00:00:00 13
11 AC_2024_010 physical 52 True Area 51, Nevada 4.3 2024-06-12T00:00:00 11
12 AC_2024_011 radio 235 False Roswell, New Mexico 3.7 2024-11-05T00:00:00 13
13 AC_2024_012 radio 111 False International Space Station 5.3 2024-07-04T00:00:00 10
14 AC_2024_013 visual 228 False Antarctic Research Station 6.8 2024-02-12T00:00:00 11
15 AC_2024_014 radio 113 False Atacama Desert, Chile Mathematical sequence detected: prime numbers 7.2 2024-10-20T00:00:00 8
16 AC_2024_015 radio 9 False Roswell, New Mexico 2.1 2024-01-02T00:00:00 13

View File

@@ -0,0 +1,167 @@
[
{
"contact_id": "AC_2024_001",
"timestamp": "2024-01-20T00:00:00",
"location": "Atacama Desert, Chile",
"contact_type": "visual",
"signal_strength": 9.6,
"duration_minutes": 99,
"witness_count": 11,
"message_received": "Greetings from Zeta Reticuli",
"is_verified": false
},
{
"contact_id": "AC_2024_002",
"timestamp": "2024-08-20T00:00:00",
"location": "Mauna Kea Observatory, Hawaii",
"contact_type": "radio",
"signal_strength": 5.6,
"duration_minutes": 152,
"witness_count": 6,
"message_received": null,
"is_verified": false
},
{
"contact_id": "AC_2024_003",
"timestamp": "2024-11-15T00:00:00",
"location": "Very Large Array, New Mexico",
"contact_type": "telepathic",
"signal_strength": 4.5,
"duration_minutes": 19,
"witness_count": 14,
"message_received": null,
"is_verified": false
},
{
"contact_id": "AC_2024_004",
"timestamp": "2024-02-24T00:00:00",
"location": "Roswell, New Mexico",
"contact_type": "telepathic",
"signal_strength": 2.4,
"duration_minutes": 46,
"witness_count": 9,
"message_received": null,
"is_verified": false
},
{
"contact_id": "AC_2024_005",
"timestamp": "2024-09-10T00:00:00",
"location": "SETI Institute, California",
"contact_type": "telepathic",
"signal_strength": 6.4,
"duration_minutes": 134,
"witness_count": 5,
"message_received": "Warning about solar flare activity",
"is_verified": false
},
{
"contact_id": "AC_2024_006",
"timestamp": "2024-02-02T00:00:00",
"location": "Area 51, Nevada",
"contact_type": "radio",
"signal_strength": 2.7,
"duration_minutes": 20,
"witness_count": 14,
"message_received": null,
"is_verified": false
},
{
"contact_id": "AC_2024_007",
"timestamp": "2024-03-25T00:00:00",
"location": "Atacama Desert, Chile",
"contact_type": "physical",
"signal_strength": 9.0,
"duration_minutes": 138,
"witness_count": 10,
"message_received": "Request for peaceful contact",
"is_verified": true
},
{
"contact_id": "AC_2024_008",
"timestamp": "2024-11-30T00:00:00",
"location": "Area 51, Nevada",
"contact_type": "radio",
"signal_strength": 8.6,
"duration_minutes": 122,
"witness_count": 13,
"message_received": "Unknown language pattern identified",
"is_verified": true
},
{
"contact_id": "AC_2024_009",
"timestamp": "2024-09-27T00:00:00",
"location": "Mauna Kea Observatory, Hawaii",
"contact_type": "visual",
"signal_strength": 2.1,
"duration_minutes": 25,
"witness_count": 13,
"message_received": null,
"is_verified": false
},
{
"contact_id": "AC_2024_010",
"timestamp": "2024-06-12T00:00:00",
"location": "Area 51, Nevada",
"contact_type": "physical",
"signal_strength": 4.3,
"duration_minutes": 52,
"witness_count": 11,
"message_received": null,
"is_verified": true
},
{
"contact_id": "AC_2024_011",
"timestamp": "2024-11-05T00:00:00",
"location": "Roswell, New Mexico",
"contact_type": "radio",
"signal_strength": 3.7,
"duration_minutes": 235,
"witness_count": 13,
"message_received": null,
"is_verified": false
},
{
"contact_id": "AC_2024_012",
"timestamp": "2024-07-04T00:00:00",
"location": "International Space Station",
"contact_type": "radio",
"signal_strength": 5.3,
"duration_minutes": 111,
"witness_count": 10,
"message_received": null,
"is_verified": false
},
{
"contact_id": "AC_2024_013",
"timestamp": "2024-02-12T00:00:00",
"location": "Antarctic Research Station",
"contact_type": "visual",
"signal_strength": 6.8,
"duration_minutes": 228,
"witness_count": 11,
"message_received": null,
"is_verified": false
},
{
"contact_id": "AC_2024_014",
"timestamp": "2024-10-20T00:00:00",
"location": "Atacama Desert, Chile",
"contact_type": "radio",
"signal_strength": 7.2,
"duration_minutes": 113,
"witness_count": 8,
"message_received": "Mathematical sequence detected: prime numbers",
"is_verified": false
},
{
"contact_id": "AC_2024_015",
"timestamp": "2024-01-02T00:00:00",
"location": "Roswell, New Mexico",
"contact_type": "radio",
"signal_strength": 2.1,
"duration_minutes": 9,
"witness_count": 13,
"message_received": null,
"is_verified": false
}
]

View File

@@ -0,0 +1,171 @@
"""
Generated test data for alien_contacts
"""
ALIEN_CONTACTS = [
{
'contact_id': 'AC_2024_001',
'timestamp': '2024-01-20T00:00:00',
'location': 'Atacama Desert, Chile',
'contact_type': 'visual',
'signal_strength': 9.6,
'duration_minutes': 99,
'witness_count': 11,
'message_received': 'Greetings from Zeta Reticuli',
'is_verified': False
},
{
'contact_id': 'AC_2024_002',
'timestamp': '2024-08-20T00:00:00',
'location': 'Mauna Kea Observatory, Hawaii',
'contact_type': 'radio',
'signal_strength': 5.6,
'duration_minutes': 152,
'witness_count': 6,
'message_received': None,
'is_verified': False
},
{
'contact_id': 'AC_2024_003',
'timestamp': '2024-11-15T00:00:00',
'location': 'Very Large Array, New Mexico',
'contact_type': 'telepathic',
'signal_strength': 4.5,
'duration_minutes': 19,
'witness_count': 14,
'message_received': None,
'is_verified': False
},
{
'contact_id': 'AC_2024_004',
'timestamp': '2024-02-24T00:00:00',
'location': 'Roswell, New Mexico',
'contact_type': 'telepathic',
'signal_strength': 2.4,
'duration_minutes': 46,
'witness_count': 9,
'message_received': None,
'is_verified': False
},
{
'contact_id': 'AC_2024_005',
'timestamp': '2024-09-10T00:00:00',
'location': 'SETI Institute, California',
'contact_type': 'telepathic',
'signal_strength': 6.4,
'duration_minutes': 134,
'witness_count': 5,
'message_received': 'Warning about solar flare activity',
'is_verified': False
},
{
'contact_id': 'AC_2024_006',
'timestamp': '2024-02-02T00:00:00',
'location': 'Area 51, Nevada',
'contact_type': 'radio',
'signal_strength': 2.7,
'duration_minutes': 20,
'witness_count': 14,
'message_received': None,
'is_verified': False
},
{
'contact_id': 'AC_2024_007',
'timestamp': '2024-03-25T00:00:00',
'location': 'Atacama Desert, Chile',
'contact_type': 'physical',
'signal_strength': 9.0,
'duration_minutes': 138,
'witness_count': 10,
'message_received': 'Request for peaceful contact',
'is_verified': True
},
{
'contact_id': 'AC_2024_008',
'timestamp': '2024-11-30T00:00:00',
'location': 'Area 51, Nevada',
'contact_type': 'radio',
'signal_strength': 8.6,
'duration_minutes': 122,
'witness_count': 13,
'message_received': 'Unknown language pattern identified',
'is_verified': True
},
{
'contact_id': 'AC_2024_009',
'timestamp': '2024-09-27T00:00:00',
'location': 'Mauna Kea Observatory, Hawaii',
'contact_type': 'visual',
'signal_strength': 2.1,
'duration_minutes': 25,
'witness_count': 13,
'message_received': None,
'is_verified': False
},
{
'contact_id': 'AC_2024_010',
'timestamp': '2024-06-12T00:00:00',
'location': 'Area 51, Nevada',
'contact_type': 'physical',
'signal_strength': 4.3,
'duration_minutes': 52,
'witness_count': 11,
'message_received': None,
'is_verified': True
},
{
'contact_id': 'AC_2024_011',
'timestamp': '2024-11-05T00:00:00',
'location': 'Roswell, New Mexico',
'contact_type': 'radio',
'signal_strength': 3.7,
'duration_minutes': 235,
'witness_count': 13,
'message_received': None,
'is_verified': False
},
{
'contact_id': 'AC_2024_012',
'timestamp': '2024-07-04T00:00:00',
'location': 'International Space Station',
'contact_type': 'radio',
'signal_strength': 5.3,
'duration_minutes': 111,
'witness_count': 10,
'message_received': None,
'is_verified': False
},
{
'contact_id': 'AC_2024_013',
'timestamp': '2024-02-12T00:00:00',
'location': 'Antarctic Research Station',
'contact_type': 'visual',
'signal_strength': 6.8,
'duration_minutes': 228,
'witness_count': 11,
'message_received': None,
'is_verified': False
},
{
'contact_id': 'AC_2024_014',
'timestamp': '2024-10-20T00:00:00',
'location': 'Atacama Desert, Chile',
'contact_type': 'radio',
'signal_strength': 7.2,
'duration_minutes': 113,
'witness_count': 8,
'message_received': 'Mathematical sequence detected: prime numbers',
'is_verified': False
},
{
'contact_id': 'AC_2024_015',
'timestamp': '2024-01-02T00:00:00',
'location': 'Roswell, New Mexico',
'contact_type': 'radio',
'signal_strength': 2.1,
'duration_minutes': 9,
'witness_count': 13,
'message_received': None,
'is_verified': False
}
]

View File

@@ -0,0 +1,24 @@
[
{
"contact_id": "WRONG_FORMAT",
"timestamp": "2024-01-15T14:30:00",
"location": "Area 51",
"contact_type": "radio",
"signal_strength": 8.5,
"duration_minutes": 45,
"witness_count": 5,
"message_received": null,
"is_verified": false
},
{
"contact_id": "AC_2024_002",
"timestamp": "2024-01-16T09:15:00",
"location": "Roswell",
"contact_type": "telepathic",
"signal_strength": 6.2,
"duration_minutes": 30,
"witness_count": 1,
"message_received": null,
"is_verified": false
}
]

View File

@@ -0,0 +1,20 @@
[
{
"station_id": "TOOLONG123456",
"name": "Test Station",
"crew_size": 25,
"power_level": 85.0,
"oxygen_level": 92.0,
"last_maintenance": "2024-01-15T10:30:00",
"is_operational": true
},
{
"station_id": "TS",
"name": "",
"crew_size": 0,
"power_level": -10.0,
"oxygen_level": 150.0,
"last_maintenance": "2024-01-15T10:30:00",
"is_operational": true
}
]

View File

@@ -0,0 +1,309 @@
[
{
"mission_id": "M2024_TITAN",
"mission_name": "Solar Observatory Research Mission",
"destination": "Solar Observatory",
"launch_date": "2024-03-30T00:00:00",
"duration_days": 451,
"crew": [
{
"member_id": "CM001",
"name": "Sarah Williams",
"rank": "captain",
"age": 43,
"specialization": "Mission Command",
"years_experience": 19,
"is_active": true
},
{
"member_id": "CM002",
"name": "James Hernandez",
"rank": "captain",
"age": 43,
"specialization": "Pilot",
"years_experience": 30,
"is_active": true
},
{
"member_id": "CM003",
"name": "Anna Jones",
"rank": "cadet",
"age": 35,
"specialization": "Communications",
"years_experience": 15,
"is_active": true
},
{
"member_id": "CM004",
"name": "David Smith",
"rank": "commander",
"age": 27,
"specialization": "Security",
"years_experience": 15,
"is_active": true
},
{
"member_id": "CM005",
"name": "Maria Jones",
"rank": "cadet",
"age": 55,
"specialization": "Research",
"years_experience": 30,
"is_active": true
}
],
"mission_status": "planned",
"budget_millions": 2208.1
},
{
"mission_id": "M2024_MARS",
"mission_name": "Jupiter Orbit Colony Mission",
"destination": "Jupiter Orbit",
"launch_date": "2024-10-01T00:00:00",
"duration_days": 1065,
"crew": [
{
"member_id": "CM011",
"name": "Emma Brown",
"rank": "commander",
"age": 49,
"specialization": "Mission Command",
"years_experience": 27,
"is_active": true
},
{
"member_id": "CM012",
"name": "John Hernandez",
"rank": "lieutenant",
"age": 36,
"specialization": "Science Officer",
"years_experience": 22,
"is_active": true
},
{
"member_id": "CM013",
"name": "Sofia Rodriguez",
"rank": "commander",
"age": 29,
"specialization": "Life Support",
"years_experience": 20,
"is_active": true
},
{
"member_id": "CM014",
"name": "Sofia Lopez",
"rank": "lieutenant",
"age": 44,
"specialization": "Systems Analysis",
"years_experience": 25,
"is_active": true
}
],
"mission_status": "planned",
"budget_millions": 4626.0
},
{
"mission_id": "M2024_EUROPA",
"mission_name": "Europa Colony Mission",
"destination": "Europa",
"launch_date": "2024-02-07T00:00:00",
"duration_days": 666,
"crew": [
{
"member_id": "CM021",
"name": "Lisa Garcia",
"rank": "captain",
"age": 36,
"specialization": "Medical Officer",
"years_experience": 12,
"is_active": true
},
{
"member_id": "CM022",
"name": "John Garcia",
"rank": "cadet",
"age": 46,
"specialization": "Security",
"years_experience": 25,
"is_active": true
},
{
"member_id": "CM023",
"name": "Michael Johnson",
"rank": "officer",
"age": 54,
"specialization": "Research",
"years_experience": 30,
"is_active": true
},
{
"member_id": "CM024",
"name": "Sarah Rodriguez",
"rank": "lieutenant",
"age": 54,
"specialization": "Research",
"years_experience": 30,
"is_active": true
},
{
"member_id": "CM025",
"name": "Maria Smith",
"rank": "cadet",
"age": 38,
"specialization": "Communications",
"years_experience": 15,
"is_active": true
}
],
"mission_status": "planned",
"budget_millions": 4976.0
},
{
"mission_id": "M2024_LUNA",
"mission_name": "Mars Colony Mission",
"destination": "Mars",
"launch_date": "2024-06-13T00:00:00",
"duration_days": 222,
"crew": [
{
"member_id": "CM031",
"name": "Anna Davis",
"rank": "commander",
"age": 27,
"specialization": "Communications",
"years_experience": 14,
"is_active": true
},
{
"member_id": "CM032",
"name": "Elena Garcia",
"rank": "lieutenant",
"age": 42,
"specialization": "Science Officer",
"years_experience": 23,
"is_active": true
},
{
"member_id": "CM033",
"name": "Anna Brown",
"rank": "officer",
"age": 55,
"specialization": "Engineering",
"years_experience": 30,
"is_active": true
},
{
"member_id": "CM034",
"name": "Emma Smith",
"rank": "captain",
"age": 37,
"specialization": "Research",
"years_experience": 23,
"is_active": true
},
{
"member_id": "CM035",
"name": "Sofia Smith",
"rank": "lieutenant",
"age": 53,
"specialization": "Security",
"years_experience": 30,
"is_active": true
},
{
"member_id": "CM036",
"name": "Maria Hernandez",
"rank": "commander",
"age": 41,
"specialization": "Medical Officer",
"years_experience": 30,
"is_active": true
},
{
"member_id": "CM037",
"name": "John Hernandez",
"rank": "officer",
"age": 42,
"specialization": "Science Officer",
"years_experience": 20,
"is_active": true
}
],
"mission_status": "planned",
"budget_millions": 4984.6
},
{
"mission_id": "M2024_EUROPA",
"mission_name": "Saturn Rings Research Mission",
"destination": "Saturn Rings",
"launch_date": "2024-09-18T00:00:00",
"duration_days": 602,
"crew": [
{
"member_id": "CM041",
"name": "William Davis",
"rank": "captain",
"age": 35,
"specialization": "Medical Officer",
"years_experience": 14,
"is_active": true
},
{
"member_id": "CM042",
"name": "Sarah Smith",
"rank": "captain",
"age": 55,
"specialization": "Research",
"years_experience": 30,
"is_active": true
},
{
"member_id": "CM043",
"name": "Elena Garcia",
"rank": "commander",
"age": 55,
"specialization": "Research",
"years_experience": 30,
"is_active": true
},
{
"member_id": "CM044",
"name": "Sofia Williams",
"rank": "officer",
"age": 30,
"specialization": "Systems Analysis",
"years_experience": 9,
"is_active": true
},
{
"member_id": "CM045",
"name": "Sarah Jones",
"rank": "lieutenant",
"age": 25,
"specialization": "Maintenance",
"years_experience": 11,
"is_active": true
},
{
"member_id": "CM046",
"name": "Lisa Rodriguez",
"rank": "officer",
"age": 30,
"specialization": "Life Support",
"years_experience": 12,
"is_active": true
},
{
"member_id": "CM047",
"name": "Sarah Smith",
"rank": "cadet",
"age": 28,
"specialization": "Pilot",
"years_experience": 8,
"is_active": true
}
],
"mission_status": "planned",
"budget_millions": 1092.6
}
]

View File

@@ -0,0 +1,313 @@
"""
Generated test data for space_missions
"""
SPACE_MISSIONS = [
{
'mission_id': 'M2024_TITAN',
'mission_name': 'Solar Observatory Research Mission',
'destination': 'Solar Observatory',
'launch_date': '2024-03-30T00:00:00',
'duration_days': 451,
'crew': [
{
'member_id': 'CM001',
'name': 'Sarah Williams',
'rank': 'captain',
'age': 43,
'specialization': 'Mission Command',
'years_experience': 19,
'is_active': True
},
{
'member_id': 'CM002',
'name': 'James Hernandez',
'rank': 'captain',
'age': 43,
'specialization': 'Pilot',
'years_experience': 30,
'is_active': True
},
{
'member_id': 'CM003',
'name': 'Anna Jones',
'rank': 'cadet',
'age': 35,
'specialization': 'Communications',
'years_experience': 15,
'is_active': True
},
{
'member_id': 'CM004',
'name': 'David Smith',
'rank': 'commander',
'age': 27,
'specialization': 'Security',
'years_experience': 15,
'is_active': True
},
{
'member_id': 'CM005',
'name': 'Maria Jones',
'rank': 'cadet',
'age': 55,
'specialization': 'Research',
'years_experience': 30,
'is_active': True
}
],
'mission_status': 'planned',
'budget_millions': 2208.1
},
{
'mission_id': 'M2024_MARS',
'mission_name': 'Jupiter Orbit Colony Mission',
'destination': 'Jupiter Orbit',
'launch_date': '2024-10-01T00:00:00',
'duration_days': 1065,
'crew': [
{
'member_id': 'CM011',
'name': 'Emma Brown',
'rank': 'commander',
'age': 49,
'specialization': 'Mission Command',
'years_experience': 27,
'is_active': True
},
{
'member_id': 'CM012',
'name': 'John Hernandez',
'rank': 'lieutenant',
'age': 36,
'specialization': 'Science Officer',
'years_experience': 22,
'is_active': True
},
{
'member_id': 'CM013',
'name': 'Sofia Rodriguez',
'rank': 'commander',
'age': 29,
'specialization': 'Life Support',
'years_experience': 20,
'is_active': True
},
{
'member_id': 'CM014',
'name': 'Sofia Lopez',
'rank': 'lieutenant',
'age': 44,
'specialization': 'Systems Analysis',
'years_experience': 25,
'is_active': True
}
],
'mission_status': 'planned',
'budget_millions': 4626.0
},
{
'mission_id': 'M2024_EUROPA',
'mission_name': 'Europa Colony Mission',
'destination': 'Europa',
'launch_date': '2024-02-07T00:00:00',
'duration_days': 666,
'crew': [
{
'member_id': 'CM021',
'name': 'Lisa Garcia',
'rank': 'captain',
'age': 36,
'specialization': 'Medical Officer',
'years_experience': 12,
'is_active': True
},
{
'member_id': 'CM022',
'name': 'John Garcia',
'rank': 'cadet',
'age': 46,
'specialization': 'Security',
'years_experience': 25,
'is_active': True
},
{
'member_id': 'CM023',
'name': 'Michael Johnson',
'rank': 'officer',
'age': 54,
'specialization': 'Research',
'years_experience': 30,
'is_active': True
},
{
'member_id': 'CM024',
'name': 'Sarah Rodriguez',
'rank': 'lieutenant',
'age': 54,
'specialization': 'Research',
'years_experience': 30,
'is_active': True
},
{
'member_id': 'CM025',
'name': 'Maria Smith',
'rank': 'cadet',
'age': 38,
'specialization': 'Communications',
'years_experience': 15,
'is_active': True
}
],
'mission_status': 'planned',
'budget_millions': 4976.0
},
{
'mission_id': 'M2024_LUNA',
'mission_name': 'Mars Colony Mission',
'destination': 'Mars',
'launch_date': '2024-06-13T00:00:00',
'duration_days': 222,
'crew': [
{
'member_id': 'CM031',
'name': 'Anna Davis',
'rank': 'commander',
'age': 27,
'specialization': 'Communications',
'years_experience': 14,
'is_active': True
},
{
'member_id': 'CM032',
'name': 'Elena Garcia',
'rank': 'lieutenant',
'age': 42,
'specialization': 'Science Officer',
'years_experience': 23,
'is_active': True
},
{
'member_id': 'CM033',
'name': 'Anna Brown',
'rank': 'officer',
'age': 55,
'specialization': 'Engineering',
'years_experience': 30,
'is_active': True
},
{
'member_id': 'CM034',
'name': 'Emma Smith',
'rank': 'captain',
'age': 37,
'specialization': 'Research',
'years_experience': 23,
'is_active': True
},
{
'member_id': 'CM035',
'name': 'Sofia Smith',
'rank': 'lieutenant',
'age': 53,
'specialization': 'Security',
'years_experience': 30,
'is_active': True
},
{
'member_id': 'CM036',
'name': 'Maria Hernandez',
'rank': 'commander',
'age': 41,
'specialization': 'Medical Officer',
'years_experience': 30,
'is_active': True
},
{
'member_id': 'CM037',
'name': 'John Hernandez',
'rank': 'officer',
'age': 42,
'specialization': 'Science Officer',
'years_experience': 20,
'is_active': True
}
],
'mission_status': 'planned',
'budget_millions': 4984.6
},
{
'mission_id': 'M2024_EUROPA',
'mission_name': 'Saturn Rings Research Mission',
'destination': 'Saturn Rings',
'launch_date': '2024-09-18T00:00:00',
'duration_days': 602,
'crew': [
{
'member_id': 'CM041',
'name': 'William Davis',
'rank': 'captain',
'age': 35,
'specialization': 'Medical Officer',
'years_experience': 14,
'is_active': True
},
{
'member_id': 'CM042',
'name': 'Sarah Smith',
'rank': 'captain',
'age': 55,
'specialization': 'Research',
'years_experience': 30,
'is_active': True
},
{
'member_id': 'CM043',
'name': 'Elena Garcia',
'rank': 'commander',
'age': 55,
'specialization': 'Research',
'years_experience': 30,
'is_active': True
},
{
'member_id': 'CM044',
'name': 'Sofia Williams',
'rank': 'officer',
'age': 30,
'specialization': 'Systems Analysis',
'years_experience': 9,
'is_active': True
},
{
'member_id': 'CM045',
'name': 'Sarah Jones',
'rank': 'lieutenant',
'age': 25,
'specialization': 'Maintenance',
'years_experience': 11,
'is_active': True
},
{
'member_id': 'CM046',
'name': 'Lisa Rodriguez',
'rank': 'officer',
'age': 30,
'specialization': 'Life Support',
'years_experience': 12,
'is_active': True
},
{
'member_id': 'CM047',
'name': 'Sarah Smith',
'rank': 'cadet',
'age': 28,
'specialization': 'Pilot',
'years_experience': 8,
'is_active': True
}
],
'mission_status': 'planned',
'budget_millions': 1092.6
}
]

View File

@@ -0,0 +1,11 @@
crew_size,is_operational,last_maintenance,name,notes,oxygen_level,power_level,station_id
6,True,2023-07-11T00:00:00,Titan Mining Outpost,,95.5,76.4,LGW125
3,False,2023-08-24T00:00:00,Deep Space Observatory,System diagnostics required,88.1,70.8,QCH189
11,True,2023-10-21T00:00:00,Europa Research Station,,91.4,82.0,ISS674
9,False,2023-10-06T00:00:00,Mars Orbital Platform,System diagnostics required,87.2,79.7,ISS877
4,False,2023-10-25T00:00:00,Deep Space Observatory,System diagnostics required,89.9,80.2,LGW194
11,False,2023-12-11T00:00:00,Solar Wind Monitor,System diagnostics required,98.1,73.6,ISS847
12,False,2023-07-15T00:00:00,Asteroid Belt Relay,System diagnostics required,86.0,75.5,QCH400
4,True,2023-09-25T00:00:00,Titan Mining Outpost,All systems nominal,97.3,94.4,ERS891
8,False,2023-07-10T00:00:00,Asteroid Belt Relay,System diagnostics required,88.8,76.0,ABR266
11,False,2023-09-25T00:00:00,Mars Orbital Platform,System diagnostics required,87.3,90.8,LGW723
1 crew_size is_operational last_maintenance name notes oxygen_level power_level station_id
2 6 True 2023-07-11T00:00:00 Titan Mining Outpost 95.5 76.4 LGW125
3 3 False 2023-08-24T00:00:00 Deep Space Observatory System diagnostics required 88.1 70.8 QCH189
4 11 True 2023-10-21T00:00:00 Europa Research Station 91.4 82.0 ISS674
5 9 False 2023-10-06T00:00:00 Mars Orbital Platform System diagnostics required 87.2 79.7 ISS877
6 4 False 2023-10-25T00:00:00 Deep Space Observatory System diagnostics required 89.9 80.2 LGW194
7 11 False 2023-12-11T00:00:00 Solar Wind Monitor System diagnostics required 98.1 73.6 ISS847
8 12 False 2023-07-15T00:00:00 Asteroid Belt Relay System diagnostics required 86.0 75.5 QCH400
9 4 True 2023-09-25T00:00:00 Titan Mining Outpost All systems nominal 97.3 94.4 ERS891
10 8 False 2023-07-10T00:00:00 Asteroid Belt Relay System diagnostics required 88.8 76.0 ABR266
11 11 False 2023-09-25T00:00:00 Mars Orbital Platform System diagnostics required 87.3 90.8 LGW723

View File

@@ -0,0 +1,102 @@
[
{
"station_id": "LGW125",
"name": "Titan Mining Outpost",
"crew_size": 6,
"power_level": 76.4,
"oxygen_level": 95.5,
"last_maintenance": "2023-07-11T00:00:00",
"is_operational": true,
"notes": null
},
{
"station_id": "QCH189",
"name": "Deep Space Observatory",
"crew_size": 3,
"power_level": 70.8,
"oxygen_level": 88.1,
"last_maintenance": "2023-08-24T00:00:00",
"is_operational": false,
"notes": "System diagnostics required"
},
{
"station_id": "ISS674",
"name": "Europa Research Station",
"crew_size": 11,
"power_level": 82.0,
"oxygen_level": 91.4,
"last_maintenance": "2023-10-21T00:00:00",
"is_operational": true,
"notes": null
},
{
"station_id": "ISS877",
"name": "Mars Orbital Platform",
"crew_size": 9,
"power_level": 79.7,
"oxygen_level": 87.2,
"last_maintenance": "2023-10-06T00:00:00",
"is_operational": false,
"notes": "System diagnostics required"
},
{
"station_id": "LGW194",
"name": "Deep Space Observatory",
"crew_size": 4,
"power_level": 80.2,
"oxygen_level": 89.9,
"last_maintenance": "2023-10-25T00:00:00",
"is_operational": false,
"notes": "System diagnostics required"
},
{
"station_id": "ISS847",
"name": "Solar Wind Monitor",
"crew_size": 11,
"power_level": 73.6,
"oxygen_level": 98.1,
"last_maintenance": "2023-12-11T00:00:00",
"is_operational": false,
"notes": "System diagnostics required"
},
{
"station_id": "QCH400",
"name": "Asteroid Belt Relay",
"crew_size": 12,
"power_level": 75.5,
"oxygen_level": 86.0,
"last_maintenance": "2023-07-15T00:00:00",
"is_operational": false,
"notes": "System diagnostics required"
},
{
"station_id": "ERS891",
"name": "Titan Mining Outpost",
"crew_size": 4,
"power_level": 94.4,
"oxygen_level": 97.3,
"last_maintenance": "2023-09-25T00:00:00",
"is_operational": true,
"notes": "All systems nominal"
},
{
"station_id": "ABR266",
"name": "Asteroid Belt Relay",
"crew_size": 8,
"power_level": 76.0,
"oxygen_level": 88.8,
"last_maintenance": "2023-07-10T00:00:00",
"is_operational": false,
"notes": "System diagnostics required"
},
{
"station_id": "LGW723",
"name": "Mars Orbital Platform",
"crew_size": 11,
"power_level": 90.8,
"oxygen_level": 87.3,
"last_maintenance": "2023-09-25T00:00:00",
"is_operational": false,
"notes": "System diagnostics required"
}
]

View File

@@ -0,0 +1,106 @@
"""
Generated test data for space_stations
"""
SPACE_STATIONS = [
{
'station_id': 'LGW125',
'name': 'Titan Mining Outpost',
'crew_size': 6,
'power_level': 76.4,
'oxygen_level': 95.5,
'last_maintenance': '2023-07-11T00:00:00',
'is_operational': True,
'notes': None
},
{
'station_id': 'QCH189',
'name': 'Deep Space Observatory',
'crew_size': 3,
'power_level': 70.8,
'oxygen_level': 88.1,
'last_maintenance': '2023-08-24T00:00:00',
'is_operational': False,
'notes': 'System diagnostics required'
},
{
'station_id': 'ISS674',
'name': 'Europa Research Station',
'crew_size': 11,
'power_level': 82.0,
'oxygen_level': 91.4,
'last_maintenance': '2023-10-21T00:00:00',
'is_operational': True,
'notes': None
},
{
'station_id': 'ISS877',
'name': 'Mars Orbital Platform',
'crew_size': 9,
'power_level': 79.7,
'oxygen_level': 87.2,
'last_maintenance': '2023-10-06T00:00:00',
'is_operational': False,
'notes': 'System diagnostics required'
},
{
'station_id': 'LGW194',
'name': 'Deep Space Observatory',
'crew_size': 4,
'power_level': 80.2,
'oxygen_level': 89.9,
'last_maintenance': '2023-10-25T00:00:00',
'is_operational': False,
'notes': 'System diagnostics required'
},
{
'station_id': 'ISS847',
'name': 'Solar Wind Monitor',
'crew_size': 11,
'power_level': 73.6,
'oxygen_level': 98.1,
'last_maintenance': '2023-12-11T00:00:00',
'is_operational': False,
'notes': 'System diagnostics required'
},
{
'station_id': 'QCH400',
'name': 'Asteroid Belt Relay',
'crew_size': 12,
'power_level': 75.5,
'oxygen_level': 86.0,
'last_maintenance': '2023-07-15T00:00:00',
'is_operational': False,
'notes': 'System diagnostics required'
},
{
'station_id': 'ERS891',
'name': 'Titan Mining Outpost',
'crew_size': 4,
'power_level': 94.4,
'oxygen_level': 97.3,
'last_maintenance': '2023-09-25T00:00:00',
'is_operational': True,
'notes': 'All systems nominal'
},
{
'station_id': 'ABR266',
'name': 'Asteroid Belt Relay',
'crew_size': 8,
'power_level': 76.0,
'oxygen_level': 88.8,
'last_maintenance': '2023-07-10T00:00:00',
'is_operational': False,
'notes': 'System diagnostics required'
},
{
'station_id': 'LGW723',
'name': 'Mars Orbital Platform',
'crew_size': 11,
'power_level': 90.8,
'oxygen_level': 87.3,
'last_maintenance': '2023-09-25T00:00:00',
'is_operational': False,
'notes': 'System diagnostics required'
}
]

14
10/ex0/lambda_spells.py Normal file
View File

@@ -0,0 +1,14 @@
def artifact_sorter(artifacts: list[dict]) -> list[dict]:
return sorted(artifacts, key=lambda artifact: artifact["power"])
def power_filter(mages: list[dict], min_power: int) -> list[dict]:
return list(filter(lambda x: x["power"] >= min_power, mages))
def spell_transformer(spells: list[str]) -> list[str]:
pass
def mage_stats(mages: list[dict]) -> dict:
pass

View File

@@ -1,160 +0,0 @@
from abc import ABC, abstractmethod
from typing import Any
from typing_extensions import override
class DataProcessor(ABC):
@abstractmethod
def process(self, data: Any) -> str:
pass
@abstractmethod
def validate(self, data: Any) -> bool:
pass
def format_output(self, result: str) -> str:
return f"Processed output: {result}"
class NumericProcessor(DataProcessor):
def process(self, data: Any) -> str:
try:
return f"{data}"
except Exception as err:
print(err)
return "error"
def validate(self, data: Any) -> bool:
try:
for n in data:
if type(n).__name__ != "int":
return False
return True
except Exception:
return False
@override
def format_output(self, result: str) -> str:
try:
to_processed = result.split(", ")
res_int: list[int] = []
for n in to_processed:
n = n.replace("[", "").replace("]", "")
if n.isnumeric():
res_int = res_int + [int(n)]
avg = sum(res_int) / len(res_int)
return f"Processed {len(res_int)} numeric value,\
sum={sum(res_int)}, avg={avg:.2f}"
except Exception as err:
print(f"NumericProcessor / format_output: {err}")
return "error"
class TextProcessor(DataProcessor):
def process(self, data: Any) -> str:
return str(data)
def validate(self, data: Any) -> bool:
try:
for char in data:
if type(char).__name__ != "str":
return False
return True
except Exception:
return False
@override
def format_output(self, result: str) -> str:
return f"Processed text: {len(result)}\
characters, {len(result.split(' '))} words"
class LogProcessor(DataProcessor):
def process(self, data: Any) -> str:
try:
return f"{data[0]}: {data[1]}"
except Exception as err:
print(err)
return "error"
def validate(self, data: Any) -> bool:
try:
if len(data) == 2:
for n in data:
if type(n).__name__ != "str":
return False
return True
return False
except Exception:
return False
@override
def format_output(self, result: str) -> str:
try:
log_res = result.split(":")[0]
if log_res == "ERROR":
header = "[ALERT]"
elif log_res == "INFO":
header = "[INFO]"
elif log_res == "SUCCESS":
header = "[SUCCESS]"
else:
header = "[UNKNOWN]"
return (
f"{header} {result.split(':', 1)[0]}{result.split(':', 1)[1]}"
)
except Exception as err:
print(err)
return "error"
def class_tester() -> None:
print("=== CODE NEXUS - DATA PROCESSOR FOUNDATION ===\n")
print("Initializing Numeric Processor...")
data = [1, 2, 3, 4, 5]
processor = NumericProcessor()
res = processor.process(data)
is_valid = processor.validate(data)
formatted_output = processor.format_output(res)
print(f"processing data: {res}")
if is_valid:
print("Validation: Numeric data verified")
else:
print("Validation: Invalid numeric data")
print(f"Output: {formatted_output}")
print("\nInitializing Text Processor...")
data = "Hello Nexus World"
processor = TextProcessor()
res = processor.process(data)
is_valid = processor.validate(data)
formatted_output = processor.format_output(res)
print(f"processing data: {res}")
if is_valid:
print("Validation: Text data verified")
else:
print("Validation: Invalid text data")
print(f"Output: {formatted_output}")
print("\nInitializing Log Processor...")
data = ["ERROR", "Connection timeout"]
processor = LogProcessor()
res = processor.process(data)
is_valid = processor.validate(data)
formatted_output = processor.format_output(res)
print(f"processing data: {res}")
if is_valid:
print("Validation: Log data verified")
else:
print("Validation: Invalid log data")
print(f"Output: {formatted_output}")
print("\n=== Polymorphic Processing Demo ===\n")
print(f"Result 1:\
{NumericProcessor().format_output('[1, 2, 3]')}")
print(f"Result 2:\
{TextProcessor().format_output('Hello World !')}")
print(f"Result 3:\
{LogProcessor().format_output('INFO: level detected: System ready')}")
print("\nFoundation systems online. Nexus ready for advanced streams.")
if __name__ == "__main__":
class_tester()