pkscreener 0.46.20250221.728__tar.gz → 0.46.20250221.731__tar.gz
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.
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/PKG-INFO +8 -8
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/README.md +5 -5
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/README.txt +5 -5
- pkscreener-0.46.20250221.731/pkscreener/classes/PKAnalytics.py +157 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PKUserRegistration.py +2 -0
- pkscreener-0.46.20250221.731/pkscreener/classes/__init__.py +1 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/globals.py +29 -4
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/pkscreenerbot.py +8 -2
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/requirements.txt +1 -1
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener.egg-info/PKG-INFO +8 -8
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener.egg-info/requires.txt +1 -1
- pkscreener-0.46.20250221.728/pkscreener/classes/PKAnalytics.py +0 -122
- pkscreener-0.46.20250221.728/pkscreener/classes/__init__.py +0 -1
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/LICENSE +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/Disclaimer.txt +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/LICENSE-Others.txt +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/LICENSE.txt +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/LogoWM.txt +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/__init__.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/ArtTexts.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/AssetsManager.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/Backtest.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/Barometer.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/BaseScreeningStatistics.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/CandlePatterns.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/Changelog.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/ConfigManager.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/ConsoleMenuUtility.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/ConsoleUtility.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/Fetcher.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/GlobalStore.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/ImageUtility.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/MarketMonitor.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/MarketStatus.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/MenuOptions.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/Messenger.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/OtaUpdater.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PKDataService.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PKDemoHandler.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PKMarketOpenCloseAnalyser.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PKPremiumHandler.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PKScanRunner.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PKScheduledTaskProgress.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PKScheduler.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PKSpreadsheets.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PKTask.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/Pktalib.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/Portfolio.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PortfolioXRay.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/ScreeningStatistics.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/StockScreener.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/StockSentiment.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/UserMenuChoicesHandler.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/Utility.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/WorkflowManager.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/keys.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/courbd.ttf +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/pkscreener.ini +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/pkscreenercli.py +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener.egg-info/SOURCES.txt +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener.egg-info/dependency_links.txt +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener.egg-info/entry_points.txt +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener.egg-info/not-zip-safe +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener.egg-info/top_level.txt +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/setup.cfg +0 -0
- {pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/setup.py +0 -0
@@ -1,9 +1,9 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: pkscreener
|
3
|
-
Version: 0.46.20250221.
|
3
|
+
Version: 0.46.20250221.731
|
4
4
|
Summary: A Python-based stock screener for NSE, India with alerts to Telegram Channel (pkscreener)
|
5
5
|
Home-page: https://github.com/pkjmesra/pkscreener
|
6
|
-
Download-URL: https://github.com/pkjmesra/pkscreener/archive/v0.46.20250221.
|
6
|
+
Download-URL: https://github.com/pkjmesra/pkscreener/archive/v0.46.20250221.731.zip
|
7
7
|
Author: pkjmesra
|
8
8
|
Author-email: pkjmesra@gmail.com
|
9
9
|
License: OSI Approved (MIT)
|
@@ -33,7 +33,7 @@ Requires-Dist: pandas
|
|
33
33
|
Requires-Dist: pandas_ta
|
34
34
|
Requires-Dist: pefile<2024.8.26,>=2023.2.7
|
35
35
|
Requires-Dist: Pillow
|
36
|
-
Requires-Dist: PKDevTools>=0.13.20250126.
|
36
|
+
Requires-Dist: PKDevTools>=0.13.20250126.238
|
37
37
|
Requires-Dist: PKNSETools>=0.1.20250122.138
|
38
38
|
Requires-Dist: Pyarrow
|
39
39
|
Requires-Dist: pyppeteer
|
@@ -392,15 +392,15 @@ After you have finished the run, go to that copied path, zip the contents of the
|
|
392
392
|
[MADE-IN-INDIA-badge]: https://img.shields.io/badge/MADE%20WITH%20%E2%9D%A4%20IN-INDIA-orange
|
393
393
|
[MADE-IN-INDIA]: https://en.wikipedia.org/wiki/India
|
394
394
|
[Windows-badge]: https://img.shields.io/badge/Windows-0078D6?logo=windows&logoColor=white
|
395
|
-
[Windows]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
395
|
+
[Windows]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli.exe
|
396
396
|
[Linux-badge_x64]: https://img.shields.io/badge/Linux(x64)-FCC624?logo=linux&logoColor=black
|
397
|
-
[Linux_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
397
|
+
[Linux_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli_x64.bin
|
398
398
|
[Linux-badge_arm64]: https://img.shields.io/badge/Linux(arm64)-FCC624?logo=linux&logoColor=black
|
399
|
-
[Linux_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
399
|
+
[Linux_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli_arm64.bin
|
400
400
|
[Mac OS-badge_x64]: https://img.shields.io/badge/mac%20os(x64)-D3D3D3?logo=apple&logoColor=000000
|
401
|
-
[Mac OS_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
401
|
+
[Mac OS_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli_x64.run
|
402
402
|
[Mac OS-badge_arm64]: https://img.shields.io/badge/mac%20os(arm64)-D3D3D3?logo=apple&logoColor=000000
|
403
|
-
[Mac OS_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
403
|
+
[Mac OS_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli_arm64.run
|
404
404
|
[GitHub release (latest by date)-badge]: https://img.shields.io/github/v/release/pkjmesra/PKScreener
|
405
405
|
[GitHub release (latest by date)]: https://github.com/pkjmesra/PKScreener/releases/latest
|
406
406
|
[pypi-badge]: https://img.shields.io/pypi/v/pkscreener.svg?style=flat-square
|
@@ -328,15 +328,15 @@ After you have finished the run, go to that copied path, zip the contents of the
|
|
328
328
|
[MADE-IN-INDIA-badge]: https://img.shields.io/badge/MADE%20WITH%20%E2%9D%A4%20IN-INDIA-orange
|
329
329
|
[MADE-IN-INDIA]: https://en.wikipedia.org/wiki/India
|
330
330
|
[Windows-badge]: https://img.shields.io/badge/Windows-0078D6?logo=windows&logoColor=white
|
331
|
-
[Windows]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
331
|
+
[Windows]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli.exe
|
332
332
|
[Linux-badge_x64]: https://img.shields.io/badge/Linux(x64)-FCC624?logo=linux&logoColor=black
|
333
|
-
[Linux_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
333
|
+
[Linux_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli_x64.bin
|
334
334
|
[Linux-badge_arm64]: https://img.shields.io/badge/Linux(arm64)-FCC624?logo=linux&logoColor=black
|
335
|
-
[Linux_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
335
|
+
[Linux_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli_arm64.bin
|
336
336
|
[Mac OS-badge_x64]: https://img.shields.io/badge/mac%20os(x64)-D3D3D3?logo=apple&logoColor=000000
|
337
|
-
[Mac OS_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
337
|
+
[Mac OS_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli_x64.run
|
338
338
|
[Mac OS-badge_arm64]: https://img.shields.io/badge/mac%20os(arm64)-D3D3D3?logo=apple&logoColor=000000
|
339
|
-
[Mac OS_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
339
|
+
[Mac OS_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli_arm64.run
|
340
340
|
[GitHub release (latest by date)-badge]: https://img.shields.io/github/v/release/pkjmesra/PKScreener
|
341
341
|
[GitHub release (latest by date)]: https://github.com/pkjmesra/PKScreener/releases/latest
|
342
342
|
[pypi-badge]: https://img.shields.io/pypi/v/pkscreener.svg?style=flat-square
|
@@ -328,15 +328,15 @@ After you have finished the run, go to that copied path, zip the contents of the
|
|
328
328
|
[MADE-IN-INDIA-badge]: https://img.shields.io/badge/MADE%20WITH%20%E2%9D%A4%20IN-INDIA-orange
|
329
329
|
[MADE-IN-INDIA]: https://en.wikipedia.org/wiki/India
|
330
330
|
[Windows-badge]: https://img.shields.io/badge/Windows-0078D6?logo=windows&logoColor=white
|
331
|
-
[Windows]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
331
|
+
[Windows]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli.exe
|
332
332
|
[Linux-badge_x64]: https://img.shields.io/badge/Linux(x64)-FCC624?logo=linux&logoColor=black
|
333
|
-
[Linux_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
333
|
+
[Linux_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli_x64.bin
|
334
334
|
[Linux-badge_arm64]: https://img.shields.io/badge/Linux(arm64)-FCC624?logo=linux&logoColor=black
|
335
|
-
[Linux_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
335
|
+
[Linux_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli_arm64.bin
|
336
336
|
[Mac OS-badge_x64]: https://img.shields.io/badge/mac%20os(x64)-D3D3D3?logo=apple&logoColor=000000
|
337
|
-
[Mac OS_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
337
|
+
[Mac OS_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli_x64.run
|
338
338
|
[Mac OS-badge_arm64]: https://img.shields.io/badge/mac%20os(arm64)-D3D3D3?logo=apple&logoColor=000000
|
339
|
-
[Mac OS_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
339
|
+
[Mac OS_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli_arm64.run
|
340
340
|
[GitHub release (latest by date)-badge]: https://img.shields.io/github/v/release/pkjmesra/PKScreener
|
341
341
|
[GitHub release (latest by date)]: https://github.com/pkjmesra/PKScreener/releases/latest
|
342
342
|
[pypi-badge]: https://img.shields.io/pypi/v/pkscreener.svg?style=flat-square
|
@@ -0,0 +1,157 @@
|
|
1
|
+
#!/usr/bin/python3
|
2
|
+
"""
|
3
|
+
The MIT License (MIT)
|
4
|
+
|
5
|
+
Copyright (c) 2023 pkjmesra
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
9
|
+
in the Software without restriction, including without limitation the rights
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
12
|
+
furnished to do so, subject to the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
15
|
+
copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
23
|
+
SOFTWARE.
|
24
|
+
|
25
|
+
"""
|
26
|
+
|
27
|
+
import os
|
28
|
+
# import base64
|
29
|
+
from sys import platform
|
30
|
+
import platform
|
31
|
+
import getpass
|
32
|
+
# import git
|
33
|
+
import json
|
34
|
+
# import io
|
35
|
+
import time
|
36
|
+
from PKDevTools.classes.Fetcher import fetcher
|
37
|
+
from PKDevTools.classes.Utils import random_user_agent
|
38
|
+
# from PKDevTools.classes.PKDateUtilities import PKDateUtilities
|
39
|
+
# from PKDevTools.classes import Archiver
|
40
|
+
from PKDevTools.classes.Singleton import SingletonType, SingletonMixin
|
41
|
+
from PKDevTools.classes.pubsub.publisher import PKUserService
|
42
|
+
from PKDevTools.classes.pubsub.subscriber import notification_service
|
43
|
+
from pkscreener.classes import VERSION
|
44
|
+
|
45
|
+
class PKAnalyticsService(SingletonMixin, metaclass=SingletonType):
|
46
|
+
def __init__(self):
|
47
|
+
super(PKAnalyticsService, self).__init__()
|
48
|
+
self.username = ""
|
49
|
+
self.locationInfo = ""
|
50
|
+
self.os = platform.system()
|
51
|
+
self.os_version = platform.release()
|
52
|
+
self.app_version = VERSION
|
53
|
+
self.start_time = time.time()
|
54
|
+
self.isRunner = "RUNNER" in os.environ.keys()
|
55
|
+
|
56
|
+
def collectMetrics(self,user=None):
|
57
|
+
try:
|
58
|
+
self.userName = self.getUserName()
|
59
|
+
metrics = self.getApproxLocationInfo()
|
60
|
+
self.locationInfo = metrics
|
61
|
+
self.send_event("app_start")
|
62
|
+
except KeyboardInterrupt: # pragma: no cover
|
63
|
+
raise KeyboardInterrupt
|
64
|
+
except Exception as e: # pragma: no cover
|
65
|
+
pass
|
66
|
+
|
67
|
+
def getUserName(self):
|
68
|
+
try:
|
69
|
+
username = os.getlogin()
|
70
|
+
if username is None or len(username) == 0:
|
71
|
+
username = os.environ.get('username') if platform.startswith("win") else os.environ.get("USER")
|
72
|
+
if username is None or len(username) == 0:
|
73
|
+
username = os.environ.get('USERPROFILE')
|
74
|
+
if username is None or len(username) == 0:
|
75
|
+
username = os.path.expandvars("%userprofile%") if platform.startswith("win") else getpass.getuser()
|
76
|
+
except KeyboardInterrupt: # pragma: no cover
|
77
|
+
raise KeyboardInterrupt
|
78
|
+
except: # pragma: no cover
|
79
|
+
username = f"Unidentified-{self.os}"
|
80
|
+
pass
|
81
|
+
return username
|
82
|
+
|
83
|
+
def getApproxLocationInfo(self):
|
84
|
+
try:
|
85
|
+
url = 'http://ipinfo.io/json'
|
86
|
+
f = fetcher()
|
87
|
+
response = f.fetchURL(url=url,timeout=5,headers={'user-agent': f'{random_user_agent()}'})
|
88
|
+
data = json.loads(response.text)
|
89
|
+
except: # pragma: no cover
|
90
|
+
data = {"locationInfo":f"Unidentified-{self.os}"}
|
91
|
+
pass
|
92
|
+
return data
|
93
|
+
|
94
|
+
def send_event(self,event_name,params={}):
|
95
|
+
event_params = {
|
96
|
+
"user_id": self.userName,
|
97
|
+
"os": self.os,
|
98
|
+
"os_version": self.os_version,
|
99
|
+
"app_version": self.app_version,
|
100
|
+
"elapsed_time": str(time.time() - self.start_time),
|
101
|
+
"is_Runner": self.isRunner
|
102
|
+
}
|
103
|
+
if len(params) > 0:
|
104
|
+
for key in params:
|
105
|
+
event_params[key] = params[key]
|
106
|
+
if self.isRunner:
|
107
|
+
try:
|
108
|
+
owner = os.popen('git ls-remote --get-url origin | cut -d/ -f4').read().replace("\n","")
|
109
|
+
repo = os.popen('git ls-remote --get-url origin | cut -d/ -f5').read().replace(".git","").replace("\n","")
|
110
|
+
event_params["repo_owner"] = owner
|
111
|
+
event_params["repo"] = repo
|
112
|
+
except:
|
113
|
+
pass
|
114
|
+
for key in self.locationInfo.keys():
|
115
|
+
if key not in ["readme"]:
|
116
|
+
event_params[key] = self.locationInfo[key]
|
117
|
+
PKUserService().send_event(event_name, event_params)
|
118
|
+
|
119
|
+
# def tryCommitAnalytics(self, userDict={},username="Unidentified"):
|
120
|
+
# import git.repo
|
121
|
+
# repo_clone_url = "https://github.com/pkjmesra/PKUserAnalytics.git"
|
122
|
+
# local_repo = os.path.join(Archiver.get_user_data_dir(),"PKUserAnalytics")
|
123
|
+
# try:
|
124
|
+
# test_branch = "main"
|
125
|
+
# repo = git.Repo.clone_from(repo_clone_url, local_repo)
|
126
|
+
# repo.git.checkout(test_branch)
|
127
|
+
# except Exception as e: # pragma: no cover
|
128
|
+
# repo = git.Repo(local_repo)
|
129
|
+
# repo.git.checkout(test_branch)
|
130
|
+
# pass
|
131
|
+
# remote = git.remote.Remote(repo=repo,name="origin")
|
132
|
+
# repo.git.reset('--hard','origin/main')
|
133
|
+
# remote.pull()
|
134
|
+
# # write to file in working directory
|
135
|
+
# scanResultFilesPath = os.path.join(local_repo, f"users-{PKDateUtilities.currentDateTime().strftime('%Y-%m-%d')}.txt")
|
136
|
+
# records = {}
|
137
|
+
# existingUserRecords = [userDict]
|
138
|
+
# mode = "rb+" if os.path.exists(scanResultFilesPath) else "wb+"
|
139
|
+
# with open(scanResultFilesPath, mode) as f:
|
140
|
+
# allUsers = f.read()
|
141
|
+
# if allUsers is not None and len(allUsers) > 0:
|
142
|
+
# allUsers = base64.b64decode(allUsers).decode("utf-8").replace("'","\"")
|
143
|
+
# records = json.loads(allUsers)
|
144
|
+
# if records is None:
|
145
|
+
# records = {}
|
146
|
+
# existingUserRecords = records.get(username)
|
147
|
+
# if existingUserRecords is not None:
|
148
|
+
# existingUserRecords.append(userDict)
|
149
|
+
# else:
|
150
|
+
# existingUserRecords = [userDict]
|
151
|
+
# records[username] = existingUserRecords
|
152
|
+
# encoded = base64.b64encode(bytes(str(records).replace("'","\""), "utf-8"))
|
153
|
+
# f.writelines(io.BytesIO(encoded))
|
154
|
+
# repo.index.add([scanResultFilesPath])
|
155
|
+
# repo.index.commit("[User-Analytics]")
|
156
|
+
# remote = git.remote.Remote(repo=repo,name="origin")
|
157
|
+
# remote.push()
|
@@ -94,6 +94,8 @@ class PKUserRegistration(SingletonMixin, metaclass=SingletonType):
|
|
94
94
|
@classmethod
|
95
95
|
def login(self, trialCount=0):
|
96
96
|
try:
|
97
|
+
from pkscreener.classes.PKAnalytics import PKAnalyticsService
|
98
|
+
PKAnalyticsService().collectMetrics()
|
97
99
|
if "RUNNER" in os.environ.keys():
|
98
100
|
return ValidationResult.Success
|
99
101
|
except: # pragma: no cover
|
@@ -0,0 +1 @@
|
|
1
|
+
VERSION='0.46.20250221.731'
|
@@ -110,6 +110,7 @@ from pkscreener.classes.PKScanRunner import PKScanRunner
|
|
110
110
|
from pkscreener.classes.PKMarketOpenCloseAnalyser import PKMarketOpenCloseAnalyser
|
111
111
|
from pkscreener.classes.PKPremiumHandler import PKPremiumHandler
|
112
112
|
from pkscreener.classes.AssetsManager import PKAssetsManager
|
113
|
+
from pkscreener.classes.PKAnalytics import PKAnalyticsService
|
113
114
|
|
114
115
|
if __name__ == '__main__':
|
115
116
|
multiprocessing.freeze_support()
|
@@ -214,6 +215,7 @@ def getDownloadChoices(defaultAnswer=None):
|
|
214
215
|
+ colorText.END
|
215
216
|
+ " already exists. Exiting as user chose not to replace it!"
|
216
217
|
)
|
218
|
+
PKAnalyticsService().send_event("app_exit")
|
217
219
|
sys.exit(0)
|
218
220
|
else:
|
219
221
|
pattern = f"{'intraday_' if intraday else ''}stock_data_*.pkl"
|
@@ -268,6 +270,7 @@ def getScannerMenuChoices(
|
|
268
270
|
+ " [+] Press <Enter> to Exit!"
|
269
271
|
+ colorText.END
|
270
272
|
)
|
273
|
+
PKAnalyticsService().send_event("app_exit")
|
271
274
|
sys.exit(0)
|
272
275
|
except Exception as e: # pragma: no cover
|
273
276
|
default_logger().debug(e, exc_info=True)
|
@@ -573,6 +576,7 @@ def initExecution(menuOption=None):
|
|
573
576
|
+ " [+] Press <Enter> to Exit!"
|
574
577
|
+ colorText.END
|
575
578
|
)
|
579
|
+
PKAnalyticsService().send_event("app_exit")
|
576
580
|
sys.exit(0)
|
577
581
|
elif selectedMenu.menuKey in ["B", "C", "G", "H", "U", "T", "S", "E", "X", "Y", "M", "D", "I", "L","F"]:
|
578
582
|
ConsoleUtility.PKConsoleTools.clearScreen(forceTop=True)
|
@@ -679,6 +683,7 @@ def initPostLevel1Execution(indexOption, executeOption=None, skip=[], retrial=Fa
|
|
679
683
|
if indexOption == "S":
|
680
684
|
ensureMenusLoaded("X",indexOption,executeOption)
|
681
685
|
if not PKPremiumHandler.hasPremium(selectedMenu):
|
686
|
+
PKAnalyticsService().send_event("app_exit")
|
682
687
|
sys.exit(0)
|
683
688
|
indexKeys = level1_index_options_sectoral.keys()
|
684
689
|
stockIndexCode = input(
|
@@ -917,6 +922,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
917
922
|
mkt_monitor_dict = mp_manager.dict()
|
918
923
|
# Let's start monitoring the market monitor
|
919
924
|
startMarketMonitor(mkt_monitor_dict,keyboardInterruptEvent)
|
925
|
+
PKAnalyticsService().send_event("market_monitor_started")
|
920
926
|
|
921
927
|
keyboardInterruptEventFired = False
|
922
928
|
if stockDictPrimary is None:
|
@@ -947,12 +953,15 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
947
953
|
if menuOption in ["F", "M", "S", "B", "G", "C", "P", "D"] or selectedMenu.isPremium:
|
948
954
|
ensureMenusLoaded(menuOption,indexOption,executeOption)
|
949
955
|
if not PKPremiumHandler.hasPremium(selectedMenu):
|
956
|
+
PKAnalyticsService().send_event(f"non_premium_user_{menuOption}")
|
957
|
+
PKAnalyticsService().send_event("app_exit")
|
950
958
|
sys.exit(0)
|
951
959
|
if menuOption in ["M", "D", "I", "L", "F"]:
|
952
960
|
launcher = f'"{sys.argv[0]}"' if " " in sys.argv[0] else sys.argv[0]
|
953
961
|
launcher = f"python3.12 {launcher}" if (launcher.endswith(".py\"") or launcher.endswith(".py")) else launcher
|
954
962
|
if menuOption in ["M"]:
|
955
963
|
OutputControls().printOutput(f"{colorText.GREEN}Launching PKScreener in monitoring mode. If it does not launch, please try with the following:{colorText.END}\n{colorText.FAIL}{launcher} --systemlaunched -a Y -m 'X'{colorText.END}\n{colorText.WARN}Press Ctrl + C to exit monitoring mode.{colorText.END}")
|
964
|
+
PKAnalyticsService().send_event(f"monitor_{menuOption}")
|
956
965
|
sleep(2)
|
957
966
|
os.system(f"{launcher} --systemlaunched -a Y -m 'X'")
|
958
967
|
elif menuOption in ["D"]:
|
@@ -963,11 +972,13 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
963
972
|
OutputControls().printOutput(colorText.END, end="")
|
964
973
|
if selDownloadOption.upper() == "D":
|
965
974
|
OutputControls().printOutput(f"{colorText.GREEN}Launching PKScreener to Download daily OHLC data. If it does not launch, please try with the following:{colorText.END}\n{colorText.FAIL}{launcher} -a Y -e -d{colorText.END}\n{colorText.WARN}Press Ctrl + C to exit at any time.{colorText.END}")
|
975
|
+
PKAnalyticsService().send_event(f"{menuOption}_{selDownloadOption.upper()}")
|
966
976
|
sleep(2)
|
967
977
|
os.system(f"{launcher} -a Y -e -d")
|
968
978
|
return None, None
|
969
979
|
elif selDownloadOption.upper() == "I":
|
970
980
|
OutputControls().printOutput(f"{colorText.GREEN}Launching PKScreener to Download intraday OHLC data. If it does not launch, please try with the following:{colorText.END}\n{colorText.FAIL}{launcher} -a Y -e -d -i 1m{colorText.END}\n{colorText.WARN}Press Ctrl + C to exit at any time.{colorText.END}")
|
981
|
+
PKAnalyticsService().send_event(f"{menuOption}_{selDownloadOption.upper()}")
|
971
982
|
sleep(2)
|
972
983
|
os.system(f"{launcher} -a Y -e -d -i 1m")
|
973
984
|
return None, None
|
@@ -975,6 +986,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
975
986
|
selectedMenu = m1.find(selDownloadOption.upper())
|
976
987
|
ConsoleUtility.PKConsoleTools.clearScreen(forceTop=True)
|
977
988
|
m2.renderForMenu(selectedMenu)
|
989
|
+
PKAnalyticsService().send_event(f"{menuOption}_{selDownloadOption.upper()}")
|
978
990
|
selDownloadOption = input(colorText.FAIL + " [+] Select option: ") or "12"
|
979
991
|
OutputControls().printOutput(colorText.END, end="")
|
980
992
|
filePrefix = "Download"
|
@@ -986,6 +998,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
986
998
|
+ ".csv"
|
987
999
|
)
|
988
1000
|
filePath = os.path.join(Archiver.get_user_indices_dir(), filename)
|
1001
|
+
PKAnalyticsService().send_event(f"{menuOption}_{selDownloadOption.upper()}")
|
989
1002
|
if selDownloadOption.upper() == "15":
|
990
1003
|
nasdaq = PKNasdaqIndexFetcher(configManager)
|
991
1004
|
_,nasdaq_df = nasdaq.fetchNasdaqIndexConstituents()
|
@@ -998,6 +1011,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
998
1011
|
input(f"{colorText.GREEN}Press any key to continue...{colorText.END}")
|
999
1012
|
return None, None
|
1000
1013
|
elif selDownloadOption.upper() == "M":
|
1014
|
+
PKAnalyticsService().send_event(f"{menuOption}_{selDownloadOption.upper()}")
|
1001
1015
|
return None, None
|
1002
1016
|
else:
|
1003
1017
|
fileContents = fetcher.fetchFileFromHostServer(filePath=filePath,tickerOption=int(selDownloadOption),fileContents="")
|
@@ -1022,12 +1036,14 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
1022
1036
|
+ PKDateUtilities.currentDateTime().strftime("%d-%m-%y_%H.%M.%S")
|
1023
1037
|
+ ".csv"
|
1024
1038
|
)
|
1039
|
+
PKAnalyticsService().send_event(f"{menuOption}_{selDownloadOption.upper()}")
|
1025
1040
|
filePath = os.path.join(Archiver.get_user_reports_dir(), filename)
|
1026
1041
|
if selDownloadOption.upper() == "M":
|
1027
1042
|
return None, None
|
1028
1043
|
else:
|
1029
1044
|
indexOption = int(selDownloadOption)
|
1030
1045
|
if indexOption > 0 and indexOption <= 14:
|
1046
|
+
PKAnalyticsService().send_event(f"{menuOption}_{selDownloadOption.upper()}")
|
1031
1047
|
shouldSuppress = not OutputControls().enableMultipleLineOutput
|
1032
1048
|
with SuppressOutput(suppress_stderr=shouldSuppress, suppress_stdout=shouldSuppress):
|
1033
1049
|
listStockCodes = fetcher.fetchStockCodes(indexOption, stockCode=None)
|
@@ -1043,12 +1059,15 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
1043
1059
|
OutputControls().printOutput(f"{colorText.FAIL}We encountered an error. Please try again!{colorText.END}")
|
1044
1060
|
input(f"{colorText.GREEN}Press any key to continue...{colorText.END}")
|
1045
1061
|
elif selDownloadOption.upper() == "M":
|
1062
|
+
PKAnalyticsService().send_event(f"{menuOption}_{selDownloadOption.upper()}")
|
1046
1063
|
return None, None
|
1047
1064
|
elif menuOption in ["L"]:
|
1065
|
+
PKAnalyticsService().send_event(f"{menuOption}")
|
1048
1066
|
OutputControls().printOutput(f"{colorText.GREEN}Launching PKScreener to collect logs. If it does not launch, please try with the following:{colorText.END}\n{colorText.FAIL}{launcher} -a Y -l{colorText.END}\n{colorText.WARN}Press Ctrl + C to exit at any time.{colorText.END}")
|
1049
1067
|
sleep(2)
|
1050
1068
|
os.system(f"{launcher} -a Y -l")
|
1051
1069
|
if menuOption in ["F"]:
|
1070
|
+
PKAnalyticsService().send_event(f"{menuOption}")
|
1052
1071
|
indexOption = 0
|
1053
1072
|
selectedChoice["0"] = "F"
|
1054
1073
|
selectedChoice["1"] = "0"
|
@@ -1754,7 +1773,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
1754
1773
|
maLength = float(maLength)
|
1755
1774
|
if maLength > 0:
|
1756
1775
|
maLength = 0 - maLength
|
1757
|
-
|
1776
|
+
|
1758
1777
|
if executeOption == MAX_MENU_OPTION:
|
1759
1778
|
ConsoleUtility.PKConsoleTools.getLastScreenedResults(defaultAnswer)
|
1760
1779
|
return None, None
|
@@ -1770,6 +1789,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
1770
1789
|
if str(indexOption).isnumeric() and int(indexOption) > 1 and executeOption <= MAX_SUPPORTED_MENU_OPTION:
|
1771
1790
|
ensureMenusLoaded(menuOption,indexOption,executeOption)
|
1772
1791
|
if not PKPremiumHandler.hasPremium(m2.find(str(executeOption).upper())):
|
1792
|
+
PKAnalyticsService().send_event("app_exit")
|
1773
1793
|
sys.exit(0)
|
1774
1794
|
if (
|
1775
1795
|
not str(indexOption).isnumeric() and str(indexOption).upper() in ["W", "E", "M", "N", "Z", "S"]
|
@@ -1781,6 +1801,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
1781
1801
|
try:
|
1782
1802
|
if str(indexOption).upper() in ["W", "E", "S"]:
|
1783
1803
|
if not PKPremiumHandler.hasPremium(m1.find(str(indexOption).upper())):
|
1804
|
+
PKAnalyticsService().send_event("app_exit")
|
1784
1805
|
sys.exit(0)
|
1785
1806
|
if indexOption == "W":
|
1786
1807
|
listStockCodes = fetcher.fetchWatchlist()
|
@@ -1790,6 +1811,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
1790
1811
|
+ f" [+] Please create the watchlist.xlsx file in {os.getcwd()} and Restart the Program!"
|
1791
1812
|
+ colorText.END
|
1792
1813
|
)
|
1814
|
+
PKAnalyticsService().send_event("app_exit")
|
1793
1815
|
sys.exit(0)
|
1794
1816
|
elif indexOption == "N":
|
1795
1817
|
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
|
@@ -1824,6 +1846,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
1824
1846
|
+ " [+] Press <Enter> to Exit!"
|
1825
1847
|
+ colorText.END
|
1826
1848
|
)
|
1849
|
+
PKAnalyticsService().send_event("app_exit")
|
1827
1850
|
sys.exit(0)
|
1828
1851
|
elif indexOption == "E":
|
1829
1852
|
return handleMonitorFiveEMA()
|
@@ -1885,7 +1908,6 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
1885
1908
|
except: # pragma: no cover
|
1886
1909
|
stockDictPrimary,stockDictSecondary = loadDatabaseOrFetch(downloadOnly, listStockCodes, menuOption, indexOption)
|
1887
1910
|
pass
|
1888
|
-
|
1889
1911
|
loadCount = len(stockDictPrimary) if stockDictPrimary is not None else 0
|
1890
1912
|
# Let's use screening only for the stocks for which we could get the data.
|
1891
1913
|
savedOrDownloadedKeys = list(stockDictPrimary.keys())
|
@@ -2179,6 +2201,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
2179
2201
|
OutputControls().printOutput(colorText.END, end="")
|
2180
2202
|
ensureMenusLoaded(menuOption,indexOption,executeOption)
|
2181
2203
|
if not PKPremiumHandler.hasPremium(m0.find(str(pinOption).upper())):
|
2204
|
+
PKAnalyticsService().send_event("app_exit")
|
2182
2205
|
sys.exit(0)
|
2183
2206
|
if pinOption in ["1","2"]:
|
2184
2207
|
if pinOption in ["2"]:
|
@@ -2508,7 +2531,6 @@ def addOrRunPipedMenus():
|
|
2508
2531
|
def describeUser():
|
2509
2532
|
if not configManager.enableUsageAnalytics:
|
2510
2533
|
return
|
2511
|
-
from pkscreener.classes.PKAnalytics import PKAnalyticsService
|
2512
2534
|
service = PKAnalyticsService()
|
2513
2535
|
func_args = None
|
2514
2536
|
task = PKTask("Usage Analytics",
|
@@ -2736,6 +2758,7 @@ def handleExitRequest(executeOption):
|
|
2736
2758
|
+ " [+] Press <Enter> to Exit!"
|
2737
2759
|
+ colorText.END
|
2738
2760
|
)
|
2761
|
+
PKAnalyticsService().send_event("app_exit")
|
2739
2762
|
sys.exit(0)
|
2740
2763
|
|
2741
2764
|
def handleMenu_XBG(menuOption, indexOption, executeOption):
|
@@ -2845,7 +2868,7 @@ def updateMenuChoiceHierarchy():
|
|
2845
2868
|
if ((":0:" in runOptionName or "_0_" in runOptionName) and userPassedArgs.progressstatus is not None) or userPassedArgs.progressstatus is not None:
|
2846
2869
|
runOptionName = userPassedArgs.progressstatus.split("=>")[0].split(" [+] ")[1].strip()
|
2847
2870
|
reportTitle = f"{runOptionName} | {reportTitle}" if runOptionName is not None else reportTitle
|
2848
|
-
|
2871
|
+
PKAnalyticsService().send_event(runOptionName)
|
2849
2872
|
OutputControls().printOutput(
|
2850
2873
|
colorText.FAIL
|
2851
2874
|
+ f" [+] You chose: {reportTitle} "
|
@@ -3840,6 +3863,7 @@ def saveDownloadedData(downloadOnly, testing, stockDictPrimary, configManager, l
|
|
3840
3863
|
os.system(f"{launcher} -a Y -e -l -d {'-i 1m' if configManager.isIntradayConfig() else ''}")
|
3841
3864
|
else:
|
3842
3865
|
del os.environ['PKDevTools_Default_Log_Level']
|
3866
|
+
PKAnalyticsService().send_event("app_exit")
|
3843
3867
|
sys.exit(0)
|
3844
3868
|
else:
|
3845
3869
|
OutputControls().printOutput(colorText.GREEN + " [+] Skipped Saving!" + colorText.END)
|
@@ -3915,6 +3939,7 @@ def sendGlobalMarketBarometer(userArgs=None):
|
|
3915
3939
|
)
|
3916
3940
|
os.remove(gmbPath)
|
3917
3941
|
else:
|
3942
|
+
PKAnalyticsService().send_event("app_exit")
|
3918
3943
|
sys.exit(0)
|
3919
3944
|
except Exception as e: # pragma: no cover
|
3920
3945
|
default_logger().debug(e,exc_info=True)
|
@@ -70,6 +70,7 @@ from PKDevTools.classes.GmailReader import PKGmailReader
|
|
70
70
|
from pkscreener.classes.MenuOptions import MenuRenderStyle, menu, menus,MAX_MENU_OPTION
|
71
71
|
from pkscreener.classes.WorkflowManager import run_workflow
|
72
72
|
import pkscreener.classes.ConfigManager as ConfigManager
|
73
|
+
from pkscreener.classes.PKAnalytics import PKAnalyticsService
|
73
74
|
try:
|
74
75
|
from PKDevTools.classes.DBManager import DBManager
|
75
76
|
from PKDevTools.classes.UserSubscriptions import PKUserSusbscriptions
|
@@ -1422,9 +1423,14 @@ def isUserSubscribed(user):
|
|
1422
1423
|
|
1423
1424
|
def launchScreener(options, user, context, optionChoices, update):
|
1424
1425
|
try:
|
1425
|
-
|
1426
|
+
scanRequest = optionChoices.replace(" ", "").replace(">", "_").replace(":","_").replace("_D","").upper()
|
1427
|
+
userSubs = isUserSubscribed(user)
|
1428
|
+
try:
|
1429
|
+
PKAnalyticsService().send_event("bot_scan",{"bot_userid":str(user.id), "bot_username":str(user.username),"scan_id":str(scanRequest),"user_subscribed":userSubs})
|
1430
|
+
except:
|
1431
|
+
pass
|
1432
|
+
if not userSubs:
|
1426
1433
|
basicSubscriptions = ["X_0","X_N","X_1_"]
|
1427
|
-
scanRequest = optionChoices.replace(" ", "").replace(">", "_").replace(":","_").replace("_D","").upper()
|
1428
1434
|
isBasicScanRequest = False
|
1429
1435
|
for basicSub in basicSubscriptions:
|
1430
1436
|
if basicSub in scanRequest:
|
@@ -19,7 +19,7 @@ pandas_ta #>=0.3.14b0
|
|
19
19
|
# and https://github.com/pyinstaller/pyinstaller/issues/7269
|
20
20
|
pefile>=2023.2.7,<2024.8.26
|
21
21
|
Pillow #>=9.5.0,<=9.5.0 # Keep at this version because getsize_multiline is deprecated/removed in higher versions
|
22
|
-
PKDevTools>=0.13.20250126.
|
22
|
+
PKDevTools>=0.13.20250126.238
|
23
23
|
PKNSETools>=0.1.20250122.138
|
24
24
|
Pyarrow #>=17.0.0
|
25
25
|
pyppeteer #>=2.0.0
|
@@ -1,9 +1,9 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: pkscreener
|
3
|
-
Version: 0.46.20250221.
|
3
|
+
Version: 0.46.20250221.731
|
4
4
|
Summary: A Python-based stock screener for NSE, India with alerts to Telegram Channel (pkscreener)
|
5
5
|
Home-page: https://github.com/pkjmesra/pkscreener
|
6
|
-
Download-URL: https://github.com/pkjmesra/pkscreener/archive/v0.46.20250221.
|
6
|
+
Download-URL: https://github.com/pkjmesra/pkscreener/archive/v0.46.20250221.731.zip
|
7
7
|
Author: pkjmesra
|
8
8
|
Author-email: pkjmesra@gmail.com
|
9
9
|
License: OSI Approved (MIT)
|
@@ -33,7 +33,7 @@ Requires-Dist: pandas
|
|
33
33
|
Requires-Dist: pandas_ta
|
34
34
|
Requires-Dist: pefile<2024.8.26,>=2023.2.7
|
35
35
|
Requires-Dist: Pillow
|
36
|
-
Requires-Dist: PKDevTools>=0.13.20250126.
|
36
|
+
Requires-Dist: PKDevTools>=0.13.20250126.238
|
37
37
|
Requires-Dist: PKNSETools>=0.1.20250122.138
|
38
38
|
Requires-Dist: Pyarrow
|
39
39
|
Requires-Dist: pyppeteer
|
@@ -392,15 +392,15 @@ After you have finished the run, go to that copied path, zip the contents of the
|
|
392
392
|
[MADE-IN-INDIA-badge]: https://img.shields.io/badge/MADE%20WITH%20%E2%9D%A4%20IN-INDIA-orange
|
393
393
|
[MADE-IN-INDIA]: https://en.wikipedia.org/wiki/India
|
394
394
|
[Windows-badge]: https://img.shields.io/badge/Windows-0078D6?logo=windows&logoColor=white
|
395
|
-
[Windows]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
395
|
+
[Windows]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli.exe
|
396
396
|
[Linux-badge_x64]: https://img.shields.io/badge/Linux(x64)-FCC624?logo=linux&logoColor=black
|
397
|
-
[Linux_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
397
|
+
[Linux_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli_x64.bin
|
398
398
|
[Linux-badge_arm64]: https://img.shields.io/badge/Linux(arm64)-FCC624?logo=linux&logoColor=black
|
399
|
-
[Linux_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
399
|
+
[Linux_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli_arm64.bin
|
400
400
|
[Mac OS-badge_x64]: https://img.shields.io/badge/mac%20os(x64)-D3D3D3?logo=apple&logoColor=000000
|
401
|
-
[Mac OS_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
401
|
+
[Mac OS_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli_x64.run
|
402
402
|
[Mac OS-badge_arm64]: https://img.shields.io/badge/mac%20os(arm64)-D3D3D3?logo=apple&logoColor=000000
|
403
|
-
[Mac OS_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.
|
403
|
+
[Mac OS_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250221.729/pkscreenercli_arm64.run
|
404
404
|
[GitHub release (latest by date)-badge]: https://img.shields.io/github/v/release/pkjmesra/PKScreener
|
405
405
|
[GitHub release (latest by date)]: https://github.com/pkjmesra/PKScreener/releases/latest
|
406
406
|
[pypi-badge]: https://img.shields.io/pypi/v/pkscreener.svg?style=flat-square
|
@@ -1,122 +0,0 @@
|
|
1
|
-
#!/usr/bin/python3
|
2
|
-
"""
|
3
|
-
The MIT License (MIT)
|
4
|
-
|
5
|
-
Copyright (c) 2023 pkjmesra
|
6
|
-
|
7
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
-
of this software and associated documentation files (the "Software"), to deal
|
9
|
-
in the Software without restriction, including without limitation the rights
|
10
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
-
copies of the Software, and to permit persons to whom the Software is
|
12
|
-
furnished to do so, subject to the following conditions:
|
13
|
-
|
14
|
-
The above copyright notice and this permission notice shall be included in all
|
15
|
-
copies or substantial portions of the Software.
|
16
|
-
|
17
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
23
|
-
SOFTWARE.
|
24
|
-
|
25
|
-
"""
|
26
|
-
|
27
|
-
import os
|
28
|
-
import base64
|
29
|
-
from sys import platform
|
30
|
-
import platform
|
31
|
-
import getpass
|
32
|
-
import git
|
33
|
-
import json
|
34
|
-
import io
|
35
|
-
|
36
|
-
from PKDevTools.classes.Fetcher import fetcher
|
37
|
-
from PKDevTools.classes.Utils import random_user_agent
|
38
|
-
from PKDevTools.classes.PKDateUtilities import PKDateUtilities
|
39
|
-
from PKDevTools.classes import Archiver
|
40
|
-
import git.repo
|
41
|
-
|
42
|
-
class PKAnalyticsService():
|
43
|
-
def collectMetrics(self,user=None):
|
44
|
-
try:
|
45
|
-
userName = self.getUserName()
|
46
|
-
metrics = self.getApproxLocationInfo()
|
47
|
-
dateTime = str(PKDateUtilities.currentDateTime())
|
48
|
-
metrics["dateTime"] = dateTime
|
49
|
-
metrics["userName"] = userName
|
50
|
-
if "readme" in metrics.keys():
|
51
|
-
del metrics['readme']
|
52
|
-
self.tryCommitAnalytics(userDict=metrics,username=userName)
|
53
|
-
except KeyboardInterrupt: # pragma: no cover
|
54
|
-
raise KeyboardInterrupt
|
55
|
-
except Exception as e: # pragma: no cover
|
56
|
-
pass
|
57
|
-
|
58
|
-
def getUserName(self):
|
59
|
-
try:
|
60
|
-
username = os.getlogin()
|
61
|
-
if username is None or len(username) == 0:
|
62
|
-
username = os.environ.get('username') if platform.startswith("win") else os.environ.get("USER")
|
63
|
-
if username is None or len(username) == 0:
|
64
|
-
username = os.environ.get('USERPROFILE')
|
65
|
-
if username is None or len(username) == 0:
|
66
|
-
username = os.path.expandvars("%userprofile%") if platform.startswith("win") else getpass.getuser()
|
67
|
-
except KeyboardInterrupt: # pragma: no cover
|
68
|
-
raise KeyboardInterrupt
|
69
|
-
except: # pragma: no cover
|
70
|
-
username = f"Unidentified-{platform.system()}"
|
71
|
-
pass
|
72
|
-
return username
|
73
|
-
|
74
|
-
def getApproxLocationInfo(self):
|
75
|
-
try:
|
76
|
-
url = 'http://ipinfo.io/json'
|
77
|
-
f = fetcher()
|
78
|
-
response = f.fetchURL(url=url,timeout=5,headers={'user-agent': f'{random_user_agent()}'})
|
79
|
-
data = json.loads(response.text)
|
80
|
-
except: # pragma: no cover
|
81
|
-
data = {"locationInfo":f"Unidentified-{platform.system()}"}
|
82
|
-
pass
|
83
|
-
return data
|
84
|
-
|
85
|
-
def tryCommitAnalytics(self, userDict={},username="Unidentified"):
|
86
|
-
repo_clone_url = "https://github.com/pkjmesra/PKUserAnalytics.git"
|
87
|
-
local_repo = os.path.join(Archiver.get_user_data_dir(),"PKUserAnalytics")
|
88
|
-
try:
|
89
|
-
test_branch = "main"
|
90
|
-
repo = git.Repo.clone_from(repo_clone_url, local_repo)
|
91
|
-
repo.git.checkout(test_branch)
|
92
|
-
except Exception as e: # pragma: no cover
|
93
|
-
repo = git.Repo(local_repo)
|
94
|
-
repo.git.checkout(test_branch)
|
95
|
-
pass
|
96
|
-
remote = git.remote.Remote(repo=repo,name="origin")
|
97
|
-
repo.git.reset('--hard','origin/main')
|
98
|
-
remote.pull()
|
99
|
-
# write to file in working directory
|
100
|
-
scanResultFilesPath = os.path.join(local_repo, f"users-{PKDateUtilities.currentDateTime().strftime('%Y-%m-%d')}.txt")
|
101
|
-
records = {}
|
102
|
-
existingUserRecords = [userDict]
|
103
|
-
mode = "rb+" if os.path.exists(scanResultFilesPath) else "wb+"
|
104
|
-
with open(scanResultFilesPath, mode) as f:
|
105
|
-
allUsers = f.read()
|
106
|
-
if allUsers is not None and len(allUsers) > 0:
|
107
|
-
allUsers = base64.b64decode(allUsers).decode("utf-8").replace("'","\"")
|
108
|
-
records = json.loads(allUsers)
|
109
|
-
if records is None:
|
110
|
-
records = {}
|
111
|
-
existingUserRecords = records.get(username)
|
112
|
-
if existingUserRecords is not None:
|
113
|
-
existingUserRecords.append(userDict)
|
114
|
-
else:
|
115
|
-
existingUserRecords = [userDict]
|
116
|
-
records[username] = existingUserRecords
|
117
|
-
encoded = base64.b64encode(bytes(str(records).replace("'","\""), "utf-8"))
|
118
|
-
f.writelines(io.BytesIO(encoded))
|
119
|
-
repo.index.add([scanResultFilesPath])
|
120
|
-
repo.index.commit("[User-Analytics]")
|
121
|
-
remote = git.remote.Remote(repo=repo,name="origin")
|
122
|
-
remote.push()
|
@@ -1 +0,0 @@
|
|
1
|
-
VERSION='0.46.20250221.728'
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/ArtTexts.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/AssetsManager.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/Backtest.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/Barometer.py
RENAMED
File without changes
|
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/CandlePatterns.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/Changelog.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/ConfigManager.py
RENAMED
File without changes
|
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/ConsoleUtility.py
RENAMED
File without changes
|
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/GlobalStore.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/ImageUtility.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/MarketMonitor.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/MarketStatus.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/MenuOptions.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/Messenger.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/OtaUpdater.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PKDataService.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PKDemoHandler.py
RENAMED
File without changes
|
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PKPremiumHandler.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PKScanRunner.py
RENAMED
File without changes
|
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PKScheduler.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PKSpreadsheets.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/Portfolio.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/PortfolioXRay.py
RENAMED
File without changes
|
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/StockScreener.py
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/StockSentiment.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener/classes/WorkflowManager.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener.egg-info/SOURCES.txt
RENAMED
File without changes
|
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener.egg-info/entry_points.txt
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener.egg-info/not-zip-safe
RENAMED
File without changes
|
{pkscreener-0.46.20250221.728 → pkscreener-0.46.20250221.731}/pkscreener.egg-info/top_level.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|