Skip to content

Commit

Permalink
Merge pull request #910 from fecgov/release/sprint-43
Browse files Browse the repository at this point in the history
Release/sprint 43
  • Loading branch information
dheitzer authored Jun 18, 2024
2 parents 3a42a8b + ef9e017 commit b7a915a
Show file tree
Hide file tree
Showing 32 changed files with 352 additions and 125 deletions.
4 changes: 2 additions & 2 deletions bin/run-api.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cd django-backend

# Run migrations and application
./manage.py migrate --no-input --traceback --verbosity 3 > migrate.out &&
./manage.py migrate --no-input --traceback --verbosity 3 &&
python manage.py create_committee_views &&
gunicorn --bind 0.0.0.0:8080 fecfiler.wsgi -w 9
exec gunicorn --bind 0.0.0.0:8080 fecfiler.wsgi -w 9
12 changes: 12 additions & 0 deletions django-backend/fecfiler/committee_accounts/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ def test_register_committee_mismatch_email(self):
user=self.other_user,
)

def test_register_committee_case_insensitive(self):
self.test_user.email = self.test_user.email.upper()
account = register_committee("C12345678", self.test_user)
self.assertEquals(account.committee_id, "C12345678")
self.assertRaisesMessage(
Exception,
self.register_error_message,
register_committee,
committee_id="C12345678",
user=self.test_user,
)


class CommitteeMemberViewSetTest(TestCase):
fixtures = ["C01234567_user_and_committee", "unaffiliated_users"]
Expand Down
26 changes: 18 additions & 8 deletions django-backend/fecfiler/committee_accounts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,13 @@ class CommitteeMemberListPagination(pagination.PageNumberPagination):
page_size_query_param = "page_size"


class CommitteePagination(pagination.PageNumberPagination):
page_size = 100


class CommitteeViewSet(viewsets.GenericViewSet, mixins.ListModelMixin):
serializer_class = CommitteeAccountSerializer
pagination_class = CommitteePagination

def get_queryset(self):
user = self.request.user
Expand Down Expand Up @@ -221,15 +226,20 @@ def register_committee(committee_id, user):

failure_reason = None

if ";" in f1_email:
f1_email = f1_email.split(";")
elif "," in f1_email:
f1_email = f1_email.split(",")
if not f1_email:
failure_reason = "No email provided in F1"
else:
f1_email = [f1_email]

if email not in f1_email:
failure_reason = f"Email {email} does not match committee email"
f1_email_lowercase = f1_email.lower()
f1_emails = []
if ";" in f1_email_lowercase:
f1_emails = f1_email_lowercase.split(";")
elif "," in f1_email_lowercase:
f1_emails = f1_email_lowercase.split(",")
else:
f1_emails = [f1_email_lowercase]

if email.lower() not in f1_emails:
failure_reason = f"Email {email} does not match committee email"

existing_account = CommitteeAccount.objects.filter(committee_id=committee_id).first()
if existing_account:
Expand Down
7 changes: 6 additions & 1 deletion django-backend/fecfiler/reports/form_3x/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from .serializers import Form3XSerializer
import structlog
from rest_framework.response import Response
from fecfiler.reports.report_code_label import report_code_label_mapping

logger = structlog.get_logger(__name__)

Expand Down Expand Up @@ -36,9 +37,13 @@ def coverage_dates(self, request):
)
return JsonResponse(data, safe=False)

@action(detail=False)
def report_code_map(self, request):
return JsonResponse(report_code_label_mapping, safe=False)

@action(detail=False, methods=["get"], url_path=r"future")
def future_form3x_reports(self, request):
json_date_string = request.GET.get('after', '')
json_date_string = request.GET.get("after", "")
data = list(
self.get_queryset().filter(coverage_through_date__gt=json_date_string)
)
Expand Down
4 changes: 4 additions & 0 deletions django-backend/fecfiler/reports/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ def delete(self):
"form_1m": "F1M",
}

FORMS_TO_CALCULATE = [
"F3X",
]


def update_recalculation(report: Report):
if report:
Expand Down
47 changes: 47 additions & 0 deletions django-backend/fecfiler/reports/report_code_label.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from django.db.models import Case, When, Value
from fecfiler.reports.models import Report

report_code_label_mapping = {
"Q1": "APRIL 15 QUARTERLY REPORT (Q1)",
"Q2": "JULY 15 QUARTERLY REPORT (Q2)",
"Q3": "OCTOBER 15 QUARTERLY REPORT (Q3)",
"YE": "JANUARY 31 YEAR-END (YE)",
"TER": "TERMINATION REPORT (TER)",
"MY": "JULY 31 MID-YEAR REPORT (MY)",
"12G": "12-DAY PRE-GENERAL (12G)",
"12P": "12-DAY PRE-PRIMARY (12P)",
"12R": "12-DAY PRE-RUNOFF (12R)",
"12S": "12-DAY PRE-SPECIAL (12S)",
"12C": "12-DAY PRE-CONVENTION (12C)",
"30G": "30-DAY POST-GENERAL (30G)",
"30R": "30-DAY POST-RUNOFF (30R)",
"30S": "30-DAY POST-SPECIAL (30S)",
"M2": "FEBRUARY 20 MONTHLY REPORT (M2)",
"M3": "MARCH 20 MONTHLY REPORT (M3)",
"M4": "APRIL 20 MONTHLY REPORT (M4)",
"M5": "MAY 20 MONTHLY REPORT (M5)",
"M6": "JUNE 20 MONTHLY REPORT (M6)",
"M7": "JULY 20 MONTHLY REPORT (M7)",
"M8": "AUGUST 20 MONTHLY REPORT (M8)",
"M9": "SEPTEMBER 20 MONTHLY REPORT (M9)",
"M10": "OCTOBER 20 MONTHLY REPORT (M10)",
"M11": "NOVEMBER 20 MONTHLY REPORT (M11)",
"M12": "DECEMBER 20 MONTHLY REPORT (M12)",
}

# Generate the Case object
report_code_label_case = Case(
*[When(report_code=k, then=Value(v)) for k, v in report_code_label_mapping.items()],
When(form_24__report_type_24_48=24, then=Value("24 HOUR")),
When(form_24__report_type_24_48=48, then=Value("48 HOUR")),
When(form_99__isnull=False, then=Value("")),
When(form_1m__isnull=False, then=Value("")),
)


def get_report_code_label(report: Report):
if report.form_3x:
return report_code_label_mapping[report.report_code]
if report.form_24:
return f"{report.form_24.report_type_24_48} HOUR"
return ""
2 changes: 1 addition & 1 deletion django-backend/fecfiler/reports/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def test_amending_f24(self):
self.assertEquals(f24_report.form_type, "F24A")

def test_delete(self):
f24_report = create_form24(self.committee, "2024-01-01", "2024-02-01", {})
f24_report = create_form24(self.committee, {})
f24_report_id = f24_report.id
f24_id = f24_report.form_24.id
f3x_report = create_form3x(self.committee, "2024-01-01", "2024-02-01", {})
Expand Down
16 changes: 12 additions & 4 deletions django-backend/fecfiler/reports/tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,24 @@
from fecfiler.reports.form_99.models import Form99


def create_form3x(committee, coverage_from, coverage_through, data):
def create_form3x(committee, coverage_from, coverage_through, data={}):
return create_test_report(Form3X, committee, coverage_from, coverage_through, data)


def create_form24(committee, coverage_from, coverage_through, data):
return create_test_report(Form24, committee, coverage_from, coverage_through, data)
def create_form24(committee, data={}):
return create_test_report(Form24, committee, data=data)


def create_form99(committee, data={}):
return create_test_report(Form99, committee, data=data)


def create_form1m(committee, data={}):
return create_test_report(Form1M, committee, data=data)


def create_test_report(
form, committee, coverage_from=None, coverage_through=None, data=None
form, committee, coverage_from=None, coverage_through=None, data={}
):
form_object = create_form(form, data)
report = Report.objects.create(
Expand Down
31 changes: 2 additions & 29 deletions django-backend/fecfiler/reports/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from fecfiler.committee_accounts.views import CommitteeOwnedViewMixin
from .models import Report
from .report_code_label import report_code_label_case
from fecfiler.transactions.models import Transaction
from fecfiler.memo_text.models import MemoText
from fecfiler.web_services.models import DotFEC, UploadSubmission, WebPrintSubmission
Expand All @@ -14,34 +15,6 @@

logger = structlog.get_logger(__name__)

report_code_label_mapping = Case(
When(report_code="Q1", then=Value("APRIL 15 (Q1)")),
When(report_code="Q2", then=Value("JULY 15 (Q2)")),
When(report_code="Q3", then=Value("OCTOBER 15 (Q3)")),
When(report_code="YE", then=Value("JANUARY 31 (YE)")),
When(report_code="TER", then=Value("TERMINATION (TER)")),
When(report_code="MY", then=Value("JULY 31 (MY)")),
When(report_code="12G", then=Value("GENERAL (12G)")),
When(report_code="12P", then=Value("PRIMARY (12P)")),
When(report_code="12R", then=Value("RUNOFF (12R)")),
When(report_code="12S", then=Value("SPECIAL (12S)")),
When(report_code="12C", then=Value("CONVENTION (12C)")),
When(report_code="30G", then=Value("GENERAL (30G)")),
When(report_code="30R", then=Value("RUNOFF (30R)")),
When(report_code="30S", then=Value("SPECIAL (30S)")),
When(report_code="M2", then=Value("FEBRUARY 20 (M2)")),
When(report_code="M3", then=Value("MARCH 30 (M3)")),
When(report_code="M4", then=Value("APRIL 20 (M4)")),
When(report_code="M5", then=Value("MAY 20 (M5)")),
When(report_code="M6", then=Value("JUNE 20 (M6)")),
When(report_code="M7", then=Value("JULY 20 (M7)")),
When(report_code="M8", then=Value("AUGUST 20 (M8)")),
When(report_code="M9", then=Value("SEPTEMBER 20 (M9)")),
When(report_code="M10", then=Value("OCTOBER 20 (M10)")),
When(report_code="M11", then=Value("NOVEMBER 20 (M11)")),
When(report_code="M12", then=Value("DECEMBER 20 (M12)")),
)


version_labels = {
"F3XN": "Original",
Expand Down Expand Up @@ -73,7 +46,7 @@ class ReportViewSet(CommitteeOwnedViewMixin, ModelViewSet):
whens = [When(form_type=k, then=Value(v)) for k, v in version_labels.items()]

queryset = (
Report.objects.annotate(report_code_label=report_code_label_mapping)
Report.objects.annotate(report_code_label=report_code_label_case)
# alias fields used by the version_label annotation only. not part of payload
.alias(
form_type_label=Case(
Expand Down
13 changes: 10 additions & 3 deletions django-backend/fecfiler/transactions/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
from decimal import Decimal
from enum import Enum
from .schedule_b.managers import refunds as schedule_b_refunds
from ..reports.models import Report
from fecfiler.reports.report_code_label import report_code_label_case

"""Manager to deterimine fields that are used the same way across transactions,
but are called different names"""
Expand Down Expand Up @@ -330,6 +332,12 @@ def transaction_view(self):

class TransactionViewManager(Manager):
def get_queryset(self):
REPORT_CODE_LABEL_CLAUSE = Subquery( # noqa: N806
Report.objects.filter(transactions=OuterRef("pk"))
.annotate(report_code_label=report_code_label_case)
.values("report_code_label")[:1]
)

return (
super()
.get_queryset()
Expand Down Expand Up @@ -376,6 +384,7 @@ def get_queryset(self):
"_calendar_ytd_per_election_office",
),
line_label=self.LINE_LABEL_CLAUSE(),
report_code_label=REPORT_CODE_LABEL_CLAUSE,
)
.alias(order_key=self.ORDER_KEY_CLAUSE())
.order_by("order_key")
Expand Down Expand Up @@ -404,9 +413,7 @@ def ORDER_KEY_CLAUSE(self): # noqa: N802
output_field=TextField(),
),
),
default=Concat(
"schedule", "_form_type", "created", output_field=TextField()
),
default=Concat("schedule", "_form_type", "created", output_field=TextField()),
output_field=TextField(),
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.11 on 2024-06-04 14:02

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("transactions", "0006_independent_expenditure_memos_no_aggregation_group"),
]

operations = [
migrations.AddField(
model_name="schedulee",
name="so_candidate_state",
field=models.TextField(blank=True, null=True),
),
]
Original file line number Diff line number Diff line change
@@ -1 +1 @@
line_labels = {"SC/10": "10", "SC/9": "9"}
line_labels = {"SC/10": "10", "SC/9": "09"}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
line_labels = {"SD9": "9", "SD10": "10"}
line_labels = {"SD9": "09", "SD10": "10"}
1 change: 1 addition & 0 deletions django-backend/fecfiler/transactions/schedule_e/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class ScheduleE(models.Model):
completing_suffix = models.TextField(null=True, blank=True)
date_signed = models.DateField(null=True, blank=True)
memo_text_description = models.TextField(null=True, blank=True)
so_candidate_state = models.TextField(null=True, blank=True)

def get_transaction(self):
return self.transaction_set.first()
Expand Down
12 changes: 12 additions & 0 deletions django-backend/fecfiler/transactions/schedule_e/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ def create(self, validated_data):
model_data = get_model_data(validated_data, ScheduleE)
return ScheduleE.objects.create(**model_data)

def to_internal_value(self, data):
# We only save the candidate state in the schedule e table for
# presidential candidates that are in a primary election.
# Otherwise, the candidate state is located in the contact_2 record.
validated_data = super().to_internal_value(data)
if not (
validated_data.get('election_code').startswith('P')
and validated_data.get('so_candidate_office') == 'P'
):
validated_data['so_candidate_state'] = None
return validated_data

class Meta:
fields = [
f.name
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,39 @@
from django.test import TestCase
from fecfiler.transactions.schedule_e.utils import add_schedule_e_contact_fields
from fecfiler.transactions.tests.test_utils import get_test_transaction
from fecfiler.transactions.schedule_e.models import ScheduleE


class ScheduleEUtilsTestCase(TestCase):

def test_contacts_to_representation(self):

instance = get_test_transaction('INDEPENDENT_EXPENDITURE')
instance = get_test_transaction("INDEPENDENT_EXPENDITURE")

representation = dict(
transaction_type_identifier='INDEPENDENT_EXPENDITURE'
representation = dict(transaction_type_identifier="INDEPENDENT_EXPENDITURE")

add_schedule_e_contact_fields(instance, representation)

self.assertEquals(
representation["transaction_type_identifier"], "INDEPENDENT_EXPENDITURE"
)
self.assertEquals(representation["payee_last_name"], "1 last name")
self.assertEquals(representation["so_candidate_last_name"], "2 last name")
self.assertEquals(representation["so_candidate_state"], "2 candidate state")

def test_contacts_to_representation_primary_presidential(self):

instance = get_test_transaction("INDEPENDENT_EXPENDITURE")
instance.schedule_e = ScheduleE(election_code="P2024", so_candidate_state="CA")
instance.contact_2.candidate_office = "P"

representation = dict(transaction_type_identifier="INDEPENDENT_EXPENDITURE")

add_schedule_e_contact_fields(instance, representation)

self.assertEquals(
representation['transaction_type_identifier'], 'INDEPENDENT_EXPENDITURE'
representation["transaction_type_identifier"], "INDEPENDENT_EXPENDITURE"
)
self.assertEquals(representation['payee_last_name'], '1 last name')
self.assertEquals(representation['so_candidate_last_name'], '2 last name')
self.assertEquals(representation["payee_last_name"], "1 last name")
self.assertEquals(representation["so_candidate_last_name"], "2 last name")
self.assertEquals(representation["so_candidate_state"], "CA")
Loading

0 comments on commit b7a915a

Please sign in to comment.