Skip to content
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

Model and Visualizations for Campaign Finance Data #5

Merged
merged 17 commits into from
Apr 17, 2024
Merged
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
45 changes: 43 additions & 2 deletions backend/app/api_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@

from .models import (
TCPDElection,
SeatShare
SeatShare,
CampaignFinance,
)

from .serializers import (
TCPDElectionSerializer,
SeatShareSerializer
SeatShareSerializer,
CampaignFinanceSerializer,
)


Expand All @@ -37,6 +39,7 @@ def all_seats(request):
serializer = SeatShareSerializer(seat_shares, many=True)
return Response(serializer.data)


@api_view(['GET'])
def get_SDE_DATA_IN_F7DSTRBND_1991(request, feature_limit=10):
"""
Expand All @@ -50,3 +53,41 @@ def get_SDE_DATA_IN_F7DSTRBND_1991(request, feature_limit=10):
"type": geojson["type"],
"features": geojson["features"][:num_features]
})


@api_view(['GET'])
def campaign_finance(request, donor_name=None, party_name=None):
"""
API endpoint to get all campaign finance donations made by donors to parties, or a specified
pair of donor-party donations
"""
if donor_name is not None and party_name is not None:
campaign_finances = CampaignFinance.objects.filter(
donor_name=donor_name, party_name=party_name
)
else:
campaign_finances = CampaignFinance.objects.all()
serializer = CampaignFinanceSerializer(campaign_finances, many=True)
return Response(serializer.data)


@api_view(['GET'])
def campaign_finance_party_subset(request, party_name):
"""
API endpoint to get campaign finance donations made by all donors to
a specified party
"""
campaign_finances = CampaignFinance.objects.filter(party_name=party_name)
serializer = CampaignFinanceSerializer(campaign_finances, many=True)
return Response(serializer.data)


@api_view(['GET'])
def campaign_finance_donor_subset(request, donor_name):
"""
API endpoint to get campaign finance donations made to all parties by
a specified donor
"""
campaign_finances = CampaignFinance.objects.filter(donor_name=donor_name)
serializer = CampaignFinanceSerializer(campaign_finances, many=True)
return Response(serializer.data)
18,644 changes: 18,644 additions & 0 deletions backend/app/data/campaignfinance/Cleaned_Matched_Values.csv

Large diffs are not rendered by default.

Binary file not shown.
47 changes: 47 additions & 0 deletions backend/app/management/commands/load_campaignfinance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""
Django management command load_campaignfinance

Updates local db with values from base csv dataset
"""
import os
import pandas

from django.conf import settings
from django.core.management.base import BaseCommand
from app.models import CampaignFinance


class Command(BaseCommand):
"""
Custom django-admin command used to run an analysis from the app/analysis folder
"""
help = ''

def add_arguments(self, parser):
parser.add_argument(
'dataset_name',
type=str,
action='store',
help='Name of dataset in app/data folder (with extension)',
)

def handle(self, *args, **options):
# pylint: disable=too-many-locals
file_name = options.get('dataset_name')
file_path = os.path.join(settings.DATASET_DIR, "campaignfinance", file_name)
df = pandas.read_csv(file_path)

# TODO: Generalize this to update correct model(s) and columns based on dataset
for full_bond_number, amount, donor_name, party_name in zip(
df["FullBondNumber"],
df["Amount"],
df["Donor"],
df["Party"]
):
campaignfinance = CampaignFinance(
full_bond_number=full_bond_number,
amount=amount,
donor_name=donor_name,
party_name=party_name,
)
campaignfinance.save()
23 changes: 23 additions & 0 deletions backend/app/migrations/0003_campaignfinance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 5.0.2 on 2024-04-06 16:52

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('app', '0002_seatshare'),
]

operations = [
migrations.CreateModel(
name='CampaignFinance',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('full_bond_number', models.CharField(max_length=7)),
('amount', models.IntegerField()),
('donor_name', models.CharField(max_length=61)),
('party_name', models.CharField(max_length=37)),
],
),
]
16 changes: 16 additions & 0 deletions backend/app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,19 @@ class SeatShare(models.Model):
seats_held = models.IntegerField()
election_year = models.IntegerField()
total_seats = models.IntegerField()


class CampaignFinance(models.Model):
"""
Represents a donation amount made by a donor to a party
"""
full_bond_number = models.CharField(
max_length=7,
)
amount = models.IntegerField()
donor_name = models.CharField(
max_length=61,
)
party_name = models.CharField(
max_length=37,
)
17 changes: 16 additions & 1 deletion backend/app/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
from rest_framework import serializers
from .models import (
TCPDElection,
SeatShare
SeatShare,
CampaignFinance,
)


Expand Down Expand Up @@ -43,3 +44,17 @@ class Meta:
"seats_held",
"total_seats"
]


class CampaignFinanceSerializer(serializers.ModelSerializer):
"""
Serializes campaign finance donations
"""
class Meta:
model = CampaignFinance
fields = [
"donor_name",
"party_name",
"amount",
"full_bond_number"
]
42 changes: 42 additions & 0 deletions backend/app/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,45 @@ def example(request, example_id=None):
'component_name': 'ExampleId'
}
return render(request, 'index.html', context)


def PieChart(request, example_id=None):
"""
Displays PieChart for data from election campaign finance
"""

context = {
'page_metadata': {
'title': 'PieChart Visualization '
},
'component_name': 'PieChart'
}
return render(request, 'index.html', context)


def BarChart(request, example_id=None):
"""
Displays Bar Chart for data from election campaign finance
"""

context = {
'page_metadata': {
'title': 'Bar Chart Visualization'
},
'component_name': 'BarChart'
}
return render(request, 'index.html', context)


def FinanceSankey(request, example_id=None):
"""
Displays FinanceSankey for data from election campaign finance
"""

context = {
'page_metadata': {
'title': 'FinanceSankey Visualization'
},
'component_name': 'FinanceSankey'
}
return render(request, 'index.html', context)
20 changes: 18 additions & 2 deletions backend/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,27 @@
path('', views.index),
path('example/', views.example),
path('example/<example_id>', views.example),
path('campaign-finance/top-10-donors-piechart/', views.PieChart),
path('campaign-finance/top-10-donors-barchart/', views.BarChart),
path('campaign-finance/donor-party-sankey/', views.FinanceSankey),




################################################################################
# API endpoints
################################################################################
path('api/1951-1962elections/', api_views.all_elections),
path('api/1962-2019seats/', api_views.all_seats),
path("api/SDE_DATA_IN_F7DSTRBND_1991/<int:feature_limit>", api_views.get_SDE_DATA_IN_F7DSTRBND_1991),
]
path(
"api/SDE_DATA_IN_F7DSTRBND_1991/<int:feature_limit>",
api_views.get_SDE_DATA_IN_F7DSTRBND_1991
),
path("api/all-campaign-finance/", api_views.campaign_finance),
path(
"api/campaign-finance/party-donor-pair/<party_name>/<donor_name>",
api_views.campaign_finance
),
path("api/campaign-finance/all-donors/<party_name>", api_views.campaign_finance_party_subset),
path("api/campaign-finance/all-parties/<donor_name>", api_views.campaign_finance_donor_subset),
]
102 changes: 102 additions & 0 deletions frontend/components/BarChart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React, {useState, useEffect} from "react";
import Chart from "chart.js/auto";


const BarChart = () => {
const [data, setData] = useState([]);


useEffect(() => {
async function fetchData() {
try {
const response = await fetch("/api/all-campaign-finance/");
const jsonData = await response.json();
console.log("Fetched data:", jsonData); // Log fetched data to the console
setData(jsonData);
} catch (error) {
console.error("Error fetching data:", error);
}
}
fetchData();
}, []);

useEffect(() => {
if (data.length > 0) {
renderChart();
}
}, [data]);

const renderChart = () => {
const donors = {};
data.forEach(item => {
let amountInCrores = item.amount / 10000000; // Convert rupees to crores
if (!donors[item.donor_name]) {
donors[item.donor_name] = amountInCrores;
} else {
donors[item.donor_name] += amountInCrores;
}
});

// Sort donors by donation amount in descending order
const sortedDonors = Object.entries(donors)
.sort(([, a], [, b]) => b - a)
.slice(0, 10); // Select top 10 donors

const xValues = sortedDonors.map(([donor]) => donor);
const yValues = sortedDonors.map(([, amount]) => amount);

const ctx = document.getElementById("myChart").getContext("2d");
new Chart(ctx, {
type: "bar",
data: {
labels: xValues,
datasets: [
{
label: "Total Donation Amount",
data: yValues,
backgroundColor: "rgba(0, 123, 255, 0.6)", // Blue color
borderColor: "rgba(0, 123, 255, 1)", // Border color
borderWidth: 1
}
]
},
options: {
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: "Total Donation Amount (crores)",
font: {size: 25}// Set the font size for the y-axis title
},
ticks: {
callback: function(value) {
return "₹" + value.toLocaleString(); // Format as currency
},
font: {size: 16}
}
},
x: {
title: {
display: true,
text: "Donor Name",
font: {size: 25}
},
ticks: {
font: {size: 16}

}
}
}
}
});
};

return (
<div className="plot-figure">
<h1>Top 10 Donors by Donation Amount</h1>
<canvas id="myChart" width="400" height="200"></canvas>
</div>
);
};
export default BarChart;
Loading
Loading