260 lines
10 KiB
Python
260 lines
10 KiB
Python
import os
|
|
import platform
|
|
import random
|
|
from datetime import date, timedelta
|
|
from django.shortcuts import render, get_object_or_404, redirect
|
|
from django.utils import timezone
|
|
from django.db.models import Count, Q
|
|
from django.http import JsonResponse
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.contrib.auth.forms import UserCreationForm
|
|
from django.contrib.auth import login as auth_login
|
|
from .models import MemberOfParliament, TradeDisclosure, Watchlist
|
|
|
|
def home(request):
|
|
"""Render the landing screen with MP trades and environment details."""
|
|
|
|
# Seed data if empty
|
|
if not MemberOfParliament.objects.exists():
|
|
mps = [
|
|
{"name": "Justin Trudeau", "party": "Liberal", "constituency": "Papineau", "province": "Quebec"},
|
|
{"name": "Pierre Poilievre", "party": "Conservative", "constituency": "Carleton", "province": "Ontario"},
|
|
{"name": "Jagmeet Singh", "party": "NDP", "constituency": "Burnaby South", "province": "British Columbia"},
|
|
{"name": "Chrystia Freeland", "party": "Liberal", "constituency": "University—Rosedale", "province": "Ontario"},
|
|
]
|
|
for mp_data in mps:
|
|
MemberOfParliament.objects.get_or_create(**mp_data)
|
|
|
|
mp_jt = MemberOfParliament.objects.get(name="Justin Trudeau")
|
|
mp_pp = MemberOfParliament.objects.get(name="Pierre Poilievre")
|
|
|
|
TradeDisclosure.objects.get_or_create(
|
|
mp=mp_jt, ticker="AAPL", company_name="Apple Inc.",
|
|
trade_type="BUY", amount_range="$15,001 - $50,000",
|
|
disclosure_date=date.today() - timedelta(days=2)
|
|
)
|
|
TradeDisclosure.objects.get_or_create(
|
|
mp=mp_pp, ticker="SHOP", company_name="Shopify Inc.",
|
|
trade_type="SELL", amount_range="$50,001 - $100,000",
|
|
disclosure_date=date.today() - timedelta(days=5)
|
|
)
|
|
TradeDisclosure.objects.get_or_create(
|
|
mp=mp_jt, ticker="TSLA", company_name="Tesla, Inc.",
|
|
trade_type="BUY", amount_range="$1,000 - $15,000",
|
|
disclosure_date=date.today() - timedelta(days=10)
|
|
)
|
|
|
|
party_filter = request.GET.get('party')
|
|
trades_qs = TradeDisclosure.objects.select_related('mp').order_by('-disclosure_date')
|
|
|
|
if party_filter:
|
|
trades_qs = trades_qs.filter(mp__party=party_filter)
|
|
|
|
trades = trades_qs[:10]
|
|
total_trades = TradeDisclosure.objects.count()
|
|
total_mps = MemberOfParliament.objects.count()
|
|
|
|
# Trending Assets (Top 3 by trade count)
|
|
trending_assets = TradeDisclosure.objects.values('ticker', 'company_name').annotate(
|
|
trade_count=Count('id')
|
|
).order_by('-trade_count')[:3]
|
|
|
|
# Get user's followed MPs if logged in
|
|
followed_mps = []
|
|
if request.user.is_authenticated:
|
|
followed_mps = Watchlist.objects.filter(user=request.user, mp__isnull=False).values_list('mp_id', flat=True)
|
|
|
|
context = {
|
|
"project_name": "Canada MP Trade Tracker",
|
|
"trades": trades,
|
|
"total_trades": total_trades,
|
|
"total_mps": total_mps,
|
|
"trending_assets": trending_assets,
|
|
"current_time": timezone.now(),
|
|
"selected_party": party_filter,
|
|
"followed_mps": followed_mps,
|
|
}
|
|
|
|
if request.headers.get('x-requested-with') == 'XMLHttpRequest':
|
|
return render(request, "core/partials/trade_feed.html", context)
|
|
|
|
return render(request, "core/index.html", context)
|
|
|
|
def mp_list(request):
|
|
"""Render a list of all Members of Parliament."""
|
|
mps = MemberOfParliament.objects.all().order_by('name')
|
|
context = {
|
|
"project_name": "Canada MP Trade Tracker",
|
|
"mps": mps,
|
|
}
|
|
return render(request, "core/mp_list.html", context)
|
|
|
|
def mp_detail(request, pk):
|
|
"""Detailed view for a specific MP."""
|
|
mp = get_object_or_404(MemberOfParliament, pk=pk)
|
|
trades = mp.trades.all().order_by('-disclosure_date')
|
|
|
|
is_followed = False
|
|
if request.user.is_authenticated:
|
|
is_followed = Watchlist.objects.filter(user=request.user, mp=mp).exists()
|
|
|
|
context = {
|
|
"project_name": "Canada MP Trade Tracker",
|
|
"mp": mp,
|
|
"trades": trades,
|
|
"is_followed": is_followed,
|
|
}
|
|
return render(request, "core/mp_detail.html", context)
|
|
|
|
def ticker_list(request):
|
|
"""List of all assets/tickers traded by MPs."""
|
|
search_query = request.GET.get('q', '')
|
|
|
|
tickers_qs = TradeDisclosure.objects.values('ticker', 'company_name').annotate(
|
|
mp_count=Count('mp', distinct=True),
|
|
trade_count=Count('id')
|
|
)
|
|
|
|
if search_query:
|
|
tickers_qs = tickers_qs.filter(
|
|
Q(ticker__icontains=search_query) | Q(company_name__icontains=search_query)
|
|
)
|
|
|
|
tickers = tickers_qs.order_by('-trade_count')
|
|
|
|
context = {
|
|
"project_name": "Canada MP Trade Tracker",
|
|
"tickers": tickers,
|
|
"search_query": search_query,
|
|
}
|
|
return render(request, "core/ticker_list.html", context)
|
|
|
|
def ticker_detail(request, ticker):
|
|
"""Detailed view for a specific ticker."""
|
|
trades = TradeDisclosure.objects.filter(ticker=ticker).select_related('mp').order_by('-disclosure_date')
|
|
company_name = trades.first().company_name if trades.exists() else ticker
|
|
|
|
is_followed = False
|
|
if request.user.is_authenticated:
|
|
is_followed = Watchlist.objects.filter(user=request.user, ticker=ticker).exists()
|
|
|
|
context = {
|
|
"project_name": "Canada MP Trade Tracker",
|
|
"ticker": ticker,
|
|
"company_name": company_name,
|
|
"trades": trades,
|
|
"is_followed": is_followed,
|
|
}
|
|
return render(request, "core/ticker_detail.html", context)
|
|
|
|
@login_required
|
|
def toggle_follow(request):
|
|
"""Toggle following an MP or Ticker via AJAX."""
|
|
if request.method == 'POST':
|
|
mp_id = request.POST.get('mp_id')
|
|
ticker = request.POST.get('ticker')
|
|
|
|
if mp_id:
|
|
mp = get_object_or_404(MemberOfParliament, id=mp_id)
|
|
obj, created = Watchlist.objects.get_or_create(user=request.user, mp=mp)
|
|
if not created:
|
|
obj.delete()
|
|
return JsonResponse({'status': 'unfollowed', 'type': 'mp', 'mp_id': mp_id})
|
|
return JsonResponse({'status': 'followed', 'type': 'mp', 'mp_id': mp_id})
|
|
|
|
if ticker:
|
|
obj, created = Watchlist.objects.get_or_create(user=request.user, ticker=ticker)
|
|
if not created:
|
|
obj.delete()
|
|
return JsonResponse({'status': 'unfollowed', 'type': 'ticker', 'ticker': ticker})
|
|
return JsonResponse({'status': 'followed', 'type': 'ticker', 'ticker': ticker})
|
|
|
|
return JsonResponse({'status': 'error'}, status=400)
|
|
|
|
def signup(request):
|
|
"""Handle user registration."""
|
|
if request.method == 'POST':
|
|
form = UserCreationForm(request.POST)
|
|
if form.is_valid():
|
|
user = form.save()
|
|
auth_login(request, user)
|
|
return redirect('home')
|
|
else:
|
|
form = UserCreationForm()
|
|
return render(request, 'registration/signup.html', {'form': form})
|
|
|
|
@login_required
|
|
def profile(request):
|
|
"""User profile page showing their watchlist and performance summary."""
|
|
followed_mps_objs = Watchlist.objects.filter(user=request.user, mp__isnull=False).select_related('mp')
|
|
followed_tickers_objs = Watchlist.objects.filter(user=request.user, ticker__isnull=False)
|
|
|
|
# Performance calculations (Simulated yet deterministic based on data)
|
|
total_trades_tracked = 0
|
|
avg_success_rate = 0
|
|
estimated_roi = 0
|
|
|
|
mp_list_data = []
|
|
for wt in followed_mps_objs:
|
|
# Deterministic performance based on MP ID
|
|
mp_id = wt.mp.id
|
|
mp_success = (mp_id * 7 % 25) + 70 # 70-95%
|
|
mp_roi = (mp_id * 3 % 15) + 5 # 5-20%
|
|
|
|
trade_count = TradeDisclosure.objects.filter(mp=wt.mp).count()
|
|
total_trades_tracked += trade_count
|
|
|
|
mp_list_data.append({
|
|
'mp': wt.mp,
|
|
'success_rate': mp_success,
|
|
'roi': mp_roi,
|
|
'trade_count': trade_count
|
|
})
|
|
|
|
ticker_list_data = []
|
|
for wt in followed_tickers_objs:
|
|
# Deterministic performance based on Ticker string
|
|
ticker_seed = sum(ord(c) for c in wt.ticker)
|
|
ticker_success = (ticker_seed % 20) + 75 # 75-95%
|
|
ticker_roi = (ticker_seed % 12) + 8 # 8-20%
|
|
|
|
latest_trade = TradeDisclosure.objects.filter(ticker=wt.ticker).first()
|
|
trade_count = TradeDisclosure.objects.filter(ticker=wt.ticker).count()
|
|
|
|
ticker_list_data.append({
|
|
'ticker': wt.ticker,
|
|
'company_name': latest_trade.company_name if latest_trade else wt.ticker,
|
|
'success_rate': ticker_success,
|
|
'roi': ticker_roi,
|
|
'trade_count': trade_count
|
|
})
|
|
|
|
# Aggregate performance for the summary
|
|
combined_items = mp_list_data + ticker_list_data
|
|
if combined_items:
|
|
avg_success_rate = sum(i['success_rate'] for i in combined_items) / len(combined_items)
|
|
estimated_roi = sum(i['roi'] for i in combined_items) / len(combined_items)
|
|
|
|
# Market Comparison Data (Simulated)
|
|
market_data = [
|
|
{"name": "Your Portfolio", "roi": round(estimated_roi, 1), "class": "bg-primary"},
|
|
{"name": "S&P 500", "roi": 10.2, "class": "bg-secondary"},
|
|
{"name": "TSX Composite", "roi": 6.8, "class": "bg-info"},
|
|
{"name": "Bitcoin (BTC)", "roi": 42.5, "class": "bg-warning"},
|
|
]
|
|
# Sort market data by ROI descending
|
|
market_data = sorted(market_data, key=lambda x: x['roi'], reverse=True)
|
|
|
|
context = {
|
|
"project_name": "Canada MP Trade Tracker",
|
|
"followed_mps": mp_list_data,
|
|
"ticker_data": ticker_list_data,
|
|
"performance": {
|
|
"total_trades": total_trades_tracked,
|
|
"avg_success": round(avg_success_rate, 1),
|
|
"estimated_roi": round(estimated_roi, 1),
|
|
"portfolio_health": "Outperforming" if estimated_roi > 10 else "Steady"
|
|
},
|
|
"market_comparison": market_data
|
|
}
|
|
return render(request, "core/profile.html", context) |