unrealon 1.0.9__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- unrealon/__init__.py +23 -21
- unrealon-1.1.0.dist-info/METADATA +164 -0
- unrealon-1.1.0.dist-info/RECORD +82 -0
- {unrealon-1.0.9.dist-info → unrealon-1.1.0.dist-info}/WHEEL +1 -1
- unrealon-1.1.0.dist-info/entry_points.txt +9 -0
- {unrealon-1.0.9.dist-info → unrealon-1.1.0.dist-info/licenses}/LICENSE +1 -1
- unrealon_bridge/__init__.py +114 -0
- unrealon_bridge/cli.py +316 -0
- unrealon_bridge/client/__init__.py +93 -0
- unrealon_bridge/client/base.py +78 -0
- unrealon_bridge/client/commands.py +89 -0
- unrealon_bridge/client/connection.py +90 -0
- unrealon_bridge/client/events.py +65 -0
- unrealon_bridge/client/health.py +38 -0
- unrealon_bridge/client/html_parser.py +146 -0
- unrealon_bridge/client/logging.py +139 -0
- unrealon_bridge/client/proxy.py +70 -0
- unrealon_bridge/client/scheduler.py +450 -0
- unrealon_bridge/client/session.py +70 -0
- unrealon_bridge/configs/__init__.py +14 -0
- unrealon_bridge/configs/bridge_config.py +212 -0
- unrealon_bridge/configs/bridge_config.yaml +39 -0
- unrealon_bridge/models/__init__.py +138 -0
- unrealon_bridge/models/base.py +28 -0
- unrealon_bridge/models/command.py +41 -0
- unrealon_bridge/models/events.py +40 -0
- unrealon_bridge/models/html_parser.py +79 -0
- unrealon_bridge/models/logging.py +55 -0
- unrealon_bridge/models/parser.py +63 -0
- unrealon_bridge/models/proxy.py +41 -0
- unrealon_bridge/models/requests.py +95 -0
- unrealon_bridge/models/responses.py +88 -0
- unrealon_bridge/models/scheduler.py +592 -0
- unrealon_bridge/models/session.py +28 -0
- unrealon_bridge/server/__init__.py +91 -0
- unrealon_bridge/server/base.py +171 -0
- unrealon_bridge/server/handlers/__init__.py +23 -0
- unrealon_bridge/server/handlers/command.py +110 -0
- unrealon_bridge/server/handlers/html_parser.py +139 -0
- unrealon_bridge/server/handlers/logging.py +95 -0
- unrealon_bridge/server/handlers/parser.py +95 -0
- unrealon_bridge/server/handlers/proxy.py +75 -0
- unrealon_bridge/server/handlers/scheduler.py +545 -0
- unrealon_bridge/server/handlers/session.py +66 -0
- unrealon_browser/__init__.py +61 -18
- unrealon_browser/{src/cli → cli}/browser_cli.py +6 -13
- unrealon_browser/{src/cli → cli}/cookies_cli.py +5 -1
- unrealon_browser/{src/core → core}/browser_manager.py +2 -2
- unrealon_browser/{src/managers → managers}/captcha.py +1 -1
- unrealon_browser/{src/managers → managers}/cookies.py +1 -1
- unrealon_browser/managers/logger_bridge.py +231 -0
- unrealon_browser/{src/managers → managers}/profile.py +1 -1
- unrealon_driver/__init__.py +73 -19
- unrealon_driver/browser/__init__.py +8 -0
- unrealon_driver/browser/config.py +74 -0
- unrealon_driver/browser/manager.py +416 -0
- unrealon_driver/exceptions.py +28 -0
- unrealon_driver/parser/__init__.py +55 -0
- unrealon_driver/parser/cli_manager.py +141 -0
- unrealon_driver/parser/daemon_manager.py +227 -0
- unrealon_driver/parser/managers/__init__.py +46 -0
- unrealon_driver/parser/managers/browser.py +51 -0
- unrealon_driver/parser/managers/config.py +281 -0
- unrealon_driver/parser/managers/error.py +412 -0
- unrealon_driver/parser/managers/html.py +732 -0
- unrealon_driver/parser/managers/logging.py +609 -0
- unrealon_driver/parser/managers/result.py +321 -0
- unrealon_driver/parser/parser_manager.py +628 -0
- unrealon/sdk_config.py +0 -88
- unrealon-1.0.9.dist-info/METADATA +0 -810
- unrealon-1.0.9.dist-info/RECORD +0 -246
- unrealon_browser/pyproject.toml +0 -182
- unrealon_browser/src/__init__.py +0 -62
- unrealon_browser/src/managers/logger_bridge.py +0 -395
- unrealon_driver/README.md +0 -204
- unrealon_driver/pyproject.toml +0 -187
- unrealon_driver/src/__init__.py +0 -90
- unrealon_driver/src/cli/__init__.py +0 -10
- unrealon_driver/src/cli/main.py +0 -66
- unrealon_driver/src/cli/simple.py +0 -510
- unrealon_driver/src/config/__init__.py +0 -11
- unrealon_driver/src/config/auto_config.py +0 -478
- unrealon_driver/src/core/__init__.py +0 -18
- unrealon_driver/src/core/exceptions.py +0 -289
- unrealon_driver/src/core/parser.py +0 -638
- unrealon_driver/src/dto/__init__.py +0 -66
- unrealon_driver/src/dto/cli.py +0 -119
- unrealon_driver/src/dto/config.py +0 -18
- unrealon_driver/src/dto/events.py +0 -237
- unrealon_driver/src/dto/execution.py +0 -313
- unrealon_driver/src/dto/services.py +0 -311
- unrealon_driver/src/execution/__init__.py +0 -23
- unrealon_driver/src/execution/daemon_mode.py +0 -317
- unrealon_driver/src/execution/interactive_mode.py +0 -88
- unrealon_driver/src/execution/modes.py +0 -45
- unrealon_driver/src/execution/scheduled_mode.py +0 -209
- unrealon_driver/src/execution/test_mode.py +0 -250
- unrealon_driver/src/logging/__init__.py +0 -24
- unrealon_driver/src/logging/driver_logger.py +0 -512
- unrealon_driver/src/services/__init__.py +0 -24
- unrealon_driver/src/services/browser_service.py +0 -726
- unrealon_driver/src/services/llm/__init__.py +0 -15
- unrealon_driver/src/services/llm/browser_llm_service.py +0 -363
- unrealon_driver/src/services/llm/llm.py +0 -195
- unrealon_driver/src/services/logger_service.py +0 -232
- unrealon_driver/src/services/metrics_service.py +0 -185
- unrealon_driver/src/services/scheduler_service.py +0 -489
- unrealon_driver/src/services/websocket_service.py +0 -362
- unrealon_driver/src/utils/__init__.py +0 -16
- unrealon_driver/src/utils/service_factory.py +0 -317
- unrealon_driver/src/utils/time_formatter.py +0 -338
- unrealon_llm/README.md +0 -44
- unrealon_llm/__init__.py +0 -26
- unrealon_llm/pyproject.toml +0 -154
- unrealon_llm/src/__init__.py +0 -228
- unrealon_llm/src/cli/__init__.py +0 -0
- unrealon_llm/src/core/__init__.py +0 -11
- unrealon_llm/src/core/smart_client.py +0 -438
- unrealon_llm/src/dto/__init__.py +0 -155
- unrealon_llm/src/dto/models/__init__.py +0 -0
- unrealon_llm/src/dto/models/config.py +0 -343
- unrealon_llm/src/dto/models/core.py +0 -328
- unrealon_llm/src/dto/models/enums.py +0 -123
- unrealon_llm/src/dto/models/html_analysis.py +0 -345
- unrealon_llm/src/dto/models/statistics.py +0 -473
- unrealon_llm/src/dto/models/translation.py +0 -383
- unrealon_llm/src/dto/models/type_conversion.py +0 -462
- unrealon_llm/src/dto/schemas/__init__.py +0 -0
- unrealon_llm/src/exceptions.py +0 -392
- unrealon_llm/src/llm_config/__init__.py +0 -20
- unrealon_llm/src/llm_config/logging_config.py +0 -178
- unrealon_llm/src/llm_logging/__init__.py +0 -42
- unrealon_llm/src/llm_logging/llm_events.py +0 -107
- unrealon_llm/src/llm_logging/llm_logger.py +0 -466
- unrealon_llm/src/managers/__init__.py +0 -15
- unrealon_llm/src/managers/cache_manager.py +0 -67
- unrealon_llm/src/managers/cost_manager.py +0 -107
- unrealon_llm/src/managers/request_manager.py +0 -298
- unrealon_llm/src/modules/__init__.py +0 -0
- unrealon_llm/src/modules/html_processor/__init__.py +0 -25
- unrealon_llm/src/modules/html_processor/base_processor.py +0 -415
- unrealon_llm/src/modules/html_processor/details_processor.py +0 -85
- unrealon_llm/src/modules/html_processor/listing_processor.py +0 -91
- unrealon_llm/src/modules/html_processor/models/__init__.py +0 -20
- unrealon_llm/src/modules/html_processor/models/processing_models.py +0 -40
- unrealon_llm/src/modules/html_processor/models/universal_model.py +0 -56
- unrealon_llm/src/modules/html_processor/processor.py +0 -102
- unrealon_llm/src/modules/llm/__init__.py +0 -0
- unrealon_llm/src/modules/translator/__init__.py +0 -0
- unrealon_llm/src/provider.py +0 -116
- unrealon_llm/src/utils/__init__.py +0 -95
- unrealon_llm/src/utils/common.py +0 -64
- unrealon_llm/src/utils/data_extractor.py +0 -188
- unrealon_llm/src/utils/html_cleaner.py +0 -767
- unrealon_llm/src/utils/language_detector.py +0 -308
- unrealon_llm/src/utils/models_cache.py +0 -592
- unrealon_llm/src/utils/smart_counter.py +0 -229
- unrealon_llm/src/utils/token_counter.py +0 -189
- unrealon_sdk/README.md +0 -25
- unrealon_sdk/__init__.py +0 -30
- unrealon_sdk/pyproject.toml +0 -231
- unrealon_sdk/src/__init__.py +0 -150
- unrealon_sdk/src/cli/__init__.py +0 -12
- unrealon_sdk/src/cli/commands/__init__.py +0 -22
- unrealon_sdk/src/cli/commands/benchmark.py +0 -42
- unrealon_sdk/src/cli/commands/diagnostics.py +0 -573
- unrealon_sdk/src/cli/commands/health.py +0 -46
- unrealon_sdk/src/cli/commands/integration.py +0 -498
- unrealon_sdk/src/cli/commands/reports.py +0 -43
- unrealon_sdk/src/cli/commands/security.py +0 -36
- unrealon_sdk/src/cli/commands/server.py +0 -483
- unrealon_sdk/src/cli/commands/servers.py +0 -56
- unrealon_sdk/src/cli/commands/tests.py +0 -55
- unrealon_sdk/src/cli/main.py +0 -126
- unrealon_sdk/src/cli/utils/reporter.py +0 -519
- unrealon_sdk/src/clients/openapi.yaml +0 -3347
- unrealon_sdk/src/clients/python_http/__init__.py +0 -3
- unrealon_sdk/src/clients/python_http/api_config.py +0 -228
- unrealon_sdk/src/clients/python_http/models/BaseModel.py +0 -12
- unrealon_sdk/src/clients/python_http/models/BroadcastDeliveryStats.py +0 -33
- unrealon_sdk/src/clients/python_http/models/BroadcastMessage.py +0 -17
- unrealon_sdk/src/clients/python_http/models/BroadcastMessageRequest.py +0 -35
- unrealon_sdk/src/clients/python_http/models/BroadcastPriority.py +0 -10
- unrealon_sdk/src/clients/python_http/models/BroadcastResponse.py +0 -21
- unrealon_sdk/src/clients/python_http/models/BroadcastResultResponse.py +0 -33
- unrealon_sdk/src/clients/python_http/models/BroadcastTarget.py +0 -11
- unrealon_sdk/src/clients/python_http/models/ConnectionStats.py +0 -27
- unrealon_sdk/src/clients/python_http/models/ConnectionsResponse.py +0 -21
- unrealon_sdk/src/clients/python_http/models/DeveloperMessageResponse.py +0 -23
- unrealon_sdk/src/clients/python_http/models/ErrorResponse.py +0 -25
- unrealon_sdk/src/clients/python_http/models/HTTPValidationError.py +0 -16
- unrealon_sdk/src/clients/python_http/models/HealthResponse.py +0 -23
- unrealon_sdk/src/clients/python_http/models/HealthStatus.py +0 -33
- unrealon_sdk/src/clients/python_http/models/LogLevel.py +0 -10
- unrealon_sdk/src/clients/python_http/models/LoggingRequest.py +0 -27
- unrealon_sdk/src/clients/python_http/models/LoggingResponse.py +0 -23
- unrealon_sdk/src/clients/python_http/models/MaintenanceMode.py +0 -9
- unrealon_sdk/src/clients/python_http/models/MaintenanceModeRequest.py +0 -33
- unrealon_sdk/src/clients/python_http/models/MaintenanceStatusResponse.py +0 -39
- unrealon_sdk/src/clients/python_http/models/ParserCommandRequest.py +0 -25
- unrealon_sdk/src/clients/python_http/models/ParserMessageResponse.py +0 -21
- unrealon_sdk/src/clients/python_http/models/ParserRegistrationRequest.py +0 -28
- unrealon_sdk/src/clients/python_http/models/ParserRegistrationResponse.py +0 -25
- unrealon_sdk/src/clients/python_http/models/ParserType.py +0 -10
- unrealon_sdk/src/clients/python_http/models/ProxyBlockRequest.py +0 -19
- unrealon_sdk/src/clients/python_http/models/ProxyEndpointResponse.py +0 -20
- unrealon_sdk/src/clients/python_http/models/ProxyListResponse.py +0 -19
- unrealon_sdk/src/clients/python_http/models/ProxyProvider.py +0 -10
- unrealon_sdk/src/clients/python_http/models/ProxyPurchaseRequest.py +0 -25
- unrealon_sdk/src/clients/python_http/models/ProxyResponse.py +0 -47
- unrealon_sdk/src/clients/python_http/models/ProxyRotationRequest.py +0 -23
- unrealon_sdk/src/clients/python_http/models/ProxyStatus.py +0 -10
- unrealon_sdk/src/clients/python_http/models/ProxyUsageRequest.py +0 -19
- unrealon_sdk/src/clients/python_http/models/ProxyUsageStatsResponse.py +0 -26
- unrealon_sdk/src/clients/python_http/models/ServiceRegistrationDto.py +0 -23
- unrealon_sdk/src/clients/python_http/models/ServiceStatsResponse.py +0 -31
- unrealon_sdk/src/clients/python_http/models/SessionStartRequest.py +0 -23
- unrealon_sdk/src/clients/python_http/models/SuccessResponse.py +0 -25
- unrealon_sdk/src/clients/python_http/models/SystemNotificationResponse.py +0 -23
- unrealon_sdk/src/clients/python_http/models/ValidationError.py +0 -18
- unrealon_sdk/src/clients/python_http/models/ValidationErrorResponse.py +0 -21
- unrealon_sdk/src/clients/python_http/models/WebSocketMetrics.py +0 -21
- unrealon_sdk/src/clients/python_http/models/__init__.py +0 -44
- unrealon_sdk/src/clients/python_http/services/None_service.py +0 -35
- unrealon_sdk/src/clients/python_http/services/ParserManagement_service.py +0 -190
- unrealon_sdk/src/clients/python_http/services/ProxyManagement_service.py +0 -289
- unrealon_sdk/src/clients/python_http/services/SocketLogging_service.py +0 -187
- unrealon_sdk/src/clients/python_http/services/SystemHealth_service.py +0 -119
- unrealon_sdk/src/clients/python_http/services/WebSocketAPI_service.py +0 -198
- unrealon_sdk/src/clients/python_http/services/__init__.py +0 -0
- unrealon_sdk/src/clients/python_http/services/admin_service.py +0 -125
- unrealon_sdk/src/clients/python_http/services/async_None_service.py +0 -35
- unrealon_sdk/src/clients/python_http/services/async_ParserManagement_service.py +0 -190
- unrealon_sdk/src/clients/python_http/services/async_ProxyManagement_service.py +0 -289
- unrealon_sdk/src/clients/python_http/services/async_SocketLogging_service.py +0 -189
- unrealon_sdk/src/clients/python_http/services/async_SystemHealth_service.py +0 -123
- unrealon_sdk/src/clients/python_http/services/async_WebSocketAPI_service.py +0 -200
- unrealon_sdk/src/clients/python_http/services/async_admin_service.py +0 -125
- unrealon_sdk/src/clients/python_websocket/__init__.py +0 -28
- unrealon_sdk/src/clients/python_websocket/client.py +0 -490
- unrealon_sdk/src/clients/python_websocket/events.py +0 -732
- unrealon_sdk/src/clients/python_websocket/example.py +0 -136
- unrealon_sdk/src/clients/python_websocket/types.py +0 -871
- unrealon_sdk/src/core/__init__.py +0 -64
- unrealon_sdk/src/core/client.py +0 -556
- unrealon_sdk/src/core/config.py +0 -465
- unrealon_sdk/src/core/exceptions.py +0 -239
- unrealon_sdk/src/core/metadata.py +0 -191
- unrealon_sdk/src/core/models.py +0 -142
- unrealon_sdk/src/core/types.py +0 -68
- unrealon_sdk/src/dto/__init__.py +0 -268
- unrealon_sdk/src/dto/authentication.py +0 -108
- unrealon_sdk/src/dto/cache.py +0 -208
- unrealon_sdk/src/dto/common.py +0 -19
- unrealon_sdk/src/dto/concurrency.py +0 -393
- unrealon_sdk/src/dto/events.py +0 -108
- unrealon_sdk/src/dto/health.py +0 -339
- unrealon_sdk/src/dto/load_balancing.py +0 -336
- unrealon_sdk/src/dto/logging.py +0 -230
- unrealon_sdk/src/dto/performance.py +0 -165
- unrealon_sdk/src/dto/rate_limiting.py +0 -295
- unrealon_sdk/src/dto/resource_pooling.py +0 -128
- unrealon_sdk/src/dto/structured_logging.py +0 -112
- unrealon_sdk/src/dto/task_scheduling.py +0 -121
- unrealon_sdk/src/dto/websocket.py +0 -55
- unrealon_sdk/src/enterprise/__init__.py +0 -59
- unrealon_sdk/src/enterprise/authentication.py +0 -401
- unrealon_sdk/src/enterprise/cache_manager.py +0 -578
- unrealon_sdk/src/enterprise/error_recovery.py +0 -494
- unrealon_sdk/src/enterprise/event_system.py +0 -549
- unrealon_sdk/src/enterprise/health_monitor.py +0 -747
- unrealon_sdk/src/enterprise/load_balancer.py +0 -964
- unrealon_sdk/src/enterprise/logging/__init__.py +0 -68
- unrealon_sdk/src/enterprise/logging/cleanup.py +0 -156
- unrealon_sdk/src/enterprise/logging/development.py +0 -744
- unrealon_sdk/src/enterprise/logging/service.py +0 -410
- unrealon_sdk/src/enterprise/multithreading_manager.py +0 -853
- unrealon_sdk/src/enterprise/performance_monitor.py +0 -539
- unrealon_sdk/src/enterprise/proxy_manager.py +0 -696
- unrealon_sdk/src/enterprise/rate_limiter.py +0 -652
- unrealon_sdk/src/enterprise/resource_pool.py +0 -763
- unrealon_sdk/src/enterprise/task_scheduler.py +0 -709
- unrealon_sdk/src/internal/__init__.py +0 -10
- unrealon_sdk/src/internal/command_router.py +0 -497
- unrealon_sdk/src/internal/connection_manager.py +0 -397
- unrealon_sdk/src/internal/http_client.py +0 -446
- unrealon_sdk/src/internal/websocket_client.py +0 -420
- unrealon_sdk/src/provider.py +0 -471
- unrealon_sdk/src/utils.py +0 -234
- /unrealon_browser/{src/cli → cli}/__init__.py +0 -0
- /unrealon_browser/{src/cli → cli}/interactive_mode.py +0 -0
- /unrealon_browser/{src/cli → cli}/main.py +0 -0
- /unrealon_browser/{src/core → core}/__init__.py +0 -0
- /unrealon_browser/{src/dto → dto}/__init__.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/config.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/core.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/dataclasses.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/detection.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/enums.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/statistics.py +0 -0
- /unrealon_browser/{src/managers → managers}/__init__.py +0 -0
- /unrealon_browser/{src/managers → managers}/stealth.py +0 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Result Manager - Automatic result tracking and metrics with Pydantic v2
|
|
3
|
+
|
|
4
|
+
Strict compliance with CRITICAL_REQUIREMENTS.md:
|
|
5
|
+
- No Dict[str, Any] usage
|
|
6
|
+
- Complete type annotations
|
|
7
|
+
- Pydantic v2 models everywhere
|
|
8
|
+
- Custom exception hierarchy
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Optional, List, TypeVar, Generic
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from pydantic import BaseModel, Field, ConfigDict, field_validator
|
|
14
|
+
from enum import Enum
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
T = TypeVar('T', bound=BaseModel)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class OperationStatus(str, Enum):
|
|
21
|
+
"""Operation status enumeration"""
|
|
22
|
+
PENDING = "pending"
|
|
23
|
+
RUNNING = "running"
|
|
24
|
+
COMPLETED = "completed"
|
|
25
|
+
FAILED = "failed"
|
|
26
|
+
CANCELLED = "cancelled"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ParseMetrics(BaseModel):
|
|
30
|
+
"""Parse operation metrics with strict typing"""
|
|
31
|
+
model_config = ConfigDict(
|
|
32
|
+
validate_assignment=True,
|
|
33
|
+
extra="forbid"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
started_at: datetime = Field(
|
|
37
|
+
default_factory=datetime.now,
|
|
38
|
+
description="Operation start time"
|
|
39
|
+
)
|
|
40
|
+
completed_at: Optional[datetime] = Field(
|
|
41
|
+
default=None,
|
|
42
|
+
description="Operation completion time"
|
|
43
|
+
)
|
|
44
|
+
duration_seconds: Optional[float] = Field(
|
|
45
|
+
default=None,
|
|
46
|
+
ge=0.0,
|
|
47
|
+
description="Operation duration in seconds"
|
|
48
|
+
)
|
|
49
|
+
pages_processed: int = Field(
|
|
50
|
+
default=0,
|
|
51
|
+
ge=0,
|
|
52
|
+
description="Number of pages processed"
|
|
53
|
+
)
|
|
54
|
+
items_found: int = Field(
|
|
55
|
+
default=0,
|
|
56
|
+
ge=0,
|
|
57
|
+
description="Number of items found"
|
|
58
|
+
)
|
|
59
|
+
errors_count: int = Field(
|
|
60
|
+
default=0,
|
|
61
|
+
ge=0,
|
|
62
|
+
description="Number of errors encountered"
|
|
63
|
+
)
|
|
64
|
+
warnings_count: int = Field(
|
|
65
|
+
default=0,
|
|
66
|
+
ge=0,
|
|
67
|
+
description="Number of warnings encountered"
|
|
68
|
+
)
|
|
69
|
+
status: OperationStatus = Field(
|
|
70
|
+
default=OperationStatus.PENDING,
|
|
71
|
+
description="Current operation status"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def complete(self, status: OperationStatus = OperationStatus.COMPLETED) -> None:
|
|
75
|
+
"""Mark operation as completed"""
|
|
76
|
+
self.completed_at = datetime.now()
|
|
77
|
+
if self.completed_at and self.started_at:
|
|
78
|
+
self.duration_seconds = (self.completed_at - self.started_at).total_seconds()
|
|
79
|
+
self.status = status
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class ParseResult(BaseModel, Generic[T]):
|
|
83
|
+
"""Generic parse result with metrics and strict typing"""
|
|
84
|
+
model_config = ConfigDict(
|
|
85
|
+
validate_assignment=True,
|
|
86
|
+
extra="forbid"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
data: List[T] = Field(
|
|
90
|
+
default_factory=list,
|
|
91
|
+
description="Parsed data items"
|
|
92
|
+
)
|
|
93
|
+
metrics: ParseMetrics = Field(
|
|
94
|
+
default_factory=ParseMetrics,
|
|
95
|
+
description="Operation metrics"
|
|
96
|
+
)
|
|
97
|
+
source_urls: List[str] = Field(
|
|
98
|
+
default_factory=list,
|
|
99
|
+
description="Source URLs processed"
|
|
100
|
+
)
|
|
101
|
+
parser_id: str = Field(
|
|
102
|
+
...,
|
|
103
|
+
min_length=1,
|
|
104
|
+
description="Parser identifier"
|
|
105
|
+
)
|
|
106
|
+
error_message: Optional[str] = Field(
|
|
107
|
+
default=None,
|
|
108
|
+
description="Error message if operation failed"
|
|
109
|
+
)
|
|
110
|
+
warnings: List[str] = Field(
|
|
111
|
+
default_factory=list,
|
|
112
|
+
description="Warning messages"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
@field_validator('source_urls')
|
|
116
|
+
@classmethod
|
|
117
|
+
def validate_urls(cls, v: List[str]) -> List[str]:
|
|
118
|
+
"""Validate URL format"""
|
|
119
|
+
for url in v:
|
|
120
|
+
if not url.startswith(('http://', 'https://')):
|
|
121
|
+
raise ValueError(f"Invalid URL format: {url}")
|
|
122
|
+
return v
|
|
123
|
+
|
|
124
|
+
def model_post_init(self, __context) -> None:
|
|
125
|
+
"""Update metrics after initialization"""
|
|
126
|
+
self.metrics.items_found = len(self.data)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class ResultManagerError(Exception):
|
|
130
|
+
"""Base exception for result manager errors"""
|
|
131
|
+
def __init__(self, message: str, operation: str, details: Optional[dict[str, str]] = None):
|
|
132
|
+
self.message = message
|
|
133
|
+
self.operation = operation
|
|
134
|
+
self.details = details or {}
|
|
135
|
+
super().__init__(message)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class OperationNotFoundError(ResultManagerError):
|
|
139
|
+
"""Raised when operation is not found"""
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class InvalidOperationStateError(ResultManagerError):
|
|
144
|
+
"""Raised when operation is in invalid state"""
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class ResultManager:
|
|
149
|
+
"""
|
|
150
|
+
🎯 Result Manager - Automatic result tracking and metrics
|
|
151
|
+
|
|
152
|
+
Features:
|
|
153
|
+
- Automatic timing and metrics
|
|
154
|
+
- Result aggregation
|
|
155
|
+
- Error tracking
|
|
156
|
+
- Performance monitoring
|
|
157
|
+
- Type-safe operations
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
def __init__(self, parser_id: str):
|
|
161
|
+
if not parser_id.strip():
|
|
162
|
+
raise ValueError("Parser ID cannot be empty")
|
|
163
|
+
|
|
164
|
+
self.parser_id: str = parser_id
|
|
165
|
+
self._current_metrics: Optional[ParseMetrics] = None
|
|
166
|
+
self._results_history: List[ParseResult] = []
|
|
167
|
+
|
|
168
|
+
def start_operation(self) -> ParseMetrics:
|
|
169
|
+
"""Start tracking a new parse operation"""
|
|
170
|
+
if self._current_metrics and self._current_metrics.status == OperationStatus.RUNNING:
|
|
171
|
+
raise InvalidOperationStateError(
|
|
172
|
+
message="Cannot start new operation while another is running",
|
|
173
|
+
operation="start_operation",
|
|
174
|
+
details={"current_status": self._current_metrics.status.value}
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
self._current_metrics = ParseMetrics(status=OperationStatus.RUNNING)
|
|
178
|
+
return self._current_metrics
|
|
179
|
+
|
|
180
|
+
def track_page(self) -> None:
|
|
181
|
+
"""Track a processed page"""
|
|
182
|
+
if not self._current_metrics:
|
|
183
|
+
raise OperationNotFoundError(
|
|
184
|
+
message="No operation in progress",
|
|
185
|
+
operation="track_page"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
self._current_metrics.pages_processed += 1
|
|
189
|
+
|
|
190
|
+
def track_error(self) -> None:
|
|
191
|
+
"""Track an error"""
|
|
192
|
+
if not self._current_metrics:
|
|
193
|
+
raise OperationNotFoundError(
|
|
194
|
+
message="No operation in progress",
|
|
195
|
+
operation="track_error"
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
self._current_metrics.errors_count += 1
|
|
199
|
+
|
|
200
|
+
def track_warning(self) -> None:
|
|
201
|
+
"""Track a warning"""
|
|
202
|
+
if not self._current_metrics:
|
|
203
|
+
raise OperationNotFoundError(
|
|
204
|
+
message="No operation in progress",
|
|
205
|
+
operation="track_warning"
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
self._current_metrics.warnings_count += 1
|
|
209
|
+
|
|
210
|
+
def complete_operation(
|
|
211
|
+
self,
|
|
212
|
+
data: List[T],
|
|
213
|
+
source_urls: List[str],
|
|
214
|
+
success: bool = True,
|
|
215
|
+
error_message: Optional[str] = None,
|
|
216
|
+
warnings: Optional[List[str]] = None
|
|
217
|
+
) -> ParseResult[T]:
|
|
218
|
+
"""Complete the current operation and return result"""
|
|
219
|
+
if not self._current_metrics:
|
|
220
|
+
raise OperationNotFoundError(
|
|
221
|
+
message="No operation in progress",
|
|
222
|
+
operation="complete_operation"
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
status = OperationStatus.COMPLETED if success else OperationStatus.FAILED
|
|
226
|
+
self._current_metrics.complete(status)
|
|
227
|
+
|
|
228
|
+
result = ParseResult[T](
|
|
229
|
+
data=data,
|
|
230
|
+
metrics=self._current_metrics,
|
|
231
|
+
source_urls=source_urls,
|
|
232
|
+
parser_id=self.parser_id,
|
|
233
|
+
error_message=error_message,
|
|
234
|
+
warnings=warnings or []
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
self._results_history.append(result)
|
|
238
|
+
self._current_metrics = None
|
|
239
|
+
|
|
240
|
+
return result
|
|
241
|
+
|
|
242
|
+
def cancel_operation(self, reason: str) -> None:
|
|
243
|
+
"""Cancel the current operation"""
|
|
244
|
+
if not self._current_metrics:
|
|
245
|
+
raise OperationNotFoundError(
|
|
246
|
+
message="No operation in progress",
|
|
247
|
+
operation="cancel_operation"
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
self._current_metrics.complete(OperationStatus.CANCELLED)
|
|
251
|
+
|
|
252
|
+
# Create cancelled result
|
|
253
|
+
result = ParseResult[BaseModel](
|
|
254
|
+
data=[],
|
|
255
|
+
metrics=self._current_metrics,
|
|
256
|
+
source_urls=[],
|
|
257
|
+
parser_id=self.parser_id,
|
|
258
|
+
error_message=f"Operation cancelled: {reason}"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
self._results_history.append(result)
|
|
262
|
+
self._current_metrics = None
|
|
263
|
+
|
|
264
|
+
def get_current_metrics(self) -> Optional[ParseMetrics]:
|
|
265
|
+
"""Get current operation metrics"""
|
|
266
|
+
return self._current_metrics
|
|
267
|
+
|
|
268
|
+
def get_history(self) -> List[ParseResult]:
|
|
269
|
+
"""Get results history"""
|
|
270
|
+
return [
|
|
271
|
+
ParseResult.model_validate(result.model_dump())
|
|
272
|
+
for result in self._results_history
|
|
273
|
+
]
|
|
274
|
+
|
|
275
|
+
def get_stats(self) -> dict[str, float]:
|
|
276
|
+
"""Get overall statistics"""
|
|
277
|
+
if not self._results_history:
|
|
278
|
+
return {
|
|
279
|
+
"total_operations": 0.0,
|
|
280
|
+
"success_rate": 0.0,
|
|
281
|
+
"average_duration": 0.0,
|
|
282
|
+
"total_items": 0.0,
|
|
283
|
+
"total_pages": 0.0
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
successful = [
|
|
287
|
+
r for r in self._results_history
|
|
288
|
+
if r.metrics.status == OperationStatus.COMPLETED
|
|
289
|
+
]
|
|
290
|
+
|
|
291
|
+
total_items = sum(r.metrics.items_found for r in self._results_history)
|
|
292
|
+
total_pages = sum(r.metrics.pages_processed for r in self._results_history)
|
|
293
|
+
|
|
294
|
+
durations = [
|
|
295
|
+
r.metrics.duration_seconds
|
|
296
|
+
for r in self._results_history
|
|
297
|
+
if r.metrics.duration_seconds is not None
|
|
298
|
+
]
|
|
299
|
+
avg_duration = sum(durations) / len(durations) if durations else 0.0
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
"total_operations": float(len(self._results_history)),
|
|
303
|
+
"successful_operations": float(len(successful)),
|
|
304
|
+
"failed_operations": float(len(self._results_history) - len(successful)),
|
|
305
|
+
"success_rate": len(successful) / len(self._results_history) * 100.0,
|
|
306
|
+
"total_items_found": float(total_items),
|
|
307
|
+
"total_pages_processed": float(total_pages),
|
|
308
|
+
"average_duration_seconds": round(avg_duration, 2),
|
|
309
|
+
"items_per_operation": total_items / len(self._results_history) if self._results_history else 0.0
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
def clear_history(self) -> None:
|
|
313
|
+
"""Clear results history"""
|
|
314
|
+
self._results_history.clear()
|
|
315
|
+
|
|
316
|
+
def get_recent_results(self, limit: int = 10) -> List[ParseResult]:
|
|
317
|
+
"""Get recent results with limit"""
|
|
318
|
+
if limit <= 0:
|
|
319
|
+
raise ValueError("Limit must be positive")
|
|
320
|
+
|
|
321
|
+
return self._results_history[-limit:]
|