From 3296fec639afba7b20616fe7ab4a652b34b8c3e5 Mon Sep 17 00:00:00 2001 From: khushal Date: Mon, 23 Dec 2024 16:27:04 +0530 Subject: [PATCH] (20241223) Exception handling in Quart API endpoints can now return stuff. --- socketio/main.py | 202 +++++++++++++++++ utils_v2/api/async_quart.py | 4 +- views_v2/__init__.py | 0 views_v2/finstitutions/__init__.py | 0 views_v2/finstitutions/trading/__init__.py | 0 .../finstitutions/trading/oauth/__init__.py | 0 .../trading/oauth/oauth_cancelled_v2.html | 114 ++++++++++ .../trading/oauth/oauth_failure_v2.html | 115 ++++++++++ .../trading/oauth/oauth_success_v2.html | 113 ++++++++++ views_v2/message/__init__.py | 0 views_v2/message/mail/__init__.py | 0 views_v2/message/mail/oauth/__init__.py | 0 .../mail/oauth/oauth_cancelled_v2.html | 114 ++++++++++ .../message/mail/oauth/oauth_failure_v2.html | 115 ++++++++++ .../message/mail/oauth/oauth_success_v2.html | 113 ++++++++++ wsocket/finstitutions/trading/main.py | 140 ++++++++++++ wsocket/main.py | 140 ++++++++++++ wsocket/main_bkp.py | 205 ++++++++++++++++++ 18 files changed, 1373 insertions(+), 2 deletions(-) create mode 100644 socketio/main.py create mode 100644 views_v2/__init__.py create mode 100644 views_v2/finstitutions/__init__.py create mode 100644 views_v2/finstitutions/trading/__init__.py create mode 100644 views_v2/finstitutions/trading/oauth/__init__.py create mode 100644 views_v2/finstitutions/trading/oauth/oauth_cancelled_v2.html create mode 100644 views_v2/finstitutions/trading/oauth/oauth_failure_v2.html create mode 100644 views_v2/finstitutions/trading/oauth/oauth_success_v2.html create mode 100644 views_v2/message/__init__.py create mode 100644 views_v2/message/mail/__init__.py create mode 100644 views_v2/message/mail/oauth/__init__.py create mode 100644 views_v2/message/mail/oauth/oauth_cancelled_v2.html create mode 100644 views_v2/message/mail/oauth/oauth_failure_v2.html create mode 100644 views_v2/message/mail/oauth/oauth_success_v2.html create mode 100644 wsocket/finstitutions/trading/main.py create mode 100644 wsocket/main.py create mode 100644 wsocket/main_bkp.py diff --git a/socketio/main.py b/socketio/main.py new file mode 100644 index 0000000..67d39f7 --- /dev/null +++ b/socketio/main.py @@ -0,0 +1,202 @@ +""" + + AUTHOR: + + Khushal P Soonderji + + DATE: + + Create: Saturday, 18th May, 2022 + Update: Thursday, 22nd Aug. 2024 + + OBJECTIVE: + + To provide an easy way to work with '.json' data and files. + + REFERENCES: + + 1) https://www.w3schools.com/python/python_json.asp + + DOWNLOADS: + + N/A + +""" + + +# ***************************************************************************************************************** +# ***** **** +# *** IMPORT *** +# ***** **** +# ***************************************************************************************************************** + + +# To make sibling directories accessible for imports: +import sys +sys.path.append(".") +sys.path.append("..") + +# System-level activities: +import io + +# To work with the JSON standard: +import json + +# To work with files: +from utils_v2.system import files + + +# ***************************************************************************************************************** +# ***** **** +# *** MACROS / ONE-TIME INIT *** +# ***** **** +# ***************************************************************************************************************** + + +# --- Nothing Yet + + +# ***************************************************************************************************************** +# ***** **** +# *** VARIABLES *** +# ***** **** +# ***************************************************************************************************************** + + +# --- Nothing Yet + + +# ***************************************************************************************************************** +# ***** **** +# *** FUNCTIONS *** +# ***** **** +# ***************************************************************************************************************** + + +def from_string(json_data): + + """ + Decodes a JSON string to a pythonic variable like a dict. + :param json_data: The JSON string to decode. + :return: The decoded pythonic variable. + """ + + python_data = json.loads(json_data) + return python_data + + +# --------------------------------------------------------------------------------------------------------------------- + + +def to_string( + python_data, + indent = 4, + default = None, + separators = None, + no_space = False +): + + """ + Converts the given pythonic data to a JSON string. + :param python_data: The input data like a dict. + :param indent: The tab-width for pretty presentation. + :param default: The function to use on something that cannot be directly parsed into a JSON string. + :param separators: Custom separators to use. + :param no_space: If you want a dense JSON string that saves memory by not using spaces or tabs or line-breaks. Not + good for human readability, very good for saving memory. WARNING: THIS OVERRIDES EVERY OTHER PARAMETER EXCEPT + 'default'. + :return: The JSON string representation of the input pythonic data. + """ + + if no_space: + json_data = json.dumps( + python_data, + default = default, + separators = (',', ':') + ) + + else: + json_data = json.dumps( + python_data, + indent = indent, + default = default, + separators = separators + ) + + return json_data + + +# --------------------------------------------------------------------------------------------------------------------- + + +def from_file(file): + + """ + Reads a JSON file and returns it as a pythonic variable like a dict. + :param file: The path to the file on the disk or a file held in RAM as a BytesIO object. + :return: The decoded pythonic variable. + """ + + if isinstance(file, io.BytesIO): + file.seek(0) + json_data = file.getvalue() + else: json_data = files.read_file(file) + python_data = from_string(json_data) + return python_data + + +# --------------------------------------------------------------------------------------------------------------------- + + +def to_file( + file, + python_data, + indent = 4, + default = None, + separators = None, + no_space = False +): + + """ + + :param file: Either a path to a file on disk, or a buffer in RAM in the form of a BytesIO object. + :param python_data: The pythonic data to be converted to the JSON string. + :param indent: The tab-width for pretty presentation. + :param default: The function to use on something that cannot be directly parsed into a JSON string. + :param separators: Custom separators to use. + :param no_space: If you want a dense JSON string that saves memory by not using spaces or tabs or line-breaks. Not + good for human readability, very good for saving memory. WARNING: THIS OVERRIDES EVERY OTHER PARAMETER EXCEPT + 'default'. + :return: True/False if a path was given, else the same BytesIO object with the written JSON data. + """ + + json_data = to_string( + python_data, + indent = indent, + default = default, + separators = separators, + no_space = no_space + ) + + if isinstance(file, io.BytesIO): + file.write(json_data.encode("utf-8")) + file.seek(0) + return file + + else: + try: + files.write_file(file, json_data, mode = "w") + return True + except: return False + + +# ***************************************************************************************************************** +# ***** **** +# *** MAIN PROGRAM *** +# ***** **** +# ***************************************************************************************************************** + + +if __name__ == "__main__": + + pass diff --git a/utils_v2/api/async_quart.py b/utils_v2/api/async_quart.py index a0113ef..d7abe9f 100644 --- a/utils_v2/api/async_quart.py +++ b/utils_v2/api/async_quart.py @@ -1255,8 +1255,8 @@ def handle_failed_request(cleanup_func = None, cleanup_coro = None): except Exception as exception: kwargs["decorator_count"] -= 1 if hasattr(current_app, "printer"): getattr(current_app, "printer")(exception) - if cleanup_func is not None: cleanup_func() - if cleanup_coro is not None: await cleanup_coro() + if cleanup_func is not None: return cleanup_func() + if cleanup_coro is not None: return await cleanup_coro() raise exception return wrapper diff --git a/views_v2/__init__.py b/views_v2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/views_v2/finstitutions/__init__.py b/views_v2/finstitutions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/views_v2/finstitutions/trading/__init__.py b/views_v2/finstitutions/trading/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/views_v2/finstitutions/trading/oauth/__init__.py b/views_v2/finstitutions/trading/oauth/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/views_v2/finstitutions/trading/oauth/oauth_cancelled_v2.html b/views_v2/finstitutions/trading/oauth/oauth_cancelled_v2.html new file mode 100644 index 0000000..368e9a2 --- /dev/null +++ b/views_v2/finstitutions/trading/oauth/oauth_cancelled_v2.html @@ -0,0 +1,114 @@ + + + + + + Authorization Cancelled + + + + + + +
+
!
+

Authorization Cancelled

+

It seems that the authorization for your {{ mail_client }} account was cancelled unexpectedly. + Please feel free to try again whenever you feel like it. You can close this tab at any time.

+ + + +
+ + + + + diff --git a/views_v2/finstitutions/trading/oauth/oauth_failure_v2.html b/views_v2/finstitutions/trading/oauth/oauth_failure_v2.html new file mode 100644 index 0000000..03d0b65 --- /dev/null +++ b/views_v2/finstitutions/trading/oauth/oauth_failure_v2.html @@ -0,0 +1,115 @@ + + + + + + Authorization Failed + + + + + + +
+
+

Authorization Failed

+

Something went wrong in getting authorization from your {{ mail_client }} account. +

Hint: {{ failure_hint }}

+ Please feel free to try the same steps again. You can close this tab at any time.

+ + + +
+ + + + + diff --git a/views_v2/finstitutions/trading/oauth/oauth_success_v2.html b/views_v2/finstitutions/trading/oauth/oauth_success_v2.html new file mode 100644 index 0000000..929f50c --- /dev/null +++ b/views_v2/finstitutions/trading/oauth/oauth_success_v2.html @@ -0,0 +1,113 @@ + + + + + + Authorization Successful + + + + + + +
+
+

Authorization Successful

+

We have received authorization from your {{ mail_client }} account. You can close this tab at any time.

+ + + +
+ + + + + diff --git a/views_v2/message/__init__.py b/views_v2/message/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/views_v2/message/mail/__init__.py b/views_v2/message/mail/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/views_v2/message/mail/oauth/__init__.py b/views_v2/message/mail/oauth/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/views_v2/message/mail/oauth/oauth_cancelled_v2.html b/views_v2/message/mail/oauth/oauth_cancelled_v2.html new file mode 100644 index 0000000..0bff500 --- /dev/null +++ b/views_v2/message/mail/oauth/oauth_cancelled_v2.html @@ -0,0 +1,114 @@ + + + + + + Authorization Cancelled + + + + + + +
+
!
+

Authorization Cancelled

+

It seems that the authorization for your {{ client }} account was cancelled unexpectedly. + Please feel free to try again whenever you feel like it. You can close this tab at any time.

+ + + +
+ + + + + diff --git a/views_v2/message/mail/oauth/oauth_failure_v2.html b/views_v2/message/mail/oauth/oauth_failure_v2.html new file mode 100644 index 0000000..e396f42 --- /dev/null +++ b/views_v2/message/mail/oauth/oauth_failure_v2.html @@ -0,0 +1,115 @@ + + + + + + Authorization Failed + + + + + + +
+
+

Authorization Failed

+

Something went wrong in getting authorization from your {{ client }} account. +

Hint: {{ failure_hint|safe }}

+ Please feel free to try the same steps again. You can close this tab at any time.

+ + + +
+ + + + + diff --git a/views_v2/message/mail/oauth/oauth_success_v2.html b/views_v2/message/mail/oauth/oauth_success_v2.html new file mode 100644 index 0000000..33ee0b1 --- /dev/null +++ b/views_v2/message/mail/oauth/oauth_success_v2.html @@ -0,0 +1,113 @@ + + + + + + Authorization Successful + + + + + + +
+
+

Authorization Successful

+

We have received authorization from your {{ client }} account. You can close this tab at any time.

+ + + +
+ + + + + diff --git a/wsocket/finstitutions/trading/main.py b/wsocket/finstitutions/trading/main.py new file mode 100644 index 0000000..42dedba --- /dev/null +++ b/wsocket/finstitutions/trading/main.py @@ -0,0 +1,140 @@ +""" + + AUTHOR: + + Khushal P Soonderji + + DATE: + + monday, 23rd Dec., 2024 + + OBJECTIVE: + + To provide a SocketIO app for socket-base communication with the front-end. + + REFERENCES: + + 01. YouTube: https://www.youtube.com/watch?v=H1eLJMC5oTg&t=3s + + DOWNLOADS: + + N/A + +""" + + +# ***************************************************************************************************************** +# ***** **** +# *** IMPORT *** +# ***** **** +# ***************************************************************************************************************** + + +# To make sibling directories accessible for imports: +import sys +sys.path.append(".") +sys.path.append("..") + +# System-level activities: +import io +import os + +# my utils: +from utils_v2.string import json + +# To work with SocketIO: +import socketio +from aiohttp import web + +# For asynchronous activities: +import asyncio + +# for debugging: +from icecream import IceCreamDebugger + + +# ***************************************************************************************************************** +# ***** **** +# *** MACROS / ONE-TIME INIT *** +# ***** **** +# ***************************************************************************************************************** + + +# --- Nothing Yet + + +# ***************************************************************************************************************** +# ***** **** +# *** VARIABLES *** +# ***** **** +# ***************************************************************************************************************** + + +# The SocketIo server: +sio = socketio.AsyncServer(cors_allowed_origins = "*") +app = web.Application() +sio.attach(app) + +# Debugging: +printer = IceCreamDebugger(prefix = "SocketIO | ", includeContext = True) + + +# ***************************************************************************************************************** +# ***** **** +# *** FUNCTIONS *** +# ***** **** +# ***************************************************************************************************************** + + +@sio.event +async def connect(sid, environ): + printer(sid) + + +# --------------------------------------------------------------------------------------------------------------------- + + +@sio.event +async def disconnect(sid): + printer(sid) + + +# --------------------------------------------------------------------------------------------------------------------- + + +async def init(): + + """ + Initialize stuff here. + :return: ? + """ + + pass + + +# ***************************************************************************************************************** +# ***** **** +# *** MAIN PROGRAM *** +# ***** **** +# ***************************************************************************************************************** + + +if __name__ == "__main__": + + async def main(): + + # Start receiving live market data in the background: + # asyncio.create_task(start_live_feed()) + + # Run the web server: + runner = web.AppRunner(app) + await runner.setup() + site = web.TCPSite(runner, "0.0.0.0", 5214) + printer("Server running.") + await site.start() + + # Keep the server running: + while True: await asyncio.sleep(3_600) + + # Let's go: + asyncio.run(main()) diff --git a/wsocket/main.py b/wsocket/main.py new file mode 100644 index 0000000..42dedba --- /dev/null +++ b/wsocket/main.py @@ -0,0 +1,140 @@ +""" + + AUTHOR: + + Khushal P Soonderji + + DATE: + + monday, 23rd Dec., 2024 + + OBJECTIVE: + + To provide a SocketIO app for socket-base communication with the front-end. + + REFERENCES: + + 01. YouTube: https://www.youtube.com/watch?v=H1eLJMC5oTg&t=3s + + DOWNLOADS: + + N/A + +""" + + +# ***************************************************************************************************************** +# ***** **** +# *** IMPORT *** +# ***** **** +# ***************************************************************************************************************** + + +# To make sibling directories accessible for imports: +import sys +sys.path.append(".") +sys.path.append("..") + +# System-level activities: +import io +import os + +# my utils: +from utils_v2.string import json + +# To work with SocketIO: +import socketio +from aiohttp import web + +# For asynchronous activities: +import asyncio + +# for debugging: +from icecream import IceCreamDebugger + + +# ***************************************************************************************************************** +# ***** **** +# *** MACROS / ONE-TIME INIT *** +# ***** **** +# ***************************************************************************************************************** + + +# --- Nothing Yet + + +# ***************************************************************************************************************** +# ***** **** +# *** VARIABLES *** +# ***** **** +# ***************************************************************************************************************** + + +# The SocketIo server: +sio = socketio.AsyncServer(cors_allowed_origins = "*") +app = web.Application() +sio.attach(app) + +# Debugging: +printer = IceCreamDebugger(prefix = "SocketIO | ", includeContext = True) + + +# ***************************************************************************************************************** +# ***** **** +# *** FUNCTIONS *** +# ***** **** +# ***************************************************************************************************************** + + +@sio.event +async def connect(sid, environ): + printer(sid) + + +# --------------------------------------------------------------------------------------------------------------------- + + +@sio.event +async def disconnect(sid): + printer(sid) + + +# --------------------------------------------------------------------------------------------------------------------- + + +async def init(): + + """ + Initialize stuff here. + :return: ? + """ + + pass + + +# ***************************************************************************************************************** +# ***** **** +# *** MAIN PROGRAM *** +# ***** **** +# ***************************************************************************************************************** + + +if __name__ == "__main__": + + async def main(): + + # Start receiving live market data in the background: + # asyncio.create_task(start_live_feed()) + + # Run the web server: + runner = web.AppRunner(app) + await runner.setup() + site = web.TCPSite(runner, "0.0.0.0", 5214) + printer("Server running.") + await site.start() + + # Keep the server running: + while True: await asyncio.sleep(3_600) + + # Let's go: + asyncio.run(main()) diff --git a/wsocket/main_bkp.py b/wsocket/main_bkp.py new file mode 100644 index 0000000..428162a --- /dev/null +++ b/wsocket/main_bkp.py @@ -0,0 +1,205 @@ +""" + + AUTHOR: + + Khushal P Soonderji + + DATE: + + monday, 23rd Dec., 2024 + + OBJECTIVE: + + To provide a SocketIO app for socket-base communication with the front-end. + + REFERENCES: + + 01. YouTube: https://www.youtube.com/watch?v=H1eLJMC5oTg&t=3s + + DOWNLOADS: + + N/A + +""" +import datetime +# ***************************************************************************************************************** +# ***** **** +# *** IMPORT *** +# ***** **** +# ***************************************************************************************************************** + + +# To make sibling directories accessible for imports: +import sys +sys.path.append(".") +sys.path.append("..") + +# System-level activities: +import io +import os + +# my utils: +from utils_v2.string import json + +# To work with SocketIO: +import socketio +import eventlet + +# For asynchronous activities: +import asyncio + +# for debugging: +from icecream import IceCreamDebugger + +# To work with date and time: +import time + +# To work with Zerodha's Kite platform: +from kiteconnect import KiteConnect, KiteTicker + + +# ***************************************************************************************************************** +# ***** **** +# *** MACROS / ONE-TIME INIT *** +# ***** **** +# ***************************************************************************************************************** + + +INSTRUMENT_MAP = { + 256265: "NIFTY 50", + 260617: "NIFTY 100", + 259849: "NIFTY IT", + 341249: "HDFCBANK", + 738561: "RELIANCE", + 408065: "INFY", + 2953217: "TCS", + 356865: "HINDUNILVR", + 1270529: "ICICIBANK", + 492033: "KOTAKBANK", + 110630919: "GOLD25JAN75800CE", + 110050823: "SILVER25FEB76000CE", + 10670594: "NIFTY24DEC23650PE", + 17167874: "BANKNIFTY24DEC45000PE", +} +INSTRUMENT_TOKENS = list(INSTRUMENT_MAP.keys()) + + +# ***************************************************************************************************************** +# ***** **** +# *** VARIABLES *** +# ***** **** +# ***************************************************************************************************************** + + +# The SocketIo server: +sio = socketio.Server(cors_allowed_origins = "*") +app = socketio.WSGIApp(sio) + +# Debugging: +printer = IceCreamDebugger(prefix = "SocketIO | ", includeContext = True) + + +# ***************************************************************************************************************** +# ***** **** +# *** FUNCTIONS *** +# ***** **** +# ***************************************************************************************************************** + + +@sio.event +def connect(sid, environ): + printer(sid) + + +# --------------------------------------------------------------------------------------------------------------------- + + +@sio.event +def disconnect(sid): + printer(sid) + + +# --------------------------------------------------------------------------------------------------------------------- + + +def on_ticks(ws, ticks): + + try: + + # print(json.to_string(ticks[0], default=str)) + printer(len(ticks)) + now = datetime.datetime.now() + for t in ticks: + t["last_trade_time"] = t.get("last_trade_time", now).strftime("%Y-%m-%d %H:%M:%S") + t["exchange_timestamp"] = t.get("exchange_timestamp", now).strftime("%Y-%m-%d %H:%M:%S") + sio.emit("ticks", ticks) + sio.emit("ticks", {"name": "Bhopli"}) + sio.emit("debug", {"name": "Debugger Bhopli"}) + + except Exception as exception: + printer(exception) + + +# --------------------------------------------------------------------------------------------------------------------- + + +def on_connect(ws, response): + + ws.subscribe(INSTRUMENT_TOKENS) + ws.set_mode(ws.MODE_FULL, INSTRUMENT_TOKENS) + printer("Subscribed to token(s) in 'Full' mode", len(INSTRUMENT_TOKENS)) + + +# --------------------------------------------------------------------------------------------------------------------- + + +def start_live_feed_input( + api_key: str, + access_token: str, +): + + kite_ws = KiteTicker( + api_key = api_key, + access_token = access_token + ) + + # Assign the callbacks: + kite_ws.on_ticks = on_ticks + # kite_ws.on_close = on_close + # kite_ws.on_error = on_error + kite_ws.on_connect = on_connect + # kite_ws.on_reconnect = on_reconnect + # kite_ws.on_noreconnect = on_noreconnect + + # If you choose to go threaded, you will need to work purely with callbacks. + # You will need to have an infinite loop in the main thread. + kite_ws.connect(threaded = True) + + +# --------------------------------------------------------------------------------------------------------------------- + + +@sio.event +def subscribe(sid, data): + printer(data) + sio.emit("echo", data) + + +# ***************************************************************************************************************** +# ***** **** +# *** MAIN PROGRAM *** +# ***** **** +# ***************************************************************************************************************** + + +if __name__ == "__main__": + + # Connect to Zerodha: + creds = json.from_file(r"../creds/zerodha/api.json") + start_live_feed_input( + api_key = creds["apiKey"], + access_token = creds["accessToken"] + ) + + eventlet.wsgi.server(eventlet.listen(("0.0.0.0", 5214)), app) +