From 16066e4ba484bf27c8fc2fee53a456183a591b09 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 6 Feb 2026 20:41:24 +0000 Subject: [PATCH] Mock 5 --- core/__pycache__/urls.cpython-311.pyc | Bin 555 -> 1105 bytes core/__pycache__/views.cpython-311.pyc | Bin 3492 -> 13754 bytes core/templates/base.html | 100 +++++++- core/templates/core/index.html | 145 ++++++------ core/templates/core/mp_detail.html | 136 +++++++++++ core/templates/core/mp_list.html | 4 +- core/templates/core/partials/trade_feed.html | 58 +++++ core/templates/core/profile.html | 226 +++++++++++++++++++ core/templates/core/ticker_detail.html | 129 +++++++++++ core/templates/core/ticker_list.html | 110 +++++++++ core/templates/registration/login.html | 4 +- core/templates/registration/signup.html | 59 +++++ core/urls.py | 8 +- core/views.py | 207 ++++++++++++++++- 14 files changed, 1091 insertions(+), 95 deletions(-) create mode 100644 core/templates/core/mp_detail.html create mode 100644 core/templates/core/partials/trade_feed.html create mode 100644 core/templates/core/profile.html create mode 100644 core/templates/core/ticker_detail.html create mode 100644 core/templates/core/ticker_list.html create mode 100644 core/templates/registration/signup.html diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 1f7e70bc2515dd4fe6c25583e5bcc5a5307fb7ab..107c43191ae381e927d6407bbd5ebd75b8bad1d2 100644 GIT binary patch literal 1105 zcmbVKzfaph6h1pI{t+k80u4}9kScWuYT^m0L==|Nu}dpeat9QAhjH=Q*4Zv4QdO2p zjMTKNMx9EAL}f#!ilI=|P-(+xP|eT;BqAbU zAb{(?r1Kg=mwc91FfyrQrIJyJgd3HNMr7QqWLiYQGnLFNQSpOH<{@d|xk_f9Xn3KL zdBnAgxP8t$bIE6E=e&cLe3oXZMiVc-MV+PV9O#G{_=E5;lWo^@B22FHxQ>#+p+`w3 zQ-9B~gD_M129t1LJDC>R<`H2f-kLg7t`&Ng<&eDt&vCrtObTqvjr>gZnRj42BtuNf z(+e5614@QK{Xl0jhlWk+>sz)P_Wh%6ohdn_l)3o{HFpYr?K927rO$S(vJG7CY4fTC_uEz@{7?{Y|(8~jAKJOjFWuIC-rqkXO- z(#dCHMxn>_V~-sL{=g)<=`l|8x;_8oPT8U!jvTVh+B`VFqu?oDZ)$=dT%l)i)wn`d z{J-NyLUr}@)!E$HyRj6*S^{e+tX+Y^vG!mQ6 kNS}~?LC)f$nB^5Q%Zn^lS6Hk*u(R+pHE@Gq5g*VP02^mH@Bjb+ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index ad9201fed461d49b3eda9fe587c7474dca5e88b7..5253fce6e08e67899b9104763a138555b5230462 100644 GIT binary patch literal 13754 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 J`WL*ie*-Km^RoZ| diff --git a/core/templates/base.html b/core/templates/base.html index c815bce..889b003 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -10,6 +10,8 @@ + + @@ -96,19 +98,31 @@
{% if user.is_authenticated %} - Hello, {{ user.username }} -
- {% csrf_token %} - -
+ {% else %} Login - Get Started + Get Started {% endif %}
@@ -126,5 +140,73 @@ + + - \ No newline at end of file + diff --git a/core/templates/core/index.html b/core/templates/core/index.html index 60dabea..6f35372 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -29,66 +29,22 @@

Live Disclosure Feed

-
- - - - - - - - - - - - - {% for trade in trades %} - - - - - - - - - {% empty %} - - - - {% endfor %} - -
MPTickerTypeAmountDate
-
-
{{ trade.mp.name|slice:":1" }}
-
-
{{ trade.mp.name }}
-
{{ trade.mp.party }}
-
-
-
-
{{ trade.ticker }}
-
{{ trade.company_name }}
-
- {% if trade.trade_type == 'BUY' %} - BUY - {% else %} - SELL - {% endif %} - {{ trade.amount_range }}{{ trade.disclosure_date|date:"M d, Y" }} - -
No disclosures found yet.
+
+ {% include 'core/partials/trade_feed.html' %}
+ @@ -100,40 +56,35 @@

Start Your Portfolio

Connect your account to set alerts and follow specific MPs or tickers.

- Create Free Account + {% if not user.is_authenticated %} + Create Free Account + {% else %} +

Welcome back, {{ user.username }}!

+ {% endif %}
@@ -186,8 +137,48 @@ justify-content: center; font-weight: 700; font-size: 0.8rem; + background-color: rgba(255,255,255,0.05) !important; + } + .transition-hover:hover { + background-color: rgba(255, 255, 255, 0.05) !important; + padding-left: 5px !important; } - .bg-blue { background-color: #3b82f6; } - .bg-success { background-color: #10b981; } + + {% endblock %} \ No newline at end of file diff --git a/core/templates/core/mp_detail.html b/core/templates/core/mp_detail.html new file mode 100644 index 0000000..717e110 --- /dev/null +++ b/core/templates/core/mp_detail.html @@ -0,0 +1,136 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}{{ mp.name }} | {{ project_name }}{% endblock %} + +{% block content %} +
+
+
+ +
+
+ +
+ +
+
+
+
{{ mp.name|slice:":1" }}
+

{{ mp.name }}

+
{{ mp.party }}
+
+ +
+ +
+ +
{{ mp.constituency }}
+
+
+ +
{{ mp.province }}
+
+
+ +
{{ trades.count }}
+
+ + +

Get notified of new disclosures

+
+
+ + +
+
+

Disclosure History

+ +
+ + + + + + + + + + + + {% for trade in trades %} + + + + + + + + {% empty %} + + + + {% endfor %} + +
TickerTypeAmountDate
+ +
{{ trade.ticker }}
+
{{ trade.company_name }}
+
+
+ {% if trade.trade_type == 'BUY' %} + BUY + {% else %} + SELL + {% endif %} + {{ trade.amount_range }}{{ trade.disclosure_date|date:"M d, Y" }} + +
No disclosures reported for this MP.
+
+
+
+
+
+ + +{% endblock %} \ No newline at end of file diff --git a/core/templates/core/mp_list.html b/core/templates/core/mp_list.html index cad5a9a..c2d26a3 100644 --- a/core/templates/core/mp_list.html +++ b/core/templates/core/mp_list.html @@ -33,7 +33,7 @@
{{ mp.constituency }}
{{ mp.province }}
- + {% empty %} @@ -67,4 +67,4 @@ font-family: 'Lexend', sans-serif; } -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/core/templates/core/partials/trade_feed.html b/core/templates/core/partials/trade_feed.html new file mode 100644 index 0000000..a66e57c --- /dev/null +++ b/core/templates/core/partials/trade_feed.html @@ -0,0 +1,58 @@ +
+ + + + + + + + + + + + + {% for trade in trades %} + + + + + + + + + {% empty %} + + + + {% endfor %} + +
MPTickerTypeAmountDate
+ +
{{ trade.mp.name|slice:":1" }}
+
+
{{ trade.mp.name }}
+
{{ trade.mp.party }}
+
+
+
+ +
{{ trade.ticker }}
+
{{ trade.company_name }}
+
+
+ {% if trade.trade_type == 'BUY' %} + BUY + {% else %} + SELL + {% endif %} + {{ trade.amount_range }}{{ trade.disclosure_date|date:"M d, Y" }} + +
No disclosures found for this filter.
+
+ + \ No newline at end of file diff --git a/core/templates/core/profile.html b/core/templates/core/profile.html new file mode 100644 index 0000000..28bb5f1 --- /dev/null +++ b/core/templates/core/profile.html @@ -0,0 +1,226 @@ +{% extends 'base.html' %} +{% load static %} + +{% block content %} +
+
+
+

Portfolio Overview

+

Tracking your followed politicians and assets.

+
+
+ + +
+
+
+
+
Avg Success Rate
+
+

{{ performance.avg_success }}%

+
+
+ High Accuracy +
+
+
+
+
+
+
+
Est. Annual ROI
+
+

+{{ performance.estimated_roi }}%

+
+
+ Based on historical signals +
+
+
+
+
+
+
+
Trades Tracked
+
+

{{ performance.total_trades }}

+
+
+ Active disclosure monitoring +
+
+
+
+
+
+
+
Portfolio Status
+
+

{{ performance.portfolio_health }}

+
+
+ Market sensitivity index +
+
+
+
+
+ + +
+
+
+
+
Market Comparison (Est. Annual ROI)
+
+
+
+ {% for item in market_comparison %} +
+
+ {{ item.name }} + {{ item.roi }}% +
+
+
+
+
+ {% endfor %} +
+
+
+
+
+ +
+ +
+
+
+
Watchlist: Politicians
+
+
+
+ + + + + + + + + + + {% for item in followed_mps %} + + + + + + + {% empty %} + + + + {% endfor %} + +
Member of ParliamentSuccess RateEst. ROIAction
+
+
+
+ {{ item.mp.name|slice:":1" }} +
+
+
+ {{ item.mp.name }} +
{{ item.mp.party }} • {{ item.trade_count }} trades
+
+
+
+
+ {{ item.success_rate }}% +
+
+
+
+
+{{ item.roi }}% + +
+ + No politicians followed yet. +
+
+
+
+
+ + +
+
+
+
Watchlist: Assets
+
+
+
    + {% for item in ticker_data %} +
  • +
    +
    + {{ item.ticker }} +
    {{ item.company_name }}
    +
    + {{ item.trade_count }} Signals + +{{ item.roi }}% Est. +
    +
    + +
    +
  • + {% empty %} +
  • + + No assets followed yet. +
  • + {% endfor %} +
+
+
+
+
+
+ + +{% endblock %} \ No newline at end of file diff --git a/core/templates/core/ticker_detail.html b/core/templates/core/ticker_detail.html new file mode 100644 index 0000000..862bde7 --- /dev/null +++ b/core/templates/core/ticker_detail.html @@ -0,0 +1,129 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}{{ ticker }} | {{ project_name }}{% endblock %} + +{% block content %} +
+
+
+ +
+
+ +
+
+
+ +
+

{{ ticker }}

+

{{ company_name }}

+
+
+
+
+ +
+
+ +
+
+
+

MPs Trading {{ ticker }}

+ +
+ + + + + + + + + + + + {% for trade in trades %} + + + + + + + + {% empty %} + + + + {% endfor %} + +
Member of ParliamentActionAmountDisclosure Date
+ +
{{ trade.mp.name|slice:":1" }}
+
+
{{ trade.mp.name }}
+
{{ trade.mp.party }}
+
+
+
+ {% if trade.trade_type == 'BUY' %} + BUY + {% else %} + SELL + {% endif %} + {{ trade.amount_range }}{{ trade.disclosure_date|date:"M d, Y" }} + View Profile +
No trades found for this ticker.
+
+
+
+
+
+ + +{% endblock %} \ No newline at end of file diff --git a/core/templates/core/ticker_list.html b/core/templates/core/ticker_list.html new file mode 100644 index 0000000..3667699 --- /dev/null +++ b/core/templates/core/ticker_list.html @@ -0,0 +1,110 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}Tickers | {{ project_name }}{% endblock %} + +{% block content %} +
+
+
+

Ticker Directory

+

Browse every asset, stock, and security traded by Canadian Members of Parliament.

+
+
+ +
+
+
+
+ + +
+
+
+ +
+
+ +
+
+ +
+ + + + + + + + + + + + {% for ticker in tickers %} + + + + + + + + {% empty %} + + + + {% endfor %} + +
AssetUnique MPsTotal TradesLast Action
+ +
{{ ticker.ticker|slice:":1" }}
+
+
{{ ticker.ticker }}
+
{{ ticker.company_name }}
+
+
+
+ {{ ticker.mp_count }} MPs + + {{ ticker.trade_count }} + + N/A + + Details +
No tickers found matching your search.
+
+
+
+ + +{% endblock %} \ No newline at end of file diff --git a/core/templates/registration/login.html b/core/templates/registration/login.html index cfdbbf1..0dd379b 100644 --- a/core/templates/registration/login.html +++ b/core/templates/registration/login.html @@ -31,7 +31,7 @@
-

Don't have an account? Contact Admin

+

Don't have an account? Create one

@@ -48,4 +48,4 @@ color: white; } -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/core/templates/registration/signup.html b/core/templates/registration/signup.html new file mode 100644 index 0000000..dcb1c3a --- /dev/null +++ b/core/templates/registration/signup.html @@ -0,0 +1,59 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}Sign Up | {{ project_name }}{% endblock %} + +{% block content %} +
+
+
+
+
+

Create Account

+

Join the tracker to follow MPs and assets.

+
+ +
+ {% csrf_token %} + {% for field in form %} +
+ + {{ field.errors }} + + {% if field.help_text %} +
{{ field.help_text|safe }}
+ {% endif %} +
+ {% endfor %} + + +
+ +
+

Already have an account? Log in

+
+
+
+
+
+ + +{% endblock %} \ No newline at end of file diff --git a/core/urls.py b/core/urls.py index f740326..3bf47ad 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,8 +1,14 @@ from django.urls import path, include -from .views import home, mp_list +from .views import home, mp_list, mp_detail, ticker_list, ticker_detail, toggle_follow, signup, profile urlpatterns = [ path("", home, name="home"), path("mps/", mp_list, name="mp_list"), + path("mp//", mp_detail, name="mp_detail"), + path("tickers/", ticker_list, name="ticker_list"), + path("ticker//", ticker_detail, name="ticker_detail"), + path("toggle-follow/", toggle_follow, name="toggle_follow"), + path("signup/", signup, name="signup"), + path("profile/", profile, name="profile"), path("accounts/", include("django.contrib.auth.urls")), ] diff --git a/core/views.py b/core/views.py index 46b7a0a..5efd032 100644 --- a/core/views.py +++ b/core/views.py @@ -1,9 +1,15 @@ import os import platform +import random from datetime import date, timedelta -from django.shortcuts import render +from django.shortcuts import render, get_object_or_404, redirect from django.utils import timezone -from .models import MemberOfParliament, TradeDisclosure +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.""" @@ -38,17 +44,40 @@ def home(request): disclosure_date=date.today() - timedelta(days=10) ) - trades = TradeDisclosure.objects.select_related('mp').order_by('-disclosure_date')[: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): @@ -58,4 +87,174 @@ def mp_list(request): "project_name": "Canada MP Trade Tracker", "mps": mps, } - return render(request, "core/mp_list.html", context) \ No newline at end of file + 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) \ No newline at end of file