vnai 2.1.8__py3-none-any.whl → 2.1.9__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.
vnai/scope/promo.py CHANGED
@@ -5,7 +5,6 @@ import random
5
5
  import threading
6
6
  import time
7
7
  import urllib.parse
8
-
9
8
  _vnii_check_attempted = False
10
9
 
11
10
  class AdCategory:
@@ -20,37 +19,22 @@ class AdCategory:
20
19
  SECURITY = 8
21
20
  MAINTENANCE = 9
22
21
  WARNING = 10
23
-
24
- # Thêm import kiểm tra license từ vnii
25
22
  try:
26
23
  from vnii import lc_init
27
24
  except ImportError:
28
- lc_init = None # Nếu không có vnii, không xác định được trạng thái license
29
-
30
- # Module-level logger setup
25
+ lc_init = None
31
26
  logger = logging.getLogger(__name__)
32
27
  if not logger.hasHandlers():
33
- # Add a simple stream handler that only outputs the message text
34
28
  handler = logging.StreamHandler()
35
29
  handler.setFormatter(logging.Formatter('%(message)s'))
36
30
  logger.addHandler(handler)
37
- # Set to ERROR level to minimize output
38
31
  logger.setLevel(logging.ERROR)
39
32
 
40
33
  class ContentManager:
41
- """
42
- Singleton manager to fetch remote or fallback promotional content and
43
- present it in different environments (Jupyter, terminal, other).
44
-
45
- Displays content automatically at randomized intervals via a background thread.
46
- """
47
34
  _instance = None
48
35
  _lock = threading.Lock()
49
36
 
50
37
  def __new__(cls):
51
- """
52
- Ensure only one instance of ContentManager is created (thread-safe).
53
- """
54
38
  with cls._lock:
55
39
  if cls._instance is None:
56
40
  cls._instance = super(ContentManager, cls).__new__(cls)
@@ -58,37 +42,30 @@ class ContentManager:
58
42
  return cls._instance
59
43
 
60
44
  def _initialize(self, debug=False):
61
- """
62
- Internal initializer: sets up display timing, URLs, and starts the periodic display thread.
63
- """
64
- # Set up content base URL
65
- self.content_base_url = "https://hq.vnstocks.com/content-delivery"
66
- self.is_paid_user = None # Mặc định: chưa xác định
45
+ self.content_base_url ="https://hq.vnstocks.com/content-delivery"
46
+ self.is_paid_user = None
67
47
  self.license_checked = False
68
48
  self._debug = debug
69
49
  global _vnii_check_attempted
70
50
  if _vnii_check_attempted:
71
- # Đã kiểm tra/cài đặt vnii trước đó, không làm lại nữa
72
51
  return
73
52
  _vnii_check_attempted = True
74
- # Nếu máy đã từng cài vnii, luôn cài lại bản mới nhất; nếu chưa từng cài thì không xác định được trạng thái license
75
53
  import sys
76
54
  import importlib
77
55
  try:
78
56
  import importlib.metadata
79
- VNII_LATEST_VERSION = "0.0.9"
80
- VNII_URL = f"https://github.com/vnstock-hq/licensing/releases/download/vnii-{VNII_LATEST_VERSION}/vnii-{VNII_LATEST_VERSION}.tar.gz"
57
+ VNII_LATEST_VERSION ="0.1.1"
58
+ VNII_URL =f"https://github.com/vnstock-hq/licensing/releases/download/vnii-{VNII_LATEST_VERSION}/vnii-{VNII_LATEST_VERSION}.tar.gz"
81
59
  import subprocess
82
60
  try:
83
61
  old_version = importlib.metadata.version("vnii")
84
- # Chỉ cài nếu version chưa đúng
85
62
  if old_version != VNII_LATEST_VERSION:
86
63
  try:
87
64
  subprocess.check_call([
88
- sys.executable, "-m", "pip", "install", f"vnii@{VNII_URL}", "--quiet"
65
+ sys.executable,"-m","pip","install",f"vnii@{VNII_URL}","--quiet"
89
66
  ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
90
67
  importlib.invalidate_caches()
91
- if "vnii" in sys.modules:
68
+ if"vnii" in sys.modules:
92
69
  importlib.reload(sys.modules["vnii"])
93
70
  else:
94
71
  import vnii
@@ -96,17 +73,15 @@ class ContentManager:
96
73
  except Exception as e:
97
74
  logger.error(f"Lỗi khi cài đặt vnii: {e}")
98
75
  pass
99
- # Nếu đã đúng version thì không log gì cả
100
76
  except importlib.metadata.PackageNotFoundError:
101
- # Nếu chưa từng cài, không cài, luôn coi là free user
102
77
  self.is_paid_user = False
103
78
  return
104
79
  except Exception as e:
105
80
  logger.error(f"Lỗi khi kiểm tra/cài đặt vnii: {e}")
106
81
  user_msg = (
107
- "Không thể tự động cài đặt/cập nhật vnii. "
108
- "Vui lòng liên hệ admin hoặc hỗ trợ kỹ thuật của Vnstock để được trợ giúp. "
109
- f"Chi tiết lỗi: {e}"
82
+ "Không thể tự động cài đặt/cập nhật vnii. "
83
+ "Vui lòng liên hệ admin hoặc hỗ trợ kỹ thuật của Vnstock để được trợ giúp. "
84
+ f"Chi tiết lỗi: {e}"
110
85
  )
111
86
  logger.error(user_msg)
112
87
  try:
@@ -115,15 +90,13 @@ class ContentManager:
115
90
  pass
116
91
  self.is_paid_user = False
117
92
  return
118
-
119
- # Kiểm tra trạng thái paid user (sponsor) và cache lại
120
93
  if lc_init is not None:
121
94
  try:
122
95
  license_info = lc_init(repo_name='vnstock', debug=self._debug)
123
- status = license_info.get('status', '').lower()
96
+ status = license_info.get('status','').lower()
124
97
  if self._debug:
125
98
  logger.info(f"License check result: {status}")
126
- if 'recognized and verified' in status:
99
+ if'recognized and verified' in status:
127
100
  self.is_paid_user = True
128
101
  if self._debug:
129
102
  logger.info("Detected paid user (license recognized and verified).")
@@ -140,110 +113,72 @@ class ContentManager:
140
113
  if self._debug:
141
114
  logger.warning("Không tìm thấy package vnii. Không xác định được trạng thái paid user.")
142
115
  self.is_paid_user = None
143
-
144
- # Timestamp of last content display (epoch seconds)
145
116
  self.last_display = 0
146
- # Minimum interval between displays (24 hours)
147
117
  self.display_interval = 24 * 3600
148
-
149
- # Base endpoints for fetching remote content and linking
150
- self.content_base_url = "https://hq.vnstocks.com/content-delivery"
151
- self.target_url = "https://vnstocks.com/lp-khoa-hoc-python-chung-khoan"
118
+ self.content_base_url ="https://hq.vnstocks.com/content-delivery"
119
+ self.target_url ="https://vnstocks.com/lp-khoa-hoc-python-chung-khoan"
152
120
  self.image_url = (
153
- "https://vnstocks.com/img/trang-chu-vnstock-python-api-phan-tich-giao-dich-chung-khoan.jpg"
121
+ "https://vnstocks.com/img/trang-chu-vnstock-python-api-phan-tich-giao-dich-chung-khoan.jpg"
154
122
  )
155
-
156
- # Launch the background thread to periodically present content
157
123
  self._start_periodic_display()
158
124
 
159
125
  def _start_periodic_display(self):
160
- """
161
- Launch a daemon thread that sleeps a random duration between 2–6 hours,
162
- then checks if the display interval has elapsed and calls present_content.
163
- """
164
126
  def periodic_display():
165
127
  while True:
166
- # Nếu là paid user thì không bao giờ hiện ads
167
128
  if self.is_paid_user:
168
129
  break
169
- # Randomize sleep to avoid synchronized requests across instances
170
130
  sleep_time = random.randint(2 * 3600, 6 * 3600)
171
131
  time.sleep(sleep_time)
172
-
173
- # Present content if enough time has passed since last_display
174
132
  current_time = time.time()
175
133
  if current_time - self.last_display >= self.display_interval:
176
134
  self.present_content(context="periodic")
177
135
  else:
178
136
  pass
179
-
180
137
  thread = threading.Thread(target=periodic_display, daemon=True)
181
138
  thread.start()
182
139
 
183
- def fetch_remote_content(self, context: str = "init", html: bool = True) -> str:
140
+ def fetch_remote_content(self, context: str ="init", html: bool = True) -> str:
184
141
  if self.is_paid_user:
185
- return ""
186
-
142
+ return""
187
143
  """
188
144
  Fetch promotional content from remote service with context and format flag.
189
-
190
145
  Args:
191
146
  context: usage context (e.g., "init", "periodic", "loop").
192
147
  html: if True, request HTML; otherwise plain text.
193
-
194
148
  Returns:
195
149
  The content string on HTTP 200, or None on failure.
196
150
  """
197
151
  try:
198
- # Build query params and URL
199
- params = {"context": context, "html": "true" if html else "false"}
200
- url = f"{self.content_base_url}?{urllib.parse.urlencode(params)}"
201
-
152
+ params = {"context": context,"html":"true" if html else"false"}
153
+ url =f"{self.content_base_url}?{urllib.parse.urlencode(params)}"
202
154
  response = requests.get(url, timeout=3)
203
155
  if response.status_code == 200:
204
156
  return response.text
205
- # Log non-200 responses at debug level
206
157
  logger.error(f"Non-200 response fetching content: {response.status_code}")
207
158
  return None
208
159
  except Exception as e:
209
- # Log exceptions without interrupting user code
210
160
  logger.error(f"Failed to fetch remote content: {e}")
211
161
  return None
212
162
 
213
- def present_content(self, context: str = "init", ad_category: int = AdCategory.FREE) -> None:
163
+ def present_content(self, context: str ="init", ad_category: int = AdCategory.FREE) -> None:
214
164
  environment = None
215
- """
216
- Display promotional content in the appropriate environment.
217
- ad_category: Loại quảng cáo (FREE, ANNOUNCEMENT, ...)
218
- """
219
- # Nếu là paid user và ad_category là FREE thì skip, còn lại vẫn hiện
220
- if getattr(self, 'is_paid_user', False) and ad_category == AdCategory.FREE:
165
+ if getattr(self,'is_paid_user', False) and ad_category == AdCategory.FREE:
221
166
  return
222
-
223
- # Update last display timestamp
224
167
  self.last_display = time.time()
225
-
226
- # Auto-detect environment if not provided
227
168
  if environment is None:
228
169
  try:
229
170
  from vnai.scope.profile import inspector
230
- environment = inspector.examine().get("environment", "unknown")
171
+ environment = inspector.examine().get("environment","unknown")
231
172
  except Exception as e:
232
173
  logger.error(f"Không detect được environment: {e}")
233
- environment = "unknown"
234
-
235
- # Retrieve remote or HTML/text content based on environment
174
+ environment ="unknown"
236
175
  remote_content = self.fetch_remote_content(
237
- context=context, html=(environment == "jupyter")
176
+ context=context, html=(environment =="jupyter")
238
177
  )
239
-
240
- # Generate fallback messages if remote fetch fails
241
178
  fallback = self._generate_fallback_content(context)
242
-
243
- if environment == "jupyter":
179
+ if environment =="jupyter":
244
180
  try:
245
181
  from IPython.display import display, HTML, Markdown
246
-
247
182
  if remote_content:
248
183
  display(HTML(remote_content))
249
184
  else:
@@ -253,23 +188,19 @@ class ContentManager:
253
188
  display(HTML(fallback["html"]))
254
189
  except Exception as e:
255
190
  pass
256
-
257
- elif environment == "terminal":
258
- # Hiển thị quảng cáo dạng text ra terminal
191
+ elif environment =="terminal":
259
192
  if remote_content:
260
193
  print(remote_content)
261
194
  else:
262
195
  print(fallback["terminal"])
263
-
264
196
  else:
265
197
  print(fallback["simple"])
266
198
 
267
199
  def _generate_fallback_content(self, context):
268
- fallback = {"html": "", "markdown": "", "terminal": "", "simple": ""}
269
-
270
- if context == "loop":
200
+ fallback = {"html":"","markdown":"","terminal":"","simple":""}
201
+ if context =="loop":
271
202
  fallback["html"] = (
272
- f"""
203
+ f"""
273
204
  <div style="border: 1px solid #e74c3c; padding: 15px; border-radius: 5px; margin: 10px 0;">
274
205
  <h3 style="color: #e74c3c;">⚠️ Bạn đang sử dụng vòng lặp với quá nhiều requests</h3>
275
206
  <p>Để tránh bị giới hạn tốc độ và tối ưu hiệu suất:</p>
@@ -282,9 +213,8 @@ class ContentManager:
282
213
  """
283
214
  )
284
215
  fallback["markdown"] = (
285
- """
216
+ """
286
217
  ## ⚠️ Bạn đang sử dụng vòng lặp với quá nhiều requests
287
-
288
218
  Để tránh bị giới hạn tốc độ và tối ưu hiệu suất:
289
219
  * Thêm thời gian chờ giữa các lần gọi API
290
220
  * Sử dụng xử lý theo batch thay vì lặp liên tục
@@ -292,7 +222,7 @@ class ContentManager:
292
222
  """
293
223
  )
294
224
  fallback["terminal"] = (
295
- """
225
+ """
296
226
  ╔═════════════════════════════════════════════════════════════════╗
297
227
  ║ ║
298
228
  ║ 🚫 ĐANG BỊ CHẶN BỞI GIỚI HẠN API? GIẢI PHÁP Ở ĐÂY! ║
@@ -307,12 +237,12 @@ class ContentManager:
307
237
  """
308
238
  )
309
239
  fallback["simple"] = (
310
- "🚫 Đang bị giới hạn API? Tăng tốc độ gọi API lên 500% với gói "
311
- "Vnstock Insider: https://vnstocks.com/insiders-program"
240
+ "🚫 Đang bị giới hạn API? Tăng tốc độ gọi API lên 500% với gói "
241
+ "Vnstock Insider: https://vnstocks.com/insiders-program"
312
242
  )
313
243
  else:
314
244
  fallback["html"] = (
315
- f"""
245
+ f"""
316
246
  <div style="border: 1px solid #3498db; padding: 15px; border-radius: 5px; margin: 10px 0;">
317
247
  <h3 style="color: #3498db;">👋 Chào mừng bạn đến với Vnstock!</h3>
318
248
  <p>Cảm ơn bạn đã sử dụng thư viện phân tích chứng khoán #1 tại Việt Nam cho Python</p>
@@ -325,19 +255,16 @@ class ContentManager:
325
255
  """
326
256
  )
327
257
  fallback["markdown"] = (
328
- """
258
+ """
329
259
  ## 👋 Chào mừng bạn đến với Vnstock!
330
-
331
260
  Cảm ơn bạn đã sử dụng package phân tích chứng khoán #1 tại Việt Nam
332
-
333
261
  * Tài liệu: [Sổ tay hướng dẫn](https://vnstocks.com/docs)
334
262
  * Cộng đồng: [Nhóm Facebook](https://facebook.com/groups/vnstock.official)
335
-
336
263
  Khám phá các tính năng mới nhất và tham gia cộng đồng để nhận hỗ trợ.
337
264
  """
338
265
  )
339
266
  fallback["terminal"] = (
340
- """
267
+ """
341
268
  ╔════════════════════════════════════════════════════════════╗
342
269
  ║ ║
343
270
  ║ 👋 Chào mừng bạn đến với Vnstock! ║
@@ -355,21 +282,12 @@ Khám phá các tính năng mới nhất và tham gia cộng đồng để nhậ
355
282
  """
356
283
  )
357
284
  fallback["simple"] = (
358
- "👋 Chào mừng bạn đến với Vnstock! "
359
- "Tài liệu: https://vnstocks.com/onboard | "
360
- "Cộng đồng: https://facebook.com/groups/vnstock.official"
285
+ "👋 Chào mừng bạn đến với Vnstock! "
286
+ "Tài liệu: https://vnstocks.com/onboard | "
287
+ "Cộng đồng: https://facebook.com/groups/vnstock.official"
361
288
  )
362
289
  return fallback
363
-
364
- # Singleton instance for module-level use
365
290
  manager = ContentManager()
366
291
 
367
- def present(context: str = "init", ad_category: int = AdCategory.FREE) -> None:
368
- """
369
- Shortcut to ContentManager.present_content for external callers.
370
-
371
- Args:
372
- context: propagate context string to ContentManager.
373
- ad_category: loại quảng cáo (FREE, ANNOUNCEMENT, ...)
374
- """
375
- manager.present_content(context=context, ad_category=ad_category)
292
+ def present(context: str ="init", ad_category: int = AdCategory.FREE) -> None:
293
+ manager.present_content(context=context, ad_category=ad_category)
vnai/scope/state.py CHANGED
@@ -1,5 +1,3 @@
1
- # vnai/scope/state.py
2
-
3
1
  import time
4
2
  import threading
5
3
  import json
@@ -8,215 +6,150 @@ from datetime import datetime
8
6
  from pathlib import Path
9
7
 
10
8
  class Tracker:
11
- """Tracks system state and performance metrics"""
12
-
13
9
  _instance = None
14
10
  _lock = threading.Lock()
15
-
11
+
16
12
  def __new__(cls):
17
13
  with cls._lock:
18
14
  if cls._instance is None:
19
15
  cls._instance = super(Tracker, cls).__new__(cls)
20
16
  cls._instance._initialize()
21
17
  return cls._instance
22
-
18
+
23
19
  def _initialize(self):
24
- """Initialize tracker"""
25
20
  self.metrics = {
26
- "startup_time": datetime.now().isoformat(),
27
- "function_calls": 0,
28
- "api_requests": 0,
29
- "errors": 0,
30
- "warnings": 0
21
+ "startup_time": datetime.now().isoformat(),
22
+ "function_calls": 0,
23
+ "api_requests": 0,
24
+ "errors": 0,
25
+ "warnings": 0
31
26
  }
32
-
33
27
  self.performance_metrics = {
34
- "execution_times": [],
35
- "last_error_time": None,
36
- "peak_memory": 0
28
+ "execution_times": [],
29
+ "last_error_time": None,
30
+ "peak_memory": 0
37
31
  }
38
-
39
- self.privacy_level = "standard"
40
-
41
- # Setup data directory
32
+ self.privacy_level ="standard"
42
33
  self.home_dir = Path.home()
43
- self.project_dir = self.home_dir / ".vnstock"
34
+ self.project_dir = self.home_dir /".vnstock"
44
35
  self.project_dir.mkdir(exist_ok=True)
45
- self.data_dir = self.project_dir / 'data'
36
+ self.data_dir = self.project_dir /'data'
46
37
  self.data_dir.mkdir(exist_ok=True)
47
- self.metrics_path = self.data_dir / "usage_metrics.json"
48
- self.privacy_config_path = self.project_dir / 'config' / "privacy.json"
49
-
50
- # Create config directory if it doesn't exist
38
+ self.metrics_path = self.data_dir /"usage_metrics.json"
39
+ self.privacy_config_path = self.project_dir /'config' /"privacy.json"
51
40
  os.makedirs(os.path.dirname(self.privacy_config_path), exist_ok=True)
52
-
53
- # Load existing metrics
54
41
  self._load_metrics()
55
-
56
- # Load privacy settings
57
42
  self._load_privacy_settings()
58
-
59
- # Start background metrics collector
60
43
  self._start_background_collector()
61
-
44
+
62
45
  def _load_metrics(self):
63
- """Load metrics from file"""
64
46
  if self.metrics_path.exists():
65
47
  try:
66
- with open(self.metrics_path, 'r') as f:
48
+ with open(self.metrics_path,'r') as f:
67
49
  stored_metrics = json.load(f)
68
-
69
- # Update metrics with stored values
70
50
  for key, value in stored_metrics.items():
71
51
  if key in self.metrics:
72
52
  self.metrics[key] = value
73
53
  except:
74
54
  pass
75
-
55
+
76
56
  def _save_metrics(self):
77
- """Save metrics to file"""
78
57
  try:
79
- with open(self.metrics_path, 'w') as f:
58
+ with open(self.metrics_path,'w') as f:
80
59
  json.dump(self.metrics, f)
81
60
  except:
82
61
  pass
83
-
62
+
84
63
  def _load_privacy_settings(self):
85
- """Load privacy settings"""
86
64
  if self.privacy_config_path.exists():
87
65
  try:
88
- with open(self.privacy_config_path, 'r') as f:
66
+ with open(self.privacy_config_path,'r') as f:
89
67
  settings = json.load(f)
90
- self.privacy_level = settings.get("level", "standard")
68
+ self.privacy_level = settings.get("level","standard")
91
69
  except:
92
70
  pass
93
-
71
+
94
72
  def setup_privacy(self, level=None):
95
- """Configure privacy level for data collection"""
96
73
  privacy_levels = {
97
- "minimal": "Essential system data only",
98
- "standard": "Performance metrics and errors",
99
- "enhanced": "Detailed operation analytics"
74
+ "minimal":"Essential system data only",
75
+ "standard":"Performance metrics and errors",
76
+ "enhanced":"Detailed operation analytics"
100
77
  }
101
-
102
78
  if level is None:
103
- # Default level
104
- level = "standard"
105
-
79
+ level ="standard"
106
80
  if level not in privacy_levels:
107
81
  raise ValueError(f"Invalid privacy level: {level}. Choose from {', '.join(privacy_levels.keys())}")
108
-
109
- # Store preference
110
82
  self.privacy_level = level
111
-
112
- # Store in configuration file
113
- with open(self.privacy_config_path, "w") as f:
83
+ with open(self.privacy_config_path,"w") as f:
114
84
  json.dump({"level": level}, f)
115
-
116
85
  return level
117
-
86
+
118
87
  def get_privacy_level(self):
119
- """Get current privacy level"""
120
88
  return self.privacy_level
121
-
89
+
122
90
  def _start_background_collector(self):
123
- """Start background metrics collection"""
124
91
  def collect_metrics():
125
92
  while True:
126
93
  try:
127
94
  import psutil
128
-
129
- # Update peak memory
130
95
  current_process = psutil.Process()
131
96
  memory_info = current_process.memory_info()
132
- memory_usage = memory_info.rss / (1024 * 1024) # MB
133
-
97
+ memory_usage = memory_info.rss / (1024 * 1024)
134
98
  if memory_usage > self.performance_metrics["peak_memory"]:
135
99
  self.performance_metrics["peak_memory"] = memory_usage
136
-
137
- # Save metrics periodically
138
100
  self._save_metrics()
139
-
140
101
  except:
141
102
  pass
142
-
143
- time.sleep(300) # Run every 5 minutes
144
-
145
- # Start thread
103
+ time.sleep(300)
146
104
  thread = threading.Thread(target=collect_metrics, daemon=True)
147
105
  thread.start()
148
-
106
+
149
107
  def record(self, event_type, data=None):
150
- """Record an event"""
151
- # Check privacy level
152
- if self.privacy_level == "minimal" and event_type != "errors":
153
- # In minimal mode, only track errors
108
+ if self.privacy_level =="minimal" and event_type !="errors":
154
109
  return True
155
-
156
- # Update counts
157
110
  if event_type in self.metrics:
158
111
  self.metrics[event_type] += 1
159
112
  else:
160
113
  self.metrics[event_type] = 1
161
-
162
- # Special handling for errors
163
- if event_type == "errors":
114
+ if event_type =="errors":
164
115
  self.performance_metrics["last_error_time"] = datetime.now().isoformat()
165
-
166
- # Special handling for function calls with timing data
167
- if event_type == "function_calls" and data and "execution_time" in data:
168
- # Keep up to 100 latest execution times
116
+ if event_type =="function_calls" and data and"execution_time" in data:
169
117
  self.performance_metrics["execution_times"].append(data["execution_time"])
170
118
  if len(self.performance_metrics["execution_times"]) > 100:
171
119
  self.performance_metrics["execution_times"] = self.performance_metrics["execution_times"][-100:]
172
-
173
- # Save if metrics change significantly
174
- if self.metrics["function_calls"] % 100 == 0 or event_type == "errors":
120
+ if self.metrics["function_calls"] % 100 == 0 or event_type =="errors":
175
121
  self._save_metrics()
176
-
177
122
  return True
178
-
123
+
179
124
  def get_metrics(self):
180
- """Get current metrics"""
181
- # Calculate derived metrics
182
125
  avg_execution_time = 0
183
126
  if self.performance_metrics["execution_times"]:
184
127
  avg_execution_time = sum(self.performance_metrics["execution_times"]) / len(self.performance_metrics["execution_times"])
185
-
186
- # Add derived metrics to output
187
128
  output = self.metrics.copy()
188
129
  output.update({
189
- "avg_execution_time": avg_execution_time,
190
- "peak_memory_mb": self.performance_metrics["peak_memory"],
191
- "uptime": (datetime.now() - datetime.fromisoformat(self.metrics["startup_time"])).total_seconds(),
192
- "privacy_level": self.privacy_level
130
+ "avg_execution_time": avg_execution_time,
131
+ "peak_memory_mb": self.performance_metrics["peak_memory"],
132
+ "uptime": (datetime.now() - datetime.fromisoformat(self.metrics["startup_time"])).total_seconds(),
133
+ "privacy_level": self.privacy_level
193
134
  })
194
-
195
135
  return output
196
-
136
+
197
137
  def reset(self):
198
- """Reset metrics"""
199
138
  self.metrics = {
200
- "startup_time": datetime.now().isoformat(),
201
- "function_calls": 0,
202
- "api_requests": 0,
203
- "errors": 0,
204
- "warnings": 0
139
+ "startup_time": datetime.now().isoformat(),
140
+ "function_calls": 0,
141
+ "api_requests": 0,
142
+ "errors": 0,
143
+ "warnings": 0
205
144
  }
206
-
207
145
  self.performance_metrics = {
208
- "execution_times": [],
209
- "last_error_time": None,
210
- "peak_memory": 0
146
+ "execution_times": [],
147
+ "last_error_time": None,
148
+ "peak_memory": 0
211
149
  }
212
-
213
150
  self._save_metrics()
214
151
  return True
215
-
216
- # Create singleton instance
217
152
  tracker = Tracker()
218
153
 
219
-
220
154
  def record(event_type, data=None):
221
- """Record an event"""
222
- return tracker.record(event_type, data)
155
+ return tracker.record(event_type, data)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vnai
3
- Version: 2.1.8
3
+ Version: 2.1.9
4
4
  Summary: Vnstock Analytics Interface
5
5
  Author-email: Vnstock Analytics Team <support@vnstocks.com>
6
6
  License: proprietary
@@ -0,0 +1,16 @@
1
+ vnai/__init__.py,sha256=oTfe5Hu8UUDUW3Gq84fqX4InkIfqIZTUqTv2Q28RB3c,5733
2
+ vnai/beam/__init__.py,sha256=h19ZEQFnWTvqed0mpTcuQf-sT9XifWhfVbaobTEs91Q,131
3
+ vnai/beam/metrics.py,sha256=2BmdJl7gB4rHwCN7G7WUMg_6rnlY3xfulBzU7usqNfE,6275
4
+ vnai/beam/pulse.py,sha256=xu7cStqdkCw8be_yVwfjGtqdy-stYC511oZOkT9j6hs,2485
5
+ vnai/beam/quota.py,sha256=0pgkCdARtojF-xNC0Y_VYYo4GO717F0MOexwNH2NdzY,14416
6
+ vnai/flow/__init__.py,sha256=9JepoOHS1xn0_ZrDyfft42g2cjhaka7xdG0velqmk-I,70
7
+ vnai/flow/queue.py,sha256=9dyMwxen1vCiFJ7wNZn-kzMY8DnB4xD575J1pPSWZLw,3223
8
+ vnai/flow/relay.py,sha256=7VUsiU1T272chBRCRR90Aodfh_PcUUAUlX3SPUtltFQ,12624
9
+ vnai/scope/__init__.py,sha256=gVvOMQ34V3Tm8_XmX4cRCrgKVfZ4XYQup79MWx6CWuM,197
10
+ vnai/scope/profile.py,sha256=RSk8w5h9rvamFozQOUrYcDSXssTF0ro70Qq-VfExQF0,21567
11
+ vnai/scope/promo.py,sha256=AcQDdzcIYCrTQ0Hqr0KVBCOJsBYQevp0T6emQlGlQ-E,13597
12
+ vnai/scope/state.py,sha256=ApzP6U0Hzp047-k3czL933soUXiZjpZSxXk7DKwf7yM,5389
13
+ vnai-2.1.9.dist-info/METADATA,sha256=RiB-4I-sviuL_7kRXBtEPsQyfLs15bJ_nEPHVC6qEbY,662
14
+ vnai-2.1.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ vnai-2.1.9.dist-info/top_level.txt,sha256=4zI0qZHePCwvgSqXl4420sBcd0VzZn4MEcRsAIFae3k,5
16
+ vnai-2.1.9.dist-info/RECORD,,