vnai 2.0.1__py3-none-any.whl → 2.0.2__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/__init__.py +72 -261
- vnai/beam/__init__.py +2 -5
- vnai/beam/metrics.py +57 -182
- vnai/beam/pulse.py +29 -107
- vnai/beam/quota.py +102 -473
- vnai/flow/__init__.py +2 -5
- vnai/flow/queue.py +55 -131
- vnai/flow/relay.py +149 -439
- vnai/scope/__init__.py +2 -5
- vnai/scope/profile.py +219 -763
- vnai/scope/promo.py +55 -215
- vnai/scope/state.py +71 -220
- {vnai-2.0.1.dist-info → vnai-2.0.2.dist-info}/METADATA +3 -2
- vnai-2.0.2.dist-info/RECORD +16 -0
- {vnai-2.0.1.dist-info → vnai-2.0.2.dist-info}/WHEEL +1 -1
- vnai-2.0.1.dist-info/RECORD +0 -16
- {vnai-2.0.1.dist-info → vnai-2.0.2.dist-info}/top_level.txt +0 -0
vnai/scope/promo.py
CHANGED
@@ -1,146 +1,55 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
_G='init'
|
2
|
+
_F='simple'
|
3
|
+
_E='markdown'
|
4
|
+
_D=True
|
5
|
+
_C=None
|
6
|
+
_B='terminal'
|
7
|
+
_A='html'
|
4
8
|
import requests
|
5
9
|
from datetime import datetime
|
6
|
-
import random
|
7
|
-
import threading
|
8
|
-
import time
|
9
|
-
import urllib.parse
|
10
|
-
|
10
|
+
import random,threading,time,urllib.parse
|
11
11
|
class ContentManager:
|
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
|
-
Fetch content from remote source
|
55
|
-
|
56
|
-
Args:
|
57
|
-
context (str): Context of the content request (init, loop, periodic, etc.)
|
58
|
-
html (bool): Whether to request HTML content (True) or plain text (False)
|
59
|
-
"""
|
60
|
-
try:
|
61
|
-
# Build the URL with query parameters
|
62
|
-
params = {
|
63
|
-
"context": context,
|
64
|
-
"html": "true" if html else "false"
|
65
|
-
}
|
66
|
-
url = f"{self.content_base_url}?{urllib.parse.urlencode(params)}"
|
67
|
-
|
68
|
-
response = requests.get(url, timeout=3)
|
69
|
-
if response.status_code == 200:
|
70
|
-
return response.text
|
71
|
-
return None
|
72
|
-
except:
|
73
|
-
return None
|
74
|
-
|
75
|
-
def present_content(self, environment=None, context="init"):
|
76
|
-
"""
|
77
|
-
Present content appropriate for the environment and context
|
78
|
-
|
79
|
-
Args:
|
80
|
-
environment (str): The display environment (jupyter, terminal, etc.)
|
81
|
-
context (str): The context for content (init, loop, periodic)
|
82
|
-
"""
|
83
|
-
# Update last display time
|
84
|
-
self.last_display = time.time()
|
85
|
-
|
86
|
-
# Get environment if not provided
|
87
|
-
if environment is None:
|
88
|
-
try:
|
89
|
-
from vnai.scope.profile import inspector
|
90
|
-
environment = inspector.examine().get("environment", "unknown")
|
91
|
-
except:
|
92
|
-
environment = "unknown"
|
93
|
-
|
94
|
-
# Try to fetch remote content based on context and environment
|
95
|
-
if environment == "jupyter":
|
96
|
-
remote_content = self.fetch_remote_content(context=context, html=True)
|
97
|
-
else:
|
98
|
-
remote_content = self.fetch_remote_content(context=context, html=False)
|
99
|
-
|
100
|
-
# Generate fallback content based on context
|
101
|
-
fallback_content = self._generate_fallback_content(context)
|
102
|
-
|
103
|
-
# Display based on environment
|
104
|
-
if environment == "jupyter":
|
105
|
-
try:
|
106
|
-
from IPython.display import display, HTML, Markdown
|
107
|
-
if remote_content:
|
108
|
-
# Use remote content if available
|
109
|
-
display(HTML(remote_content))
|
110
|
-
else:
|
111
|
-
# Try markdown version first
|
112
|
-
try:
|
113
|
-
display(Markdown(fallback_content["markdown"]))
|
114
|
-
except:
|
115
|
-
# Fall back to HTML if markdown fails
|
116
|
-
display(HTML(fallback_content["html"]))
|
117
|
-
except:
|
118
|
-
# If IPython display fails, do nothing
|
119
|
-
pass
|
120
|
-
|
121
|
-
elif environment == "terminal":
|
122
|
-
if remote_content:
|
123
|
-
print(remote_content)
|
124
|
-
else:
|
125
|
-
print(fallback_content["terminal"])
|
126
|
-
|
127
|
-
else:
|
128
|
-
# Simple text version for other environments
|
129
|
-
print(fallback_content["simple"])
|
130
|
-
|
131
|
-
def _generate_fallback_content(self, context):
|
132
|
-
"""Generate appropriate fallback content based on context"""
|
133
|
-
fallback = {
|
134
|
-
"html": "",
|
135
|
-
"markdown": "",
|
136
|
-
"terminal": "",
|
137
|
-
"simple": ""
|
138
|
-
}
|
139
|
-
|
140
|
-
if context == "loop":
|
141
|
-
|
142
|
-
# Content for rate limit exceeded scenario
|
143
|
-
fallback["html"] = f"""
|
12
|
+
_instance=_C;_lock=threading.Lock()
|
13
|
+
def __new__(A):
|
14
|
+
with A._lock:
|
15
|
+
if A._instance is _C:A._instance=super(ContentManager,A).__new__(A);A._instance._initialize()
|
16
|
+
return A._instance
|
17
|
+
def _initialize(A):A.last_display=0;A.display_interval=86400;A.content_base_url='https://vnstock-beam.hf.space/content-delivery';A.target_url='https://vnstocks.com/lp-khoa-hoc-python-chung-khoan';A.image_url='https://vnstocks.com/img/trang-chu-vnstock-python-api-phan-tich-giao-dich-chung-khoan.jpg';A._start_periodic_display()
|
18
|
+
def _start_periodic_display(A):
|
19
|
+
def B():
|
20
|
+
while _D:
|
21
|
+
B=random.randint(7200,21600);time.sleep(B);C=time.time()
|
22
|
+
if C-A.last_display>=A.display_interval:A.present_content(context='periodic')
|
23
|
+
C=threading.Thread(target=B,daemon=_D);C.start()
|
24
|
+
def fetch_remote_content(B,context=_G,html=_D):
|
25
|
+
try:
|
26
|
+
C={'context':context,_A:'true'if html else'false'};D=f"{B.content_base_url}?{urllib.parse.urlencode(C)}";A=requests.get(D,timeout=3)
|
27
|
+
if A.status_code==200:return A.text
|
28
|
+
return
|
29
|
+
except:return
|
30
|
+
def present_content(C,environment=_C,context=_G):
|
31
|
+
I='jupyter';H='unknown';E=context;A=environment;C.last_display=time.time()
|
32
|
+
if A is _C:
|
33
|
+
try:from vnai.scope.profile import inspector as J;A=J.examine().get('environment',H)
|
34
|
+
except:A=H
|
35
|
+
if A==I:B=C.fetch_remote_content(context=E,html=_D)
|
36
|
+
else:B=C.fetch_remote_content(context=E,html=False)
|
37
|
+
D=C._generate_fallback_content(E)
|
38
|
+
if A==I:
|
39
|
+
try:
|
40
|
+
from IPython.display import display as F,HTML as G,Markdown as K
|
41
|
+
if B:F(G(B))
|
42
|
+
else:
|
43
|
+
try:F(K(D[_E]))
|
44
|
+
except:F(G(D[_A]))
|
45
|
+
except:pass
|
46
|
+
elif A==_B:
|
47
|
+
if B:print(B)
|
48
|
+
else:print(D[_B])
|
49
|
+
else:print(D[_F])
|
50
|
+
def _generate_fallback_content(B,context):
|
51
|
+
A={_A:'',_E:'',_B:'',_F:''}
|
52
|
+
if context=='loop':A[_A]=f'''
|
144
53
|
<div style="border: 1px solid #e74c3c; padding: 15px; border-radius: 5px; margin: 10px 0;">
|
145
54
|
<h3 style="color: #e74c3c;">⚠️ Bạn đang sử dụng vòng lặp với quá nhiều requests</h3>
|
146
55
|
<p>Để tránh bị giới hạn tốc độ và tối ưu hiệu suất:</p>
|
@@ -150,36 +59,8 @@ class ContentManager:
|
|
150
59
|
<li>Tham gia gói tài trợ <a href="https://vnstocks.com/insiders-program" style="color: #3498db;">Vnstock Insider</a> để tăng 5X giới hạn API</li>
|
151
60
|
</ul>
|
152
61
|
</div>
|
153
|
-
|
154
|
-
|
155
|
-
fallback["markdown"] = """
|
156
|
-
## ⚠️ Bạn đang sử dụng vòng lặp với quá nhiều requests
|
157
|
-
|
158
|
-
Để tránh bị giới hạn tốc độ và tối ưu hiệu suất:
|
159
|
-
* Thêm thời gian chờ giữa các lần gọi API
|
160
|
-
* Sử dụng xử lý theo batch thay vì lặp liên tục
|
161
|
-
* Tham gia gói tài trợ [Vnstock Insider](https://vnstocks.com/insiders-program) để tăng 5X giới hạn API
|
162
|
-
"""
|
163
|
-
|
164
|
-
fallback["terminal"] = """
|
165
|
-
╔═════════════════════════════════════════════════════════════════╗
|
166
|
-
║ ║
|
167
|
-
║ 🚫 ĐANG BỊ CHẶN BỞI GIỚI HẠN API? GIẢI PHÁP Ở ĐÂY! ║
|
168
|
-
║ ║
|
169
|
-
║ ✓ Tăng ngay 500% tốc độ gọi API - Không còn lỗi RateLimit ║
|
170
|
-
║ ✓ Tiết kiệm 85% thời gian chờ đợi giữa các request ║
|
171
|
-
║ ║
|
172
|
-
║ ➤ NÂNG CẤP NGAY VỚI GÓI TÀI TRỢ VNSTOCK: ║
|
173
|
-
║ https://vnstocks.com/insiders-program ║
|
174
|
-
║ ║
|
175
|
-
╚═════════════════════════════════════════════════════════════════╝
|
176
|
-
"""
|
177
|
-
|
178
|
-
fallback["simple"] = "🚫 Đang bị giới hạn API? Tăng tốc độ gọi API lên 500% với gói Vnstock Insider: https://vnstocks.com/insiders-program"
|
179
|
-
|
180
|
-
else: # init or periodic or default
|
181
|
-
# General welcome content
|
182
|
-
fallback["html"] = f"""
|
62
|
+
''';A[_E]='\n## ⚠️ Bạn đang sử dụng vòng lặp với quá nhiều requests\n\nĐể tránh bị giới hạn tốc độ và tối ưu hiệu suất:\n* Thêm thời gian chờ giữa các lần gọi API\n* Sử dụng xử lý theo batch thay vì lặp liên tục\n* Tham gia gói tài trợ [Vnstock Insider](https://vnstocks.com/insiders-program) để tăng 5X giới hạn API\n ';A[_B]='\n╔═════════════════════════════════════════════════════════════════╗\n║ ║\n║ 🚫 ĐANG BỊ CHẶN BỞI GIỚI HẠN API? GIẢI PHÁP Ở ĐÂY! ║\n║ ║\n║ ✓ Tăng ngay 500% tốc độ gọi API - Không còn lỗi RateLimit ║\n║ ✓ Tiết kiệm 85% thời gian chờ đợi giữa các request ║\n║ ║\n║ ➤ NÂNG CẤP NGAY VỚI GÓI TÀI TRỢ VNSTOCK: ║\n║ https://vnstocks.com/insiders-program ║\n║ ║\n╚═════════════════════════════════════════════════════════════════╝\n ';A[_F]='🚫 Đang bị giới hạn API? Tăng tốc độ gọi API lên 500% với gói Vnstock Insider: https://vnstocks.com/insiders-program'
|
63
|
+
else:A[_A]=f'''
|
183
64
|
<div style="border: 1px solid #3498db; padding: 15px; border-radius: 5px; margin: 10px 0;">
|
184
65
|
<h3 style="color: #3498db;">👋 Chào mừng bạn đến với Vnstock!</h3>
|
185
66
|
<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>
|
@@ -189,48 +70,7 @@ class ContentManager:
|
|
189
70
|
</ul>
|
190
71
|
<p>Khám phá các tính năng mới nhất và tham gia cộng đồng để nhận hỗ trợ.</p>
|
191
72
|
</div>
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
Cảm ơn bạn đã sử dụng package phân tích chứng khoán #1 tại Việt Nam
|
198
|
-
|
199
|
-
* Tài liệu: [vnstocks.com/docs](https://vnstocks.com/docs)
|
200
|
-
* Cộng đồng: [vnstocks.com/community](https://vnstocks.com/community)
|
201
|
-
|
202
|
-
Khám phá các tính năng mới nhất và tham gia cộng đồng để nhận hỗ trợ.
|
203
|
-
"""
|
204
|
-
|
205
|
-
fallback["terminal"] = """
|
206
|
-
╔══════════════════════════════════════════════════════════╗
|
207
|
-
║ ║
|
208
|
-
║ 👋 Chào mừng bạn đến với Vnstock! ║
|
209
|
-
║ ║
|
210
|
-
║ Cảm ơn bạn đã sử dụng package phân tích ║
|
211
|
-
║ chứng khoán #1 tại Việt Nam ║
|
212
|
-
║ ║
|
213
|
-
║ ✓ Tài liệu: https://vnstocks.com/docs ║
|
214
|
-
║ ✓ Cộng đồng: https://vnstocks.com/community ║
|
215
|
-
║ ║
|
216
|
-
║ Khám phá các tính năng mới nhất và tham gia ║
|
217
|
-
║ cộng đồng để nhận hỗ trợ. ║
|
218
|
-
║ ║
|
219
|
-
╚══════════════════════════════════════════════════════════╝
|
220
|
-
"""
|
221
|
-
|
222
|
-
fallback["simple"] = "👋 Chào mừng bạn đến với Vnstock! Tài liệu: https://vnstocks.com/docs | Cộng đồng: https://vnstocks.com/community"
|
223
|
-
|
224
|
-
return fallback
|
225
|
-
|
226
|
-
# Create singleton instance
|
227
|
-
manager = ContentManager()
|
228
|
-
|
229
|
-
def present(context="init"):
|
230
|
-
"""
|
231
|
-
Public API to present content
|
232
|
-
|
233
|
-
Args:
|
234
|
-
context (str): Context for content presentation (init, loop, periodic)
|
235
|
-
"""
|
236
|
-
return manager.present_content(context=context)
|
73
|
+
''';A[_E]='\n## 👋 Chào mừng bạn đến với Vnstock!\n\nCảm ơn bạn đã sử dụng package phân tích chứng khoán #1 tại Việt Nam\n\n* Tài liệu: [vnstocks.com/docs](https://vnstocks.com/docs)\n* Cộng đồng: [vnstocks.com/community](https://vnstocks.com/community)\n\nKhám phá các tính năng mới nhất và tham gia cộng đồng để nhận hỗ trợ.\n ';A[_B]='\n╔══════════════════════════════════════════════════════════╗\n║ ║\n║ 👋 Chào mừng bạn đến với Vnstock! ║\n║ ║\n║ Cảm ơn bạn đã sử dụng package phân tích ║\n║ chứng khoán #1 tại Việt Nam ║\n║ ║\n║ ✓ Tài liệu: https://vnstocks.com/docs ║\n║ ✓ Cộng đồng: https://vnstocks.com/community ║\n║ ║\n║ Khám phá các tính năng mới nhất và tham gia ║\n║ cộng đồng để nhận hỗ trợ. ║\n║ ║\n╚══════════════════════════════════════════════════════════╝\n ';A[_F]='👋 Chào mừng bạn đến với Vnstock! Tài liệu: https://vnstocks.com/docs | Cộng đồng: https://vnstocks.com/community'
|
74
|
+
return A
|
75
|
+
manager=ContentManager()
|
76
|
+
def present(context=_G):return manager.present_content(context=context)
|
vnai/scope/state.py
CHANGED
@@ -1,223 +1,74 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
_L='minimal'
|
2
|
+
_K='warnings'
|
3
|
+
_J='api_requests'
|
4
|
+
_I='last_error_time'
|
5
|
+
_H='startup_time'
|
6
|
+
_G='standard'
|
7
|
+
_F='function_calls'
|
8
|
+
_E='peak_memory'
|
9
|
+
_D='errors'
|
10
|
+
_C=True
|
11
|
+
_B=None
|
12
|
+
_A='execution_times'
|
13
|
+
import time,threading,json,os
|
8
14
|
from datetime import datetime
|
9
15
|
from pathlib import Path
|
10
|
-
|
11
16
|
class Tracker:
|
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
|
-
# Update metrics with stored values
|
71
|
-
for key, value in stored_metrics.items():
|
72
|
-
if key in self.metrics:
|
73
|
-
self.metrics[key] = value
|
74
|
-
except:
|
75
|
-
pass
|
76
|
-
|
77
|
-
def _save_metrics(self):
|
78
|
-
"""Save metrics to file"""
|
79
|
-
try:
|
80
|
-
with open(self.metrics_path, 'w') as f:
|
81
|
-
json.dump(self.metrics, f)
|
82
|
-
except:
|
83
|
-
pass
|
84
|
-
|
85
|
-
def _load_privacy_settings(self):
|
86
|
-
"""Load privacy settings"""
|
87
|
-
if self.privacy_config_path.exists():
|
88
|
-
try:
|
89
|
-
with open(self.privacy_config_path, 'r') as f:
|
90
|
-
settings = json.load(f)
|
91
|
-
self.privacy_level = settings.get("level", "standard")
|
92
|
-
except:
|
93
|
-
pass
|
94
|
-
|
95
|
-
def setup_privacy(self, level=None):
|
96
|
-
"""Configure privacy level for data collection"""
|
97
|
-
privacy_levels = {
|
98
|
-
"minimal": "Essential system data only",
|
99
|
-
"standard": "Performance metrics and errors",
|
100
|
-
"enhanced": "Detailed operation analytics"
|
101
|
-
}
|
102
|
-
|
103
|
-
if level is None:
|
104
|
-
# Default level
|
105
|
-
level = "standard"
|
106
|
-
|
107
|
-
if level not in privacy_levels:
|
108
|
-
raise ValueError(f"Invalid privacy level: {level}. Choose from {', '.join(privacy_levels.keys())}")
|
109
|
-
|
110
|
-
# Store preference
|
111
|
-
self.privacy_level = level
|
112
|
-
|
113
|
-
# Store in configuration file
|
114
|
-
with open(self.privacy_config_path, "w") as f:
|
115
|
-
json.dump({"level": level}, f)
|
116
|
-
|
117
|
-
return level
|
118
|
-
|
119
|
-
def get_privacy_level(self):
|
120
|
-
"""Get current privacy level"""
|
121
|
-
return self.privacy_level
|
122
|
-
|
123
|
-
def _start_background_collector(self):
|
124
|
-
"""Start background metrics collection"""
|
125
|
-
def collect_metrics():
|
126
|
-
while True:
|
127
|
-
try:
|
128
|
-
import psutil
|
129
|
-
|
130
|
-
# Update peak memory
|
131
|
-
current_process = psutil.Process()
|
132
|
-
memory_info = current_process.memory_info()
|
133
|
-
memory_usage = memory_info.rss / (1024 * 1024) # MB
|
134
|
-
|
135
|
-
if memory_usage > self.performance_metrics["peak_memory"]:
|
136
|
-
self.performance_metrics["peak_memory"] = memory_usage
|
137
|
-
|
138
|
-
# Save metrics periodically
|
139
|
-
self._save_metrics()
|
140
|
-
|
141
|
-
except:
|
142
|
-
pass
|
143
|
-
|
144
|
-
time.sleep(300) # Run every 5 minutes
|
145
|
-
|
146
|
-
# Start thread
|
147
|
-
thread = threading.Thread(target=collect_metrics, daemon=True)
|
148
|
-
thread.start()
|
149
|
-
|
150
|
-
def record(self, event_type, data=None):
|
151
|
-
"""Record an event"""
|
152
|
-
# Check privacy level
|
153
|
-
if self.privacy_level == "minimal" and event_type != "errors":
|
154
|
-
# In minimal mode, only track errors
|
155
|
-
return True
|
156
|
-
|
157
|
-
# Update counts
|
158
|
-
if event_type in self.metrics:
|
159
|
-
self.metrics[event_type] += 1
|
160
|
-
else:
|
161
|
-
self.metrics[event_type] = 1
|
162
|
-
|
163
|
-
# Special handling for errors
|
164
|
-
if event_type == "errors":
|
165
|
-
self.performance_metrics["last_error_time"] = datetime.now().isoformat()
|
166
|
-
|
167
|
-
# Special handling for function calls with timing data
|
168
|
-
if event_type == "function_calls" and data and "execution_time" in data:
|
169
|
-
# Keep up to 100 latest execution times
|
170
|
-
self.performance_metrics["execution_times"].append(data["execution_time"])
|
171
|
-
if len(self.performance_metrics["execution_times"]) > 100:
|
172
|
-
self.performance_metrics["execution_times"] = self.performance_metrics["execution_times"][-100:]
|
173
|
-
|
174
|
-
# Save if metrics change significantly
|
175
|
-
if self.metrics["function_calls"] % 100 == 0 or event_type == "errors":
|
176
|
-
self._save_metrics()
|
177
|
-
|
178
|
-
return True
|
179
|
-
|
180
|
-
def get_metrics(self):
|
181
|
-
"""Get current metrics"""
|
182
|
-
# Calculate derived metrics
|
183
|
-
avg_execution_time = 0
|
184
|
-
if self.performance_metrics["execution_times"]:
|
185
|
-
avg_execution_time = sum(self.performance_metrics["execution_times"]) / len(self.performance_metrics["execution_times"])
|
186
|
-
|
187
|
-
# Add derived metrics to output
|
188
|
-
output = self.metrics.copy()
|
189
|
-
output.update({
|
190
|
-
"avg_execution_time": avg_execution_time,
|
191
|
-
"peak_memory_mb": self.performance_metrics["peak_memory"],
|
192
|
-
"uptime": (datetime.now() - datetime.fromisoformat(self.metrics["startup_time"])).total_seconds(),
|
193
|
-
"privacy_level": self.privacy_level
|
194
|
-
})
|
195
|
-
|
196
|
-
return output
|
197
|
-
|
198
|
-
def reset(self):
|
199
|
-
"""Reset metrics"""
|
200
|
-
self.metrics = {
|
201
|
-
"startup_time": datetime.now().isoformat(),
|
202
|
-
"function_calls": 0,
|
203
|
-
"api_requests": 0,
|
204
|
-
"errors": 0,
|
205
|
-
"warnings": 0
|
206
|
-
}
|
207
|
-
|
208
|
-
self.performance_metrics = {
|
209
|
-
"execution_times": [],
|
210
|
-
"last_error_time": None,
|
211
|
-
"peak_memory": 0
|
212
|
-
}
|
213
|
-
|
214
|
-
self._save_metrics()
|
215
|
-
return True
|
216
|
-
|
217
|
-
# Create singleton instance
|
218
|
-
tracker = Tracker()
|
219
|
-
|
220
|
-
|
221
|
-
def record(event_type, data=None):
|
222
|
-
"""Record an event"""
|
223
|
-
return tracker.record(event_type, data)
|
17
|
+
_instance=_B;_lock=threading.Lock()
|
18
|
+
def __new__(A):
|
19
|
+
with A._lock:
|
20
|
+
if A._instance is _B:A._instance=super(Tracker,A).__new__(A);A._instance._initialize()
|
21
|
+
return A._instance
|
22
|
+
def _initialize(A):A.metrics={_H:datetime.now().isoformat(),_F:0,_J:0,_D:0,_K:0};A.performance_metrics={_A:[],_I:_B,_E:0};A.privacy_level=_G;A.home_dir=Path.home();A.project_dir=A.home_dir/'.vnstock';A.project_dir.mkdir(exist_ok=_C);A.data_dir=A.project_dir/'data';A.data_dir.mkdir(exist_ok=_C);A.metrics_path=A.data_dir/'usage_metrics.json';A.privacy_config_path=A.project_dir/'config'/'privacy.json';os.makedirs(os.path.dirname(A.privacy_config_path),exist_ok=_C);A._load_metrics();A._load_privacy_settings();A._start_background_collector()
|
23
|
+
def _load_metrics(A):
|
24
|
+
if A.metrics_path.exists():
|
25
|
+
try:
|
26
|
+
with open(A.metrics_path,'r')as C:D=json.load(C)
|
27
|
+
for(B,E)in D.items():
|
28
|
+
if B in A.metrics:A.metrics[B]=E
|
29
|
+
except:pass
|
30
|
+
def _save_metrics(A):
|
31
|
+
try:
|
32
|
+
with open(A.metrics_path,'w')as B:json.dump(A.metrics,B)
|
33
|
+
except:pass
|
34
|
+
def _load_privacy_settings(A):
|
35
|
+
if A.privacy_config_path.exists():
|
36
|
+
try:
|
37
|
+
with open(A.privacy_config_path,'r')as B:C=json.load(B);A.privacy_level=C.get('level',_G)
|
38
|
+
except:pass
|
39
|
+
def setup_privacy(B,level=_B):
|
40
|
+
A=level;C={_L:'Essential system data only',_G:'Performance metrics and errors','enhanced':'Detailed operation analytics'}
|
41
|
+
if A is _B:A=_G
|
42
|
+
if A not in C:raise ValueError(f"Invalid privacy level: {A}. Choose from {', '.join(C.keys())}")
|
43
|
+
B.privacy_level=A
|
44
|
+
with open(B.privacy_config_path,'w')as D:json.dump({'level':A},D)
|
45
|
+
return A
|
46
|
+
def get_privacy_level(A):return A.privacy_level
|
47
|
+
def _start_background_collector(A):
|
48
|
+
def B():
|
49
|
+
while _C:
|
50
|
+
try:
|
51
|
+
import psutil as C;D=C.Process();E=D.memory_info();B=E.rss/1048576
|
52
|
+
if B>A.performance_metrics[_E]:A.performance_metrics[_E]=B
|
53
|
+
A._save_metrics()
|
54
|
+
except:pass
|
55
|
+
time.sleep(300)
|
56
|
+
C=threading.Thread(target=B,daemon=_C);C.start()
|
57
|
+
def record(A,event_type,data=_B):
|
58
|
+
D='execution_time';C=data;B=event_type
|
59
|
+
if A.privacy_level==_L and B!=_D:return _C
|
60
|
+
if B in A.metrics:A.metrics[B]+=1
|
61
|
+
else:A.metrics[B]=1
|
62
|
+
if B==_D:A.performance_metrics[_I]=datetime.now().isoformat()
|
63
|
+
if B==_F and C and D in C:
|
64
|
+
A.performance_metrics[_A].append(C[D])
|
65
|
+
if len(A.performance_metrics[_A])>100:A.performance_metrics[_A]=A.performance_metrics[_A][-100:]
|
66
|
+
if A.metrics[_F]%100==0 or B==_D:A._save_metrics()
|
67
|
+
return _C
|
68
|
+
def get_metrics(A):
|
69
|
+
B=0
|
70
|
+
if A.performance_metrics[_A]:B=sum(A.performance_metrics[_A])/len(A.performance_metrics[_A])
|
71
|
+
C=A.metrics.copy();C.update({'avg_execution_time':B,'peak_memory_mb':A.performance_metrics[_E],'uptime':(datetime.now()-datetime.fromisoformat(A.metrics[_H])).total_seconds(),'privacy_level':A.privacy_level});return C
|
72
|
+
def reset(A):A.metrics={_H:datetime.now().isoformat(),_F:0,_J:0,_D:0,_K:0};A.performance_metrics={_A:[],_I:_B,_E:0};A._save_metrics();return _C
|
73
|
+
tracker=Tracker()
|
74
|
+
def record(event_type,data=_B):return tracker.record(event_type,data)
|
@@ -1,12 +1,12 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: vnai
|
3
|
-
Version: 2.0.
|
3
|
+
Version: 2.0.2
|
4
4
|
Summary: System optimization and resource management toolkit
|
5
5
|
Home-page: https://github.com/yourusername/vnai
|
6
6
|
Author: Your Name
|
7
7
|
Author-email: your.email@example.com
|
8
|
+
License: MIT
|
8
9
|
Classifier: Programming Language :: Python :: 3
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
10
10
|
Classifier: Operating System :: OS Independent
|
11
11
|
Classifier: Development Status :: 4 - Beta
|
12
12
|
Classifier: Intended Audience :: Developers
|
@@ -24,6 +24,7 @@ Dynamic: classifier
|
|
24
24
|
Dynamic: description
|
25
25
|
Dynamic: description-content-type
|
26
26
|
Dynamic: home-page
|
27
|
+
Dynamic: license
|
27
28
|
Dynamic: provides-extra
|
28
29
|
Dynamic: requires-dist
|
29
30
|
Dynamic: requires-python
|
@@ -0,0 +1,16 @@
|
|
1
|
+
vnai/__init__.py,sha256=iS0LLz-J9MnC1m3aLPBPGfc7B36t2v4YX5lK4pRoiEU,4791
|
2
|
+
vnai/beam/__init__.py,sha256=MG_4FkhQZyuKeaoQQh-KaULhxcGu370lGR6FVV5blU4,129
|
3
|
+
vnai/beam/metrics.py,sha256=nVKoe0SJg0TNCPvAOaE99ZyBJ9Tw5WSRydyAObH8FrA,2709
|
4
|
+
vnai/beam/pulse.py,sha256=5e21Ky6rVKD57bf8BzSA2lGzwHhohansoQzggrnEpIE,1580
|
5
|
+
vnai/beam/quota.py,sha256=zJWONvtd4pxYGDtdalrXHj1yBDasEzoQpMkENN38a70,7364
|
6
|
+
vnai/flow/__init__.py,sha256=BURTo8cXicmqqTbeB0qfXwVole0oGDVp_UxRSeh4qfA,80
|
7
|
+
vnai/flow/queue.py,sha256=pvC_HSjctR62Uzt4b3h3EANJXmrkKBm3iiNVIrlTnJA,1912
|
8
|
+
vnai/flow/relay.py,sha256=RU-paE3HVkgodPmNlAI8fAoVUcwufegY1WmsGL-sWpY,6676
|
9
|
+
vnai/scope/__init__.py,sha256=o7N7JjgSqIfQeDojgnxzV9gthEWL3YxxljnvRO9AXkQ,196
|
10
|
+
vnai/scope/profile.py,sha256=BHgX2yUQOMoJeUp_AaUWuc635bT1AJfw-FShNVRPikw,14985
|
11
|
+
vnai/scope/promo.py,sha256=5Q8VQszYdUaHt-3VPQrZsnxSSYgaXYG-oXnGridBM7U,7167
|
12
|
+
vnai/scope/state.py,sha256=JkVwJv8l_-ef201I_O1PHqFyp8KJ3VWyfmZnCltH18c,3283
|
13
|
+
vnai-2.0.2.dist-info/METADATA,sha256=_jz71GfABM6GmUKnPCwZ4vc2iSwJEQOphpbozgitdDw,1017
|
14
|
+
vnai-2.0.2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
15
|
+
vnai-2.0.2.dist-info/top_level.txt,sha256=4zI0qZHePCwvgSqXl4420sBcd0VzZn4MEcRsAIFae3k,5
|
16
|
+
vnai-2.0.2.dist-info/RECORD,,
|
vnai-2.0.1.dist-info/RECORD
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
vnai/__init__.py,sha256=iqCNvUVrP6JVFk5jBnCLpVyZdBSZkcfkzu64JNEijis,8895
|
2
|
-
vnai/beam/__init__.py,sha256=xKb_iu9aAPXCulI7dENrvqVIhelSD1mIqKE9Go3GAHw,200
|
3
|
-
vnai/beam/metrics.py,sha256=xVmVw93yhKeWzRZJurmrD9mWur16HyLJl_p1XqMwW_w,7187
|
4
|
-
vnai/beam/pulse.py,sha256=jp1YwjLaMhne2nYhM5PofveDsdrSp2YtewQ2jjE78Is,3470
|
5
|
-
vnai/beam/quota.py,sha256=I2rUC3z5s5ni9yTU8Vj7mpMA2uPGMT64s9PwbJUsr7s,20798
|
6
|
-
vnai/flow/__init__.py,sha256=K3OeabzAWGrdPgTAOlDqrJh2y9aQW2pgLZg8tblN3ho,147
|
7
|
-
vnai/flow/queue.py,sha256=b9YKUbiXDZRC3fVgEnA77EO0EMXAi8eCoBkHnAUI5Sc,4162
|
8
|
-
vnai/flow/relay.py,sha256=RtIPRZ3BlQd-XgTbisJg0iC1HqikAjHGyyo8aTj_fUw,15766
|
9
|
-
vnai/scope/__init__.py,sha256=overJZ_UiEfBRNcSieE1GPU_9X3oS4C5l6JeBaFFVxk,267
|
10
|
-
vnai/scope/profile.py,sha256=xKEqDAaeZvRZMn1YK8qS6ILz7DRYxRkd_IJxLm7O-cs,30738
|
11
|
-
vnai/scope/promo.py,sha256=bIVok3eW3GXGGu-OdDeMggrvC_JfpT6blYVJuj35k7s,11031
|
12
|
-
vnai/scope/state.py,sha256=LlcZNKBy2mcAnD765BO2Tlv3Zzbak2TOEz4RUPMCFZ8,7490
|
13
|
-
vnai-2.0.1.dist-info/METADATA,sha256=9gyeWPMPAoIUmQPeeyow2USwrGO3Ir8Q3ivMNalh40o,1038
|
14
|
-
vnai-2.0.1.dist-info/WHEEL,sha256=tTnHoFhvKQHCh4jz3yCn0WPTYIy7wXx3CJtJ7SJGV7c,91
|
15
|
-
vnai-2.0.1.dist-info/top_level.txt,sha256=4zI0qZHePCwvgSqXl4420sBcd0VzZn4MEcRsAIFae3k,5
|
16
|
-
vnai-2.0.1.dist-info/RECORD,,
|
File without changes
|