triflix's picture
Upload 53 files
3c4e575 verified
from fastapi import APIRouter, Request, Depends, HTTPException
from fastapi.responses import HTMLResponse, RedirectResponse
from sqlalchemy.orm import Session
from app import models, schemas, crud
from app.database import get_db
from app.auth import get_current_active_user
from app.main import templates # Import templates from main.py
import logging
import yfinance as yf
from datetime import datetime, timedelta
import pytz # for timezone handling
from typing import List
logger = logging.getLogger(__name__)
router = APIRouter()
# Define timezone (Asia/Kolkata)
tz = pytz.timezone('Asia/Kolkata')
# Function to read tickers from tickers.txt
def get_all_tickers():
tickers = []
try:
with open("app/tickers.txt", "r") as f:
tickers = [line.strip() for line in f if line.strip()]
except FileNotFoundError:
logger.error("tickers.txt not found.")
return tickers
# Function to find closest available trading day price on or before the target date
def get_closest_price(hist, date):
# Filter dates in hist index that are <= target date
available_dates = hist.index[hist.index <= date]
if not available_dates.empty:
closest_date = available_dates[-1]
return hist.loc[closest_date]['Close'], closest_date.date()
else:
return None, None
# Function to fetch historical data and calculate prices
def fetch_stock_prices(ticker_symbol: str):
ticker = yf.Ticker(ticker_symbol)
# Get today's date as timezone-aware datetime
today = datetime.now(tz)
# Define the dates for 1 month, 6 months, 1 year, and 3 years ago (timezone-aware)
dates = {
"Current": today,
"1 Month Ago": today - timedelta(days=30),
"6 Months Ago": today - timedelta(days=182),
"1 Year Ago": today - timedelta(days=365),
"3 Years Ago": today - timedelta(days=3*365),
}
# Fetch historical data for the last 4 years to cover all dates
start_date = (today - timedelta(days=4*365)).strftime('%Y-%m-%d')
end_date = today.strftime('%Y-%m-%d')
hist = ticker.history(start=start_date, end=end_date)
# The index of hist is timezone-aware, ensure it matches tz
if hist.index.tz is None:
# If index is naive, localize it
hist.index = hist.index.tz_localize(tz)
else:
# Convert to desired timezone
hist.index = hist.index.tz_convert(tz)
# Retrieve prices for each date
prices = {}
for label, date in dates.items():
price, actual_date = get_closest_price(hist, date)
if price is not None:
prices[label] = (f"₹{price:.2f}", actual_date.strftime('%Y-%m-%d') if actual_date else None)
else:
prices[label] = ("No data", None)
return prices
# API endpoint for searching tickers
@router.get("/api/stocks/search", response_model=List[str])
async def search_stocks(query: str):
all_tickers = get_all_tickers()
# Simple case-insensitive search
matching_tickers = [ticker for ticker in all_tickers if query.lower() in ticker.lower()]
return matching_tickers
# API endpoint for fetching stock prices
@router.get("/api/stocks/price/{ticker_symbol}")
async def get_stock_price(ticker_symbol: str):
try:
prices = fetch_stock_prices(ticker_symbol)
return prices
except Exception as e:
logger.error(f"Error fetching price for {ticker_symbol}: {e}")
raise HTTPException(status_code=500, detail="Could not fetch stock price")
@router.get("/home", response_class=HTMLResponse)
async def user_homepage(request: Request): # Removed: current_user: models.User = Depends(get_current_active_user)
# User data will be fetched and displayed by client-side JavaScript
return templates.TemplateResponse("user/homepage.html", {
"request": request,
# "user": current_user, # This will be handled by client-side JS
"title": "User Homepage"
})
@router.get("/chatbot/{model_type}", response_class=HTMLResponse)
async def chatbot_page(request: Request, model_type: str): # Removed: current_user: models.User = Depends(get_current_active_user)
# Client-side JS on chatbot.html already handles token for API submission.
# This route now just serves the page structure.
valid_models = ["base", "enhanced", "rule_based"]
if model_type.lower() not in valid_models:
raise HTTPException(status_code=404, detail="Model type not found")
# Prepare context for the template based on model_type
# This context will help the template render the correct form fields
form_fields = []
page_title = ""
if model_type == "base":
page_title = "Base Model Advisor"
form_fields = [
{"name": "Salary", "label": "Monthly Salary (₹)", "type": "number", "required": True, "min": 0},
{"name": "Expenses", "label": "Monthly Expenses (₹)", "type": "number", "required": True, "min": 0},
{"name": "Savings", "label": "Monthly Savings (₹)", "type": "number", "required": True, "min": 0},
{"name": "Lifecycle_Stage", "label": "Lifecycle Stage", "type": "select", "required": True, "options": ["Student", "Early Career", "Mid-Career", "Late Career", "Retired"]},
{"name": "Risk_Appetite", "label": "Risk Appetite", "type": "select", "required": True, "options": ["Low", "Medium", "High"]},
{"name": "Investment_Horizon", "label": "Investment Horizon", "type": "select", "required": True, "options": ["Short-term", "Medium-term", "Long-term"]},
]
elif model_type == "enhanced":
page_title = "Enhanced Model Advisor"
# Define the options for dropdowns based on provided lists
cities = sorted([
'Mumbai', 'Delhi', 'Bangalore', 'Hyderabad', 'Pune', 'Chennai', 'Jaipur',
'Kochi', 'Kolkata', 'Ahmedabad', 'Gurgaon', 'Lucknow', 'Nagpur', 'Chandigarh',
'Surat', 'Indore', 'Bhopal'
])
professions = sorted([
'Software Engineer', 'Doctor', 'Retired Banker', 'Student', 'Marketing Manager', 'Teacher',
'Freelancer', 'Architect', 'Business Owner', 'Nurse', 'Product Manager', 'CA', 'Data Analyst',
'Retired Professor', 'Journalist', 'Graphic Designer', 'Sales Executive', 'HR Manager', 'Intern',
'Dentist', 'Lawyer', 'Content Creator', 'Pensioner', 'UX Designer', 'Government Employee',
'Fashion Designer', 'Startup Founder', 'Homemaker (Investor)', 'Investment Banker', 'IT Consultant',
'College Student', 'Pharmacist', 'Textile Business Owner', 'Event Planner', 'Film Producer',
'Nutritionist', 'Retired Army Officer', 'Social Media Manager', 'Airline Pilot', 'Biotech Researcher',
'Real Estate Agent', 'AI Engineer', 'Retired Teacher', 'Financial Analyst', 'Software Developer',
'NGO Director', 'Fitness Trainer', 'Digital Marketer', 'Content Strategist', 'Retired Engineer',
'UI Developer', 'Operations Manager', 'Corporate Lawyer', 'School Principal', 'AI Researcher',
'Junior Doctor', 'Finance Manager', 'Fashion Blogger', 'HR Consultant', 'Video Editor',
'Small Business Owner', 'Dietitian', 'Supply Chain Manager', 'Interior Designer', 'Sales Manager',
'PR Executive', 'Logistics Head', 'Content Writer', 'Retired Govt. Employee', 'Marketing Lead',
'IT Manager', 'Startup Intern', 'Export Manager', 'Fitness Instructor', 'Real Estate Broker',
'Event Manager', 'Software Trainee', 'Data Scientist', 'HR Director', 'Hotel Manager',
'Social Worker', 'Cybersecurity Expert', 'E-commerce Manager', 'Bank Manager', 'Retired IT Manager',
'UX Researcher', 'Product Designer', 'Cybersecurity Analyst', 'Podcast Producer', 'E-commerce Seller',
'Cloud Architect', 'Corporate Trainer', 'AI Trainer', 'Supply Chain Head', 'Product Owner',
'UI/UX Designer', 'Finance Director', 'Sustainability Consultant', 'Retired Bank Manager',
'Social Media Influencer', 'Blockchain Developer', 'NGO Head', 'Data Engineer', 'Event Curator',
'CTO', 'Content Marketer', 'Retired Army Major', 'AR/VR Developer', 'Logistics Manager', 'VP Sales',
'EdTech Founder', 'SEO Specialist', 'Yoga Instructor', 'IT Director', 'App Developer',
'Consultant Cardiologist', 'Freelance Writer', 'Cloud Engineer', 'Retired CA', 'Digital Artist',
'Retired Pilot', 'Graphic Animator', 'AI Ethicist', 'School Trustee', 'Robotics Engineer',
'Fashion Stylist', 'Retired Nurse', 'AI Ethics Consultant', 'Drone Engineer', 'Retired Bank Clerk',
'Digital Nomad', 'CFO', 'Sustainability Analyst', 'Retired Journalist', 'Social Entrepreneur',
'DevOps Engineer', 'School Counselor', 'Retired Army Colonel', 'AR Developer', 'Sales Director',
'EdTech Consultant', 'SEO Expert', 'Cardiologist', '3D Artist', 'HR Head', 'Animator','Other',
'Fashion Influencer'
])
form_fields = [
{"name": "Profession", "label": "Profession", "type": "select", "required": True, "options": professions},
{"name": "City", "label": "City", "type": "select", "required": True, "options": cities},
{"name": "Salary", "label": "Monthly Salary (₹)", "type": "number", "required": True, "min": 0},
{"name": "Expenses", "label": "Monthly Expenses (₹)", "type": "number", "required": True, "min": 0},
{"name": "Savings", "label": "Monthly Savings (₹)", "type": "number", "required": True, "min": 0},
{"name": "Lifecycle_Stage", "label": "Lifecycle Stage", "type": "select", "required": True, "options": ["Student", "Early Career", "Mid-Career", "Late Career", "Retired"]},
{"name": "Risk_Appetite", "label": "Risk Appetite", "type": "select", "required": True, "options": ["Low", "Medium", "High"]},
{"name": "Investment_Horizon", "label": "Investment Horizon", "type": "select", "required": True, "options": ["Short-term", "Medium-term", "Long-term"]},
]
elif model_type == "rule_based":
page_title = "Rule-Based Advisor"
form_fields = [
{"name": "Lifecycle_Stage", "label": "Lifecycle Stage", "type": "select", "required": True, "options": ["Student", "Early Career", "Mid-Career", "Late Career", "Retired"]},
{"name": "Risk_Appetite", "label": "Risk Appetite", "type": "select", "required": True, "options": ["Low", "Medium", "High"]},
{"name": "Investment_Horizon", "label": "Investment Horizon", "type": "select", "required": True, "options": ["Short-term", "Medium-term", "Long-term"]},
{"name": "Annual_Salary_Package", "label": "Annual Salary Package (CTC) (₹)", "type": "number", "required": True, "min": 0},
{"name": "Monthly_In_hand_Salary", "label": "Actual Monthly In-hand Salary (₹)", "type": "number", "required": True, "min": 0},
{"name": "Total_Monthly_Expenses", "label": "Total Estimated Monthly Expenses (₹)", "type": "number", "required": True, "min": 0},
]
return templates.TemplateResponse("user/chatbot.html", {
"request": request,
# "user": current_user, # Client-side JS on this page handles API calls with token
"title": page_title,
"model_type": model_type,
"form_fields": form_fields
})
@router.get("/recommendations", response_class=HTMLResponse)
async def recommendations_page(request: Request): # Removed: current_user: models.User = Depends(get_current_active_user)
# Client-side JS could be added to this page if it needs to display user-specific info
# or make authenticated API calls for dynamic market data.
# For now, it serves static structure with mocked data.
# Data fetching logic would go into services/market_data_service.py
# For now, just rendering a template.
market_data = { # Mock data
"stocks": [
{"name": "Reliance Industries", "price": "2,850.50 INR", "change": "+0.5%"},
{"name": "Tata Consultancy Services", "price": "3,900.75 INR", "change": "-0.2%"},
{"name": "HDFC Bank", "price": "1,500.20 INR", "change": "+1.1%"},
],
"gold_price": "96,172.27 -745.25 INR per 10g",
"recommended_stocks": [
{"name": "Infosys", "reason": "Strong growth potential in IT sector."},
{"name": "ICICI Bank", "reason": "Good financial performance and outlook."}
]
}
return templates.TemplateResponse("user/recommendations.html", {
"request": request,
# "user": current_user, # Can be fetched by client-side JS if needed
"title": "Market Trends & Recommendations",
"market_data": market_data
})