File size: 7,350 Bytes
d80bf0f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
403c184
 
a3a4e3f
 
d80bf0f
 
a758bd8
 
a3a4e3f
 
d80bf0f
a3a4e3f
 
d80bf0f
 
 
 
 
 
 
 
 
403c184
d80bf0f
 
 
 
 
 
 
 
 
 
403c184
d80bf0f
 
 
 
 
 
403c184
 
d80bf0f
 
 
 
 
 
 
 
 
 
 
403c184
d80bf0f
403c184
d80bf0f
 
7e3a96a
d80bf0f
e7c4b2b
 
7e3a96a
d118535
7e3a96a
 
a3a4e3f
 
d80bf0f
a3a4e3f
 
a758bd8
a3a4e3f
 
 
 
 
 
a758bd8
a3a4e3f
a758bd8
 
a3a4e3f
 
d80bf0f
 
 
a3a4e3f
d80bf0f
 
 
a3a4e3f
d80bf0f
 
 
 
 
 
a3a4e3f
d80bf0f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
"""
Configuration management for Hugging Face Spaces deployment
Handles environment variables and application settings
"""

import os
from typing import Dict, Any
from dotenv import load_dotenv

# Load environment variables from .env file if it exists (for local development)
load_dotenv()

class Config:
    """Application configuration management"""
    
    def __init__(self):
        # No automatic environment variable loading - tokens come from Gradio interface only
        self.wildberries_api_token = None
        
        # Official Wildberries API URLs based on documentation
        self.wildberries_base_url = "https://statistics-api.wildberries.ru"
        self.wildberries_content_url = "https://content-api.wildberries.ru"
        self.wildberries_analytics_url = "https://seller-analytics-api.wildberries.ru"
        self.wildberries_common_url = "https://common-api.wildberries.ru"
        self.wildberries_marketplace_url = "https://marketplace-api.wildberries.ru"
        self.wildberries_supplies_url = "https://supplies-api.wildberries.ru"
        
        # Rate limiting settings (based on official documentation)
        # Statistics API: Maximum of 300 requests per minute
        self.rate_limit_requests = 300  # requests per minute
        self.rate_limit_window = 60     # seconds
        
        # Request timeout settings
        self.request_timeout = 30       # seconds
        self.max_retries = 3
        self.retry_backoff_factor = 2
        
        # Application settings
        self.demo_mode = True  # Always start in demo mode, users provide tokens via UI
        self.debug_mode = os.getenv("DEBUG", "false").lower() == "true"
        
        # Hugging Face Spaces specific settings
        self.hf_space_id = os.getenv("SPACE_ID")
        self.hf_space_author = os.getenv("SPACE_AUTHOR_NAME")
        
        # Gradio settings
        self.gradio_analytics = os.getenv("GRADIO_ANALYTICS_ENABLED", "false").lower() == "true"
        self.gradio_theme = os.getenv("GRADIO_THEME", "soft")
        
    def get_api_headers(self, api_token: str = None) -> Dict[str, str]:
        """Get headers for Wildberries API requests"""
        headers = {
            "Content-Type": "application/json",
            "User-Agent": "Wildberries-Analytics-Dashboard/1.0"
        }
        
        if api_token:
            headers["Authorization"] = f"Bearer {api_token}"
        
        return headers
    
    def get_rate_limit_config(self) -> Dict[str, Any]:
        """Get rate limiting configuration"""
        return {
            "requests_per_minute": self.rate_limit_requests,
            "window_seconds": self.rate_limit_window,
            "backoff_factor": self.retry_backoff_factor
        }
    
    def is_configured(self, api_token: str = None) -> bool:
        """Check if the application is properly configured with API token"""
        return api_token is not None and len(api_token.strip()) > 0
    
    def get_endpoints(self) -> Dict[str, str]:
        """Get API endpoint configurations based on working API calls"""
        return {
            # Statistics API endpoints - Correct sales endpoint
            "sales": f"{self.wildberries_base_url}/api/v1/supplier/sales",
            "orders": f"{self.wildberries_base_url}/api/v5/supplier/reportDetailByPeriod", 
            "stocks": f"{self.wildberries_base_url}/api/v1/supplier/stocks",
            "incomes": f"{self.wildberries_base_url}/api/v5/supplier/reportDetailByPeriod",
            "reportDetailByPeriod": f"{self.wildberries_base_url}/api/v5/supplier/reportDetailByPeriod",
            
            # Analytics API endpoints
            "analytics": f"{self.wildberries_analytics_url}/api/v2/nm-report/detail",
            
            # Content API endpoints
            "content": f"{self.wildberries_content_url}/content/v1/cards/cursor/list",
            
            # Common API endpoints
            "news": f"{self.wildberries_common_url}/api/communications/v2/news",
            "seller_info": f"{self.wildberries_common_url}/api/v1/seller-info",
            
            # Connection check endpoints for each service
            "ping_statistics": f"{self.wildberries_base_url}/ping",
            "ping_content": f"{self.wildberries_content_url}/ping", 
            "ping_analytics": f"{self.wildberries_analytics_url}/ping",
            "ping_common": f"{self.wildberries_common_url}/ping",
            "ping_marketplace": f"{self.wildberries_marketplace_url}/ping",
            "ping_supplies": f"{self.wildberries_supplies_url}/ping"
        }
    
    def validate_token(self, token: str) -> bool:
        """Validate the format of a Wildberries API token (JWT format)"""
        if not token:
            return False
        
        # Wildberries tokens are JWT format based on official documentation
        try:
            # Check if it looks like a JWT (three parts separated by dots)
            parts = token.split('.')
            if len(parts) != 3:
                return False
            
            # Check minimum length (JWT tokens are typically longer)
            if len(token) < 50:
                return False
                
            return True
        except Exception:
            return False
    
    def get_demo_settings(self) -> Dict[str, Any]:
        """Get settings for demo mode"""
        return {
            "demo_products_count": 15,
            "demo_sales_days": 30,
            "demo_revenue_range": (10000, 500000),  # rubles
            "demo_stock_range": (0, 1000),
            "demo_categories": [
                "Одежда", "Обувь", "Аксессуары", "Красота", 
                "Дом и сад", "Спорт", "Электроника", "Книги"
            ]
        }
    
    def __repr__(self) -> str:
        """String representation of configuration"""
        return f"""
        Wildberries Analytics Dashboard Configuration:
        - Demo Mode: {self.demo_mode}
        - Debug Mode: {self.debug_mode}
        - API Configured: {self.is_configured()}
        - Rate Limit: {self.rate_limit_requests}/min
        - HF Space: {self.hf_space_author}/{self.hf_space_id if self.hf_space_id else 'local'}
        """

# Global configuration instance
_config = None

def get_config() -> Config:
    """Get the global configuration instance"""
    global _config
    if _config is None:
        _config = Config()
    return _config

def reload_config() -> Config:
    """Reload configuration (useful for testing)"""
    global _config
    _config = Config()
    return _config

# Environment validation for Hugging Face Spaces
def validate_hf_environment():
    """Validate that we're running in a proper HF Spaces environment"""
    config = get_config()
    
    issues = []
    
    if not config.wildberries_api_token:
        issues.append("WILDBERRIES_API_TOKEN not set - running in demo mode")
    
    if config.wildberries_api_token and not config.validate_token(config.wildberries_api_token):
        issues.append("WILDBERRIES_API_TOKEN appears to be invalid format")
    
    return issues

# Export commonly used configurations
DEFAULT_CONFIG = get_config()
API_ENDPOINTS = DEFAULT_CONFIG.get_endpoints()
RATE_LIMIT_CONFIG = DEFAULT_CONFIG.get_rate_limit_config()
DEMO_SETTINGS = DEFAULT_CONFIG.get_demo_settings()