diff --git a/api/blueprints/ai/llm/invoke.py b/api/blueprints/ai/llm/invoke.py index b2c0e5b..1e8f7e3 100644 --- a/api/blueprints/ai/llm/invoke.py +++ b/api/blueprints/ai/llm/invoke.py @@ -63,7 +63,7 @@ from utils_v2.api.async_quart import ( from shared import constants # Data Models: -from models.data.ai.llm import LLMRequestHeaders, LLMInput +from models.data.api.ai.llm import LLMRequestHeaders, LLMInput # For asynchronous activities: import asyncio diff --git a/api/blueprints/chat/auth.py b/api/blueprints/chat/auth.py index 2d03c86..6c7820d 100644 --- a/api/blueprints/chat/auth.py +++ b/api/blueprints/chat/auth.py @@ -60,7 +60,7 @@ from utils_v2.api.async_quart import ( ) # Data Models: -from models.data.chat.auth import ChatAuthRequestHeaders, ChatAuthRequestData +from models.data.api.chat.auth import ChatAuthRequestHeaders, ChatAuthRequestData # Common: from shared import constants diff --git a/api/blueprints/chat/webhook.py b/api/blueprints/chat/webhook.py index 163a352..c2f99f9 100644 --- a/api/blueprints/chat/webhook.py +++ b/api/blueprints/chat/webhook.py @@ -60,7 +60,7 @@ from utils_v2.api.async_quart import ( ) # Data Models: -from models.data.chat.auth import ChatAuthRequestHeaders, ChatAuthRequestData +from models.data.api.chat.auth import ChatAuthRequestHeaders, ChatAuthRequestData # Common: from shared import constants diff --git a/api/blueprints/mail/list.py b/api/blueprints/mail/list.py index 0677a56..0000fb7 100644 --- a/api/blueprints/mail/list.py +++ b/api/blueprints/mail/list.py @@ -70,7 +70,7 @@ from utils_v2.goog.models.data.auth_tokens import GoogleAuthTokens from shared import constants # Data Models: -from models.data.mail.list import MailListRequestHeaders, MailListByAccountIdRequestData +from models.data.api.mail.list import MailListRequestHeaders, MailListByAccountIdRequestData # To work with datatypes: from typing import Literal diff --git a/api/blueprints/mail/oauth_request.py b/api/blueprints/mail/oauth_request.py index 861bce7..2ad9cd3 100644 --- a/api/blueprints/mail/oauth_request.py +++ b/api/blueprints/mail/oauth_request.py @@ -67,7 +67,7 @@ from utils_v2.goog.gmail.gmail_client import SCOPES_GMAIL_MAIL_MANAGEMENT from shared import constants # Data Models: -from models.data.mail.oauth import ( +from models.data.api.mail.oauth import ( OAuthMailAuthorizationRequestHeaders, OAuthMailAuthorizationRequestData ) diff --git a/api/blueprints/mail/retrieve.py b/api/blueprints/mail/retrieve.py index f210225..6a91dd2 100644 --- a/api/blueprints/mail/retrieve.py +++ b/api/blueprints/mail/retrieve.py @@ -69,7 +69,7 @@ from utils_v2.goog.models.data.auth_tokens import GoogleAuthTokens from shared import constants # Data Models: -from models.data.mail.retrieve import MailGetRequestHeaders, MailGetRequestData +from models.data.api.mail.retrieve import MailGetRequestHeaders, MailGetRequestData # To work with datatypes: from typing import Literal diff --git a/api/blueprints/mail/sync.py b/api/blueprints/mail/sync.py index 9bb83d4..10d8d28 100644 --- a/api/blueprints/mail/sync.py +++ b/api/blueprints/mail/sync.py @@ -70,8 +70,8 @@ from utils_v2.goog.models.data.auth_tokens import GoogleAuthTokens from shared import constants # Data Models: -from models.data.mail.sync import MailSyncRequestHeaders, MailSyncRequestData -from models.data.mail.sync import MailSyncOneResult, MailSyncManyResults +from models.data.api.mail.sync import MailSyncRequestHeaders, MailSyncRequestData +from models.data.api.mail.sync import MailSyncOneResult, MailSyncManyResults # To work with datatypes: from typing import Literal diff --git a/api/blueprints/sms/auth.py b/api/blueprints/sms/auth.py index b528c3e..9ee2a45 100644 --- a/api/blueprints/sms/auth.py +++ b/api/blueprints/sms/auth.py @@ -67,7 +67,7 @@ from utils_v2.sms.savvy_bulk_sms.async_savvy_bulk_sms import AsyncSavvyBulkSMS from shared import constants # Data Models: -from models.data.sms.auth import SMSAuthRequestHeaders, SMSAuthRequestData +from models.data.api.sms.auth import SMSAuthRequestHeaders, SMSAuthRequestData # For asynchronous activities: import asyncio diff --git a/api/blueprints/tech/chat_alerts.py b/api/blueprints/tech/chat_alerts.py index 4200a27..649cde3 100644 --- a/api/blueprints/tech/chat_alerts.py +++ b/api/blueprints/tech/chat_alerts.py @@ -63,7 +63,7 @@ from utils_v2.api.async_quart import ( ) # Data models: -from models.data.tech.alerts import ( +from models.data.api.tech.alerts import ( ChatAlertRequestHeaders, ChatAlertRequestData ) diff --git a/models/behaviour/ai/llm/open_ai.py b/models/behaviour/ai/llm/open_ai.py index b8ad019..bb0bb99 100644 --- a/models/behaviour/ai/llm/open_ai.py +++ b/models/behaviour/ai/llm/open_ai.py @@ -44,7 +44,7 @@ from utils_v2.database.async_mongo_v2 import AsyncMongo from models.behaviour.base import BaseModel # Data Models: -from models.data.ai.llm import LLMInput, LLMOutput, LLMUsageTokens +from models.data.api.ai.llm import LLMInput, LLMOutput, LLMUsageTokens # To work with LLMs: from langchain_openai import ChatOpenAI diff --git a/models/behaviour/mail/sync_v2.py b/models/behaviour/mail/sync_v2.py index de4fd81..a6da37c 100644 --- a/models/behaviour/mail/sync_v2.py +++ b/models/behaviour/mail/sync_v2.py @@ -51,7 +51,7 @@ from utils_v2.goog.models.data.auth_tokens import GoogleAuthTokens from models.behaviour.base import BaseModel # Data models: -from models.data.mail.sync import MailSyncOneResult, MailSyncManyResults +from models.data.api.mail.sync import MailSyncOneResult, MailSyncManyResults # To work with MongoDB: from bson import ObjectId @@ -59,7 +59,7 @@ from pymongo import InsertOne, UpdateOne, ReplaceOne # To work with LLMs: from models.behaviour.ai.llm.open_ai import LLMOpenAI -from models.data.ai.llm import LLMInput +from models.data.api.ai.llm import LLMInput # To work with datatypes: from typing import Literal, List, Dict, Any diff --git a/models/data/ai/__init__.py b/models/data/api/ai/__init__.py similarity index 100% rename from models/data/ai/__init__.py rename to models/data/api/ai/__init__.py diff --git a/models/data/ai/llm.py b/models/data/api/ai/llm.py similarity index 100% rename from models/data/ai/llm.py rename to models/data/api/ai/llm.py diff --git a/models/data/chat/__init__.py b/models/data/api/chat/__init__.py similarity index 100% rename from models/data/chat/__init__.py rename to models/data/api/chat/__init__.py diff --git a/models/data/chat/auth.py b/models/data/api/chat/auth.py similarity index 100% rename from models/data/chat/auth.py rename to models/data/api/chat/auth.py diff --git a/models/data/mail/__init__.py b/models/data/api/mail/__init__.py similarity index 100% rename from models/data/mail/__init__.py rename to models/data/api/mail/__init__.py diff --git a/models/data/mail/list.py b/models/data/api/mail/list.py similarity index 100% rename from models/data/mail/list.py rename to models/data/api/mail/list.py diff --git a/models/data/mail/oauth.py b/models/data/api/mail/oauth.py similarity index 100% rename from models/data/mail/oauth.py rename to models/data/api/mail/oauth.py diff --git a/models/data/mail/retrieve.py b/models/data/api/mail/retrieve.py similarity index 100% rename from models/data/mail/retrieve.py rename to models/data/api/mail/retrieve.py diff --git a/models/data/mail/sync.py b/models/data/api/mail/sync.py similarity index 100% rename from models/data/mail/sync.py rename to models/data/api/mail/sync.py diff --git a/models/data/sms/__init__.py b/models/data/api/sms/__init__.py similarity index 100% rename from models/data/sms/__init__.py rename to models/data/api/sms/__init__.py diff --git a/models/data/sms/auth.py b/models/data/api/sms/auth.py similarity index 100% rename from models/data/sms/auth.py rename to models/data/api/sms/auth.py diff --git a/models/data/tech/__init__.py b/models/data/api/tech/__init__.py similarity index 100% rename from models/data/tech/__init__.py rename to models/data/api/tech/__init__.py diff --git a/models/data/tech/alerts.py b/models/data/api/tech/alerts.py similarity index 100% rename from models/data/tech/alerts.py rename to models/data/api/tech/alerts.py diff --git a/models/data/database/auth_token.py b/models/data/database/auth_token.py index 9b9222c..630a1b9 100644 --- a/models/data/database/auth_token.py +++ b/models/data/database/auth_token.py @@ -10,7 +10,7 @@ OBJECTIVE: - To define how messages will be stored in the database. + To define how auth tokens will be stored in the database. REFERENCES: @@ -77,7 +77,7 @@ import datetime # ***************************************************************************************************************** -class CoreMessageModel(BaseModel): +class CoreAuthTokenModel(BaseModel): version: str = Field( description = "a hint about the version no. of this message", @@ -86,52 +86,59 @@ class CoreMessageModel(BaseModel): default = "1.0.0" ) - ts: AwareDatetime = Field( - description = "the time (utc) at which this message was sent by the sender", - frozen = True - ) - - readTs: AwareDatetime = Field( - description = "the time (utc) at which this message was read and stored by your server", - frozen = True, - default_factory = lambda: date_time.get_current_utc_date_time(as_string = False) - ) - - tokenId: ObjectId = Field( - description = "the id of the auth token that is associated with this message", - frozen = True - ) - serviceType: Literal["email", "sms", "chat"] = Field( description = "the kind of service this message was sent/received from", frozen = True ) client: Literal[ - "gmail", - "outlook", - "telegram", - "whatsapp", - "nimbusSmsIndia", - "savvyBulkSmsKenya" + "gmail", "outlook", # ...................... Mail Clients + "telegram", "whatsapp", # .................. Chat Clients + "nimbusSmsIndia", "savvyBulkSmsKenya", # ... SMS Clients + "razorpay", "safaricomMPesaExpress" # ...... Payment Gateways ] = Field( description = "the third-part client that was used", frozen = True ) - clientMessageId: str | int = Field( - description = "how the client identifies this message", + authType: Literal["oauth", "auth"] = Field( + description = "the type of authentication procedure used", frozen = True ) - clientThreadId: str | int | None = Field( - description = "how the client identifies the chat/thread in which this message was sent/received", - frozen = True, - default = None + firstRequestTs: AwareDatetime = Field( + description = "the time (utc) at which authorization was first requested", + frozen = True ) - payload: dict = Field( - description = "the actual contents of the message", + lastRequestTs: AwareDatetime = Field( + description = "the time (utc) at which authorization was last requested", + frozen = False + ) + + firstRefreshTs: AwareDatetime = Field( + description = "the time (utc) at which the tokens were first refreshed", + frozen = False + ) + + lastRefreshTs: AwareDatetime = Field( + description = "the time (utc) at which the tokens were last refreshed", + frozen = False + ) + + token: dict | None = Field( + description = "the actual auth tokens of that client; will differ for each client", + frozen = True, + default_factory = lambda: date_time.get_current_utc_date_time(as_string = False) + ) + + user: dict = Field( + description = "how you identify your user", + frozen = True + ) + + clientUserId: dict = Field( + description = "how third-party client identifies the same user", frozen = True ) @@ -148,16 +155,14 @@ class CoreMessageModel(BaseModel): # ┃┃┏┓┃┓┏┫┏┓╋┓┏┓┏┓ # ┗┛┗┻┗┗┗┻┗┻┗┗┗┛┛┗ - @field_validator("ts", "readTs", mode = "before") + @field_validator( + "firstRequestTs", + "lastRequestTs", "firstRefreshTs", "lastRefreshTs", + mode = "before" + ) def parse_date_time(cls, value): return date_time.parse_date_time(input_value = value, timezone = date_time.TIMEZONE_UTC) - @field_validator("tokenId", mode = "before") - def parse_oid(cls, value): - try: value = ObjectId(value) - except: pass - return value - # ***************************************************************************************************************** # ***** **** @@ -169,18 +174,27 @@ class CoreMessageModel(BaseModel): if __name__ == "__main__": from utils_v2.string import json - message = CoreMessageModel( - ts = date_time.get_current_utc_date_time(as_string = False), - tokenId = "67519cf3a7804fcbc6f12452", + auth_token = CoreAuthTokenModel( serviceType = "email", client = "gmail", - clientMessageId = 123, - clientThreadId = 456, - payload = { - "from": "bhopli@gmil.com", - "to": "hello@thecaoffice.com", - "message": "Hello, World!" + authType = "oauth", + firstRequestTs = date_time.get_current_utc_date_time(as_string = False), + lastRequestTs = date_time.get_current_utc_date_time(as_string = False), + firstRefreshTs = date_time.get_current_utc_date_time(as_string = False), + lastRefreshTs = date_time.get_current_utc_date_time(as_string = False), + token = { + "username": "testing123", + "password": "abcdefgh" + }, + user = { + "userId": 0, + "entityId": 1, + "billingAccountId": 2, + "fullName": "Bhopli" + }, + clientUserId = { + "email": "bhopli@gmail.com" } ) - print("MESSAGE MODEL:", json.to_string(message.model_dump(), default = str)) + print("AUTH-TOKEN MODEL:", json.to_string(auth_token.model_dump(), default = str)) diff --git a/models/data/database/message.py b/models/data/database/message.py index 1edc15c..afe11c5 100644 --- a/models/data/database/message.py +++ b/models/data/database/message.py @@ -6,11 +6,11 @@ DATE: - Friday, 6th Dec., 2024. + Saturday, 7th Dec., 2024. OBJECTIVE: - To provide a structure to receive auth details of various chat apps (like Telegram and WhatsApp). + To define how messages will be stored in the database. REFERENCES: @@ -36,13 +36,16 @@ sys.path.append(".") sys.path.append("..") # For making data behaviour_models: -from pydantic import BaseModel, Field, field_validator, PastDatetime +from pydantic import BaseModel, Field, field_validator, PastDatetime, AwareDatetime from typing import Optional, Literal, Union # My utils: from utils_v2.string import regex from utils_v2.date_time import date_time +# To work with MongoDB: +from bson.objectid import ObjectId + # To work with date and time: import datetime @@ -54,8 +57,7 @@ import datetime # ***************************************************************************************************************** -# RegEx Patterns: -REGEX_SESSION_TOKEN = r"^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" +# --- Nothing Yet # ***************************************************************************************************************** @@ -75,11 +77,58 @@ REGEX_SESSION_TOKEN = r"^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9] # ***************************************************************************************************************** -class TelegramAuth(BaseModel): +class CoreMessageModel(BaseModel): - botToken: str = Field( - description = "the token granted by BotFather", + version: str = Field( + description = "a hint about the version no. of this message", min_length = 1, + frozen = True, + default = "1.0.0" + ) + + ts: AwareDatetime = Field( + description = "the time (utc) at which this message was sent by the sender", + frozen = True + ) + + readTs: AwareDatetime = Field( + description = "the time (utc) at which this message was read and stored by your server", + frozen = True, + default_factory = lambda: date_time.get_current_utc_date_time(as_string = False) + ) + + tokenId: ObjectId = Field( + description = "the id of the auth token that is associated with this message", + frozen = True + ) + + serviceType: Literal["email", "sms", "chat"] = Field( + description = "the kind of service this message was sent/received from", + frozen = True + ) + + client: Literal[ + "gmail", "outlook", # ...................... Mail Clients + "telegram", "whatsapp", # .................. Chat Clients + "nimbusSmsIndia", "savvyBulkSmsKenya", # ... SMS Clients + ] = Field( + description = "the third-part client that was used", + frozen = True + ) + + clientMessageId: str | int = Field( + description = "how the client identifies this message", + frozen = True + ) + + clientThreadId: str | int | None = Field( + description = "how the client identifies the chat/thread in which this message was sent/received", + frozen = True, + default = None + ) + + payload: dict = Field( + description = "the actual contents of the message; will differ for each client", frozen = True ) @@ -88,49 +137,23 @@ class TelegramAuth(BaseModel): # ┗┛┗┛┛┗┛┗┗┫ # ┛ - class Config: - extra = "forbid" - - -# --------------------------------------------------------------------------------------------------------------------- - - -class ChatAuthRequestHeaders(BaseModel): - - sessionToken: str = Field( - description = "the session token of the user who is requesting the service", - pattern = REGEX_SESSION_TOKEN, - frozen = True, - alias = "X-Session-Token" - ) - - # ┏┓ ┏• - # ┃ ┏┓┏┓╋┓┏┓ - # ┗┛┗┛┛┗┛┗┗┫ - # ┛ - class Config: extra = "allow" + arbitrary_types_allowed = True - def model_dump(self, *args, **kwargs): - return super().model_dump(*args, by_alias = True, **kwargs) + # ┓┏ ┓• ┓ • + # ┃┃┏┓┃┓┏┫┏┓╋┓┏┓┏┓ + # ┗┛┗┻┗┗┗┻┗┻┗┗┗┛┛┗ + @field_validator("ts", "readTs", mode = "before") + def parse_date_time(cls, value): + return date_time.parse_date_time(input_value = value, timezone = date_time.TIMEZONE_UTC) -# --------------------------------------------------------------------------------------------------------------------- - - -class ChatAuthRequestData(BaseModel): - - chatClient: Literal["telegram", "whatsapp"] = Field(alias = "client") - auth: Union[TelegramAuth] - - # ┏┓ ┏• - # ┃ ┏┓┏┓╋┓┏┓ - # ┗┛┗┛┛┗┛┗┗┫ - # ┛ - - class Config: - extra = "forbid" + @field_validator("tokenId", mode = "before") + def parse_oid(cls, value): + try: value = ObjectId(value) + except: pass + return value # ***************************************************************************************************************** @@ -141,5 +164,20 @@ class ChatAuthRequestData(BaseModel): if __name__ == "__main__": + from utils_v2.string import json - pass + message = CoreMessageModel( + ts = date_time.get_current_utc_date_time(as_string = False), + tokenId = "67519cf3a7804fcbc6f12452", + serviceType = "email", + client = "gmail", + clientMessageId = 123, + clientThreadId = 456, + payload = { + "from": "bhopli@gmil.com", + "to": "hello@thecaoffice.com", + "message": "Hello, World!" + } + ) + + print("MESSAGE MODEL:", json.to_string(message.model_dump(), default = str)) diff --git a/models/data/database/payment.py b/models/data/database/payment.py index 59123a8..82d3c4c 100644 --- a/models/data/database/payment.py +++ b/models/data/database/payment.py @@ -10,7 +10,7 @@ OBJECTIVE: - To define how messages will be stored in the database. + To define how payment transactions will be stored in the database. REFERENCES: @@ -49,6 +49,9 @@ from bson.objectid import ObjectId # To work with date and time: import datetime +# To work with currencies: +import pycountry + # ***************************************************************************************************************** # ***** **** @@ -77,7 +80,7 @@ import datetime # ***************************************************************************************************************** -class CoreMessageModel(BaseModel): +class CorePaymentModel(BaseModel): version: str = Field( description = "a hint about the version no. of this message", @@ -86,48 +89,79 @@ class CoreMessageModel(BaseModel): default = "1.0.0" ) - ts: AwareDatetime = Field( - description = "the time (utc) at which this message was sent by the sender", - frozen = True + paymentStatus: Literal["requested", "paid", "rejected"] = Field( + description = "the status of the payment request to see what stage of the process we are in", + frozen = False, + default = "requested" ) - readTs: AwareDatetime = Field( - description = "the time (utc) at which this message was read and stored by your server", - frozen = True - ) - - tokenId: ObjectId = Field( - description = "the id of the auth token that is associated with this message", - frozen = True - ) - - serviceType: Literal["email", "sms", "chat"] = Field( - description = "the kind of service this message was sent/received from", - frozen = True - ) - - client: str = Field( - description = "the third-part client that was used", + requestTs: AwareDatetime = Field( + description = "the time (utc) at which the payment request was initiated", frozen = True, - examples = ["gmail", "outlook", "telegram", "whatsapp"] + default_factory = lambda: date_time.get_current_utc_date_time(as_string = False) ) - clientMessageId: str | int = Field( - description = "how the client identifies this message", - frozen = True - ) - - clientThreadId: str | int | None = Field( - description = "how the client identifies the chat/thread in which this message was sent/received", - frozen = True, + responseTs: AwareDatetime | None = Field( + description = "the time (utc) at which the payer responded to the payment request", + frozen = False, default = None ) - payload: dict = Field( - description = "the actual contents of the message", + tokenId: ObjectId = Field( + description = "the id of the auth token that is associated with this payment", frozen = True ) + amount: float | int = Field( + description = "the amount of money being requested" + ) + + currencyCode: str = Field( + description = "the three-letter ISO 4217 code to identify the currency", + frozen = True, + examples = ["INR", "USD", "KES"] + ) + + metadata: dict = Field( + description = "any arbitrary amount of data to identify the user and payment details", + frozen = True + ) + + client: Literal["razorpay", "safaricomMPesaExpress"] = Field( + description = "the third-part client that was used", + frozen = True + ) + + clientPaymentReferenceId: int | str = Field( + description = "the reference id given by the third-party client", + frozen = False, + default = None + ) + + clientPaymentRequestHttpCode: int | str = Field( + description = "the http code the third-party client returned when you requested the payment", + frozen = False, + default = None + ) + + clientPaymentRequestJSON: str | int = Field( + description = "how the third-party client responded when you requested the payment", + frozen = False, + default = None + ) + + clientPaymentResponseHttpCode: int | str = Field( + description = "the http code the third-party client returned when your user responded to the payment request", + frozen = False, + default = None + ) + + clientPaymentResponseJSON: str | int | None = Field( + description = "how the third-party client responded when your user responded to the payment request", + frozen = False, + default = None + ) + # ┏┓ ┏• # ┃ ┏┓┏┓╋┓┏┓ # ┗┛┗┛┛┗┛┗┗┫ @@ -135,12 +169,13 @@ class CoreMessageModel(BaseModel): class Config: extra = "allow" + arbitrary_types_allowed = True # ┓┏ ┓• ┓ • # ┃┃┏┓┃┓┏┫┏┓╋┓┏┓┏┓ # ┗┛┗┻┗┗┗┻┗┻┗┗┗┛┛┗ - @field_validator("ts", "readTs", mode = "before") + @field_validator("requestTs", "responseTs", mode = "before") def parse_date_time(cls, value): return date_time.parse_date_time(input_value = value, timezone = date_time.TIMEZONE_UTC) @@ -150,6 +185,12 @@ class CoreMessageModel(BaseModel): except: pass return value + @field_validator("currencyCode", mode = "before") + def validate_currency(cls, value): + currency = pycountry.currencies.get(alpha_3 = value) + if currency is None: raise ValueError("invalid currency code, please use iso 4217 standard") + return value + # ***************************************************************************************************************** # ***** **** @@ -160,4 +201,18 @@ class CoreMessageModel(BaseModel): if __name__ == "__main__": - pass + from utils_v2.string import json + + payment = CorePaymentModel( + paymentStatus = "requested", + tokenId = "67519cf3a7804fcbc6f12452", + amount = 1.00, + currencyCode = "INR", + metadata = { + "userId": 1, + "name": "My Test" + }, + client = "safaricomMPesaExpress" + ) + + print("PAYMENT TXN. MODEL:", json.to_string(payment.model_dump(), default = str))