Skip to content

Initial project structure for Rukn AlTasawoq backend. #796

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,095 changes: 35 additions & 1,060 deletions README.md

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
requests
beautifulsoup4
pydantic
notion-client
gspread
oauth2client
Binary file not shown.
121 changes: 121 additions & 0 deletions rukn_altasawoq/models/schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from pydantic import BaseModel, Field
from typing import List, Optional
from datetime import datetime

from uuid import uuid4

class Client(BaseModel):
id: str = Field(default_factory=lambda: str(uuid4()), alias="_id")
name: str
phone: str
sessions: List[str] = []
addresses: List[str] = []
wallet_id: Optional[str] = None
affiliate_level: str = "Free"
loyalty_points: int = 0
preferences: dict = {}
behavior_history: List[str] = []
customer_type: str = "New"
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)


class Quote(BaseModel):
id: str = Field(default_factory=lambda: str(uuid4()), alias="_id")
customer_id: str
product_links: List[str] = []
images: List[str] = []
cart: List[dict] = []
voice_note: Optional[str] = None
quote_price: float
markup_percentage: float = 35.0
tax: float = 0.0
discount: float = 0.0
final_price: float
urgency_tag: Optional[str] = None
status: str = "Pending" # Pending, Approved, Rejected
handler_id: Optional[str] = None
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)


class Order(BaseModel):
id: str = Field(default_factory=lambda: str(uuid4()), alias="_id")
quote_id: str
customer_id: str
products: List[dict]
total_price: float
payment_status: str = "Unpaid" # Unpaid, Paid, Refunded
order_status: str = "Confirmed" # Confirmed, Shipped, Delivered, Canceled
shipping_id: Optional[str] = None
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)


class Wallet(BaseModel):
id: str = Field(default_factory=lambda: str(uuid4()), alias="_id")
customer_id: str
balance: float = 0.0
transactions: List[dict] = []
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)


class Coupon(BaseModel):
id: str = Field(default_factory=lambda: str(uuid4()), alias="_id")
code: str
discount_percentage: Optional[float] = None
discount_amount: Optional[float] = None
valid_from: datetime
valid_to: datetime
is_active: bool = True
usage_limit: int = 1
used_count: int = 0
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)


class Product(BaseModel):
id: str = Field(default_factory=lambda: str(uuid4()), alias="_id")
name: str
description: Optional[str] = None
price: float
currency: str = "USD"
supplier: Optional[str] = None
product_url: Optional[str] = None
images: List[str] = []
attributes: dict = {}
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)


class Shipment(BaseModel):
id: str = Field(default_factory=lambda: str(uuid4()), alias="_id")
order_id: str
tracking_number: str
provider: str
status: str = "Pending" # Pending, In-Transit, Delivered, Failed
tracking_history: List[dict] = []
media: dict = {
"invoice": Optional[str],
"proof_of_delivery": Optional[str],
"waybill": Optional[str],
"warehouse_receipt": Optional[str],
}
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)


class Affiliate(BaseModel):
id: str = Field(default_factory=lambda: str(uuid4()), alias="_id")
client_id: str
level: str # Free, Paid, Sponsored
upline_id: Optional[str] = None
downline_ids: List[str] = []
commission_rate: float
commission_balance: float = 0.0
performance_score: float = 0.0
linked_offers: List[str] = []
traffic_sources: List[str] = []
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)
Binary file not shown.
47 changes: 47 additions & 0 deletions rukn_altasawoq/quote_engine/processor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import requests
from bs4 import BeautifulSoup
from rukn_altasawoq.models.schemas import Quote

def get_product_price_from_url(url: str) -> float:
"""
Fetches the price of a product from a given URL.
This is a placeholder and would need to be adapted for specific websites.
"""
try:
response = requests.get(url)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser')

# This is a placeholder for price extraction logic.
# In a real implementation, you would need specific selectors for each site.
# For example, for Amazon, it might be:
# price = soup.find('span', {'id': 'priceblock_ourprice'}).text
# For now, we'll return a dummy price.
return 100.0
except requests.exceptions.RequestException as e:
print(f"Error fetching product URL: {e}")
return 0.0

def create_quote_from_url(customer_id: str, product_url: str) -> Quote:
"""
Creates a quote for a given customer and product URL.
"""
price = get_product_price_from_url(product_url)

markup = 0.35 # 35%
tax = 0.15 # 15%

quote_price = price
final_price = price * (1 + markup + tax)

quote = Quote(
customer_id=customer_id,
product_links=[product_url],
quote_price=quote_price,
markup_percentage=markup * 100,
tax=tax * 100,
final_price=final_price,
status="Pending"
)

return quote
23 changes: 23 additions & 0 deletions rukn_altasawoq/services/chatbot_builder_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import os
import requests

class ChatbotBuilderService:
def __init__(self):
self.api_key = os.environ.get("CHATBOT_BUILDER_API_KEY")
self.base_url = "https://api.chatbotbuilder.com" # Replace with actual API base URL

def send_message(self, user_id: str, message: str):
# Logic to send a message to a user
pass

def trigger_flow(self, user_id: str, flow_id: str):
# Logic to trigger a flow for a user
pass

def get_user_data(self, user_id: str):
# Logic to get user data
pass

def update_user_tags(self, user_id: str, tags: list):
# Logic to update user tags
pass
24 changes: 24 additions & 0 deletions rukn_altasawoq/services/google_sheets_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import os
import gspread
from oauth2client.service_account import ServiceAccountCredentials

class GoogleSheetsService:
def __init__(self):
scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]
creds = ServiceAccountCredentials.from_json_keyfile_name(os.environ.get("GOOGLE_SHEETS_CREDENTIALS_PATH"), scope)
self.client = gspread.authorize(creds)

def get_sheet(self, sheet_name: str):
return self.client.open(sheet_name).sheet1

def add_row(self, sheet_name: str, data: list):
sheet = self.get_sheet(sheet_name)
sheet.append_row(data)

def get_all_records(self, sheet_name: str):
sheet = self.get_sheet(sheet_name)
return sheet.get_all_records()

def update_cell(self, sheet_name: str, row: int, col: int, value: str):
sheet = self.get_sheet(sheet_name)
sheet.update_cell(row, col, value)
22 changes: 22 additions & 0 deletions rukn_altasawoq/services/notion_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os
from notion_client import Client

class NotionService:
def __init__(self):
self.notion = Client(auth=os.environ.get("NOTION_API_KEY"))

def create_database(self, parent_page_id: str, schema: dict):
# Logic to create a new database in Notion
pass

def add_entry(self, database_id: str, data: dict):
# Logic to add a new entry to a Notion database
pass

def query_database(self, database_id: str, filters: dict):
# Logic to query a Notion database
pass

def update_entry(self, page_id: str, data: dict):
# Logic to update an entry in a Notion database
pass
Binary file not shown.
27 changes: 27 additions & 0 deletions rukn_altasawoq/tests/test_quote_engine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import unittest
from unittest.mock import patch
from rukn_altasawoq.quote_engine.processor import create_quote_from_url

class TestQuoteEngine(unittest.TestCase):

@patch('rukn_altasawoq.quote_engine.processor.get_product_price_from_url')
def test_create_quote_from_url(self, mock_get_price):
# Arrange
mock_get_price.return_value = 100.0
customer_id = "test_customer"
product_url = "http://example.com/product"

# Act
quote = create_quote_from_url(customer_id, product_url)

# Assert
self.assertEqual(quote.customer_id, customer_id)
self.assertEqual(quote.product_links, [product_url])
self.assertEqual(quote.quote_price, 100.0)
self.assertEqual(quote.markup_percentage, 35.0)
self.assertEqual(quote.tax, 15.0)
self.assertEqual(quote.final_price, 150.0)
self.assertEqual(quote.status, "Pending")

if __name__ == '__main__':
unittest.main()