Skip to content

Feature/allocation model get information tests #707

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
3 changes: 2 additions & 1 deletion coldfront/core/allocation/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from ast import literal_eval
from enum import Enum

from django.conf import settings
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.db import models
Expand Down Expand Up @@ -155,7 +156,7 @@ def get_information(self):

html_string = ""
for attribute in self.allocationattribute_set.all():
if attribute.allocation_attribute_type.name in ALLOCATION_ATTRIBUTE_VIEW_LIST:
if attribute.allocation_attribute_type.name in settings.ALLOCATION_ATTRIBUTE_VIEW_LIST:
html_string += "%s: %s <br>" % (attribute.allocation_attribute_type.name, attribute.value)

if hasattr(attribute, "allocationattributeusage"):
Expand Down
289 changes: 288 additions & 1 deletion coldfront/core/allocation/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@
import datetime

from django.core.exceptions import ValidationError
from django.test import TestCase
from django.test import TestCase, override_settings
from django.utils import timezone
from django.utils.html import format_html
from django.utils.safestring import SafeString

from coldfront.core.allocation.models import (
Allocation,
AllocationAttribute,
AllocationStatusChoice,
)
from coldfront.core.project.models import Project
from coldfront.core.test_helpers.factories import (
AllocationAttributeFactory,
AllocationAttributeTypeFactory,
AllocationAttributeUsageFactory,
AllocationFactory,
AllocationStatusChoiceFactory,
ProjectFactory,
Expand Down Expand Up @@ -140,3 +146,284 @@ def test_status_is_active_and_start_date_equals_end_date_no_error(self):
status=self.active_status, start_date=start_and_end_date, end_date=start_and_end_date, project=self.project
)
actual_allocation.full_clean()


class AllocationModelGetInformationTests(TestCase):
path_to_allocation_models_allocation_attribute_view_list: str = (
"coldfront.core.allocation.models.ALLOCATION_ATTRIBUTE_VIEW_LIST"
)

attribute_usage_formatter_template: str = "{}: {}/{} ({} %) <br>"
attribute_view_list_formatter_template: str = "{}: {} <br>"

name_in_view_list_1 = "Something 1"
name_in_view_list_2 = "Something Else"
name_not_in_view_list_1 = "This is another thing "
name_not_in_view_list_2 = "This Is Not In List"
sample_allocation_attribute_view_list = [name_in_view_list_1, name_in_view_list_2]

def find_percent(self, numerator, denominator):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add a comment to explain the math here without having to dig into what is happening in models.py, especially the multiplication by 10000

return round(float(numerator) / float(denominator) * 10000) / 100

def test_no_allocation_attributes_returns_empty_string(self):
"""Test that the get_information method returns an empty string if there are no allocation attributes."""
allocation: Allocation = AllocationFactory()
self.assertEqual(allocation.get_information, "")

@override_settings(ALLOCATION_ATTRIBUTE_VIEW_LIST=sample_allocation_attribute_view_list)
def test_attribute_type_not_in_view_list_returns_without_type_substring_included(self):
"""Test that the get_information method returns a string without the attribute type substring when the type name is not in ALLOCATION_ATTRIBUTE_VIEW_LIST."""
allocation: Allocation = AllocationFactory()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we we move this allocation factory call to a test setup function if every test in this class runs the same setup step

allocation_attribute_type = AllocationAttributeTypeFactory(name="Not a name in the view list")
allocation_attribute = AllocationAttributeFactory(
allocation_attribute_type=allocation_attribute_type, allocation=allocation
)
allocation_attribute_usage = AllocationAttributeUsageFactory(allocation_attribute=allocation_attribute)

expected_percent = self.find_percent(allocation_attribute_usage.value, allocation_attribute.value)

expected_information: SafeString = format_html(
self.attribute_usage_formatter_template,
allocation_attribute_type.name,
float(allocation_attribute_usage.value),
allocation_attribute.value,
expected_percent,
)

self.assertHTMLEqual(allocation.get_information, expected_information)

@override_settings(ALLOCATION_ATTRIBUTE_VIEW_LIST=sample_allocation_attribute_view_list)
def test_attribute_type_in_view_list_returns_with_type_substring_included(self):
"""Test that the get_information method returns a string with the attribute type substring when the type name is in ALLOCATION_ATTRIBUTE_VIEW_LIST."""
allocation: Allocation = AllocationFactory()
allocation_attribute_type = AllocationAttributeTypeFactory(name=self.name_in_view_list_1)
allocation_attribute = AllocationAttributeFactory(
allocation_attribute_type=allocation_attribute_type, allocation=allocation
)
allocation_attribute_usage = AllocationAttributeUsageFactory(allocation_attribute=allocation_attribute)

expected_percent = self.find_percent(allocation_attribute_usage.value, allocation_attribute.value)

regular_substring: SafeString = format_html(
self.attribute_usage_formatter_template,
allocation_attribute_type.name,
float(allocation_attribute_usage.value),
allocation_attribute.value,
expected_percent,
)

view_list_substring: SafeString = format_html(
self.attribute_view_list_formatter_template, allocation_attribute_type.name, allocation_attribute.value
)

expected_information: SafeString = view_list_substring + regular_substring

self.assertHTMLEqual(allocation.get_information, expected_information)

def test_attribute_value_is_zero_returns_100_percent_string(self):
allocation: Allocation = AllocationFactory()
allocation_attribute: AllocationAttribute = AllocationAttributeFactory(allocation=allocation, value=0)
allocation_attribute_usage = AllocationAttributeUsageFactory(
allocation_attribute=allocation_attribute, value=10
)

allocation_attribute_type_name: str = allocation_attribute.allocation_attribute_type.name
allocation_attribute_usage_value: float = float(allocation_attribute_usage.value)
allocation_attribute_value: str = allocation_attribute.value
expected_percent = 100

expected_information: SafeString = format_html(
self.attribute_usage_formatter_template,
allocation_attribute_type_name,
allocation_attribute_usage_value,
allocation_attribute_value,
expected_percent,
)

self.assertHTMLEqual(allocation.get_information, expected_information)

def test_multiple_attributes_with_same_type_returns_combined_information(self):
"""Test that the get_information method returns combined information for multiple attributes."""
allocation: Allocation = AllocationFactory()
allocation_attribute_type = AllocationAttributeTypeFactory()

allocation_attribute_1: AllocationAttribute = AllocationAttributeFactory(
allocation=allocation, allocation_attribute_type=allocation_attribute_type, value=100
)
allocation_attribute_2: AllocationAttribute = AllocationAttributeFactory(
allocation=allocation, allocation_attribute_type=allocation_attribute_type, value=1000
)
allocation_attribute_usage_1 = AllocationAttributeUsageFactory(
allocation_attribute=allocation_attribute_1, value=50
)
allocation_attribute_usage_2 = AllocationAttributeUsageFactory(
allocation_attribute=allocation_attribute_2, value=500
)

percent_1 = self.find_percent(allocation_attribute_usage_1.value, allocation_attribute_1.value)
percent_2 = self.find_percent(allocation_attribute_usage_2.value, allocation_attribute_2.value)

expected_information: SafeString = format_html(
"{}: {}/{} ({} %) <br>{}: {}/{} ({} %) <br>",
allocation_attribute_type.name,
float(allocation_attribute_usage_1.value),
allocation_attribute_1.value,
percent_1,
allocation_attribute_type.name,
float(allocation_attribute_usage_2.value),
allocation_attribute_2.value,
percent_2,
)

self.assertHTMLEqual(allocation.get_information, expected_information)

@override_settings(ALLOCATION_ATTRIBUTE_VIEW_LIST=sample_allocation_attribute_view_list)
def test_attributes_with_names_in_view_list_included_in_information(self):
"""Test that only allocations with AttributeTypes whose names are in ALLOCATION_ATTRIBUTE_VIEW_LIST
have their special snippets also included in the information"""

# AllocationAttributeTypes whose names are inside of ALLOCATION_ATTRIBUTE_VIEW_LIST
attribute_type_in_view_list_1 = AllocationAttributeTypeFactory(name=self.name_in_view_list_1)
attribute_type_in_view_list_2 = AllocationAttributeTypeFactory(name=self.name_in_view_list_2)

# AllocationAttributeTypes whose names are NOT inside of ALLOCATION_ATTRIBUTE_VIEW_LIST
attribute_type_not_in_view_list_1 = AllocationAttributeTypeFactory(name=self.name_not_in_view_list_1)
attribute_type_not_in_view_list_2 = AllocationAttributeTypeFactory(name=self.name_not_in_view_list_2)

# Allocation that we are testing, needed for creating AllocationAttributes
tested_allocation: Allocation = AllocationFactory()

# AllocationAttribute for each AllocationAttributeType
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have concerns about how maintainable this test will be, I think maybe adding some comments to provide more context for how you're constructing the test example might make it a little easier to parse and understand.


view_list_allocation_attribute_value_1 = 2000
view_list_allocation_attribute_1 = AllocationAttributeFactory(
allocation_attribute_type=attribute_type_in_view_list_1,
value=view_list_allocation_attribute_value_1,
allocation=tested_allocation,
)

view_list_allocation_attribute_value_2 = 5000
view_list_allocation_attribute_2 = AllocationAttributeFactory(
allocation_attribute_type=attribute_type_in_view_list_2,
value=view_list_allocation_attribute_value_2,
allocation=tested_allocation,
)

no_view_list_allocation_attribute_value_1 = 20
no_view_list_allocation_attribute_1 = AllocationAttributeFactory(
allocation_attribute_type=attribute_type_not_in_view_list_1,
value=no_view_list_allocation_attribute_value_1,
allocation=tested_allocation,
)

no_view_list_allocation_attribute_value_2 = 80000
no_view_list_allocation_attribute_2 = AllocationAttributeFactory(
allocation_attribute_type=attribute_type_not_in_view_list_2,
value=no_view_list_allocation_attribute_value_2,
allocation=tested_allocation,
)

# AllocationAttributeUsage for each AllocationAttribute
view_list_allocation_attribute_usage_value_1 = 4
view_list_allocation_attribute_usage_1 = AllocationAttributeUsageFactory( # noqa: F841
allocation_attribute=view_list_allocation_attribute_1,
value=view_list_allocation_attribute_usage_value_1,
)

view_list_allocation_attribute_usage_value_2 = 200
view_list_allocation_attribute_usage_2 = AllocationAttributeUsageFactory( # noqa: F841
allocation_attribute=view_list_allocation_attribute_2,
value=view_list_allocation_attribute_usage_value_2,
)

no_view_list_allocation_attribute_usage_value_1 = 5
no_view_list_allocation_attribute_usage_1 = AllocationAttributeUsageFactory( # noqa: F841
allocation_attribute=no_view_list_allocation_attribute_1,
value=no_view_list_allocation_attribute_usage_value_1,
)

no_view_list_allocation_attribute_usage_value_2 = 5
no_view_list_allocation_attribute_usage_2 = AllocationAttributeUsageFactory( # noqa: F841
allocation_attribute=no_view_list_allocation_attribute_2,
value=no_view_list_allocation_attribute_usage_value_2,
)

view_list_percent_1 = self.find_percent(
view_list_allocation_attribute_usage_value_1, view_list_allocation_attribute_value_1
)
view_list_percent_2 = self.find_percent(
view_list_allocation_attribute_usage_value_2, view_list_allocation_attribute_value_2
)
no_view_list_percent_1 = self.find_percent(
no_view_list_allocation_attribute_usage_value_1, no_view_list_allocation_attribute_value_1
)
no_view_list_percent_2 = self.find_percent(
no_view_list_allocation_attribute_usage_value_2, no_view_list_allocation_attribute_value_2
)

# Build up the substrings that make up the expected information string

usage_string_from_view_1: SafeString = format_html(
self.attribute_usage_formatter_template,
self.name_in_view_list_1,
float(view_list_allocation_attribute_usage_value_1),
view_list_allocation_attribute_value_1,
view_list_percent_1,
)

usage_string_from_view_2: SafeString = format_html(
self.attribute_usage_formatter_template,
self.name_in_view_list_2,
float(view_list_allocation_attribute_usage_value_2),
view_list_allocation_attribute_value_2,
view_list_percent_2,
)

usage_string_from_no_view_1: SafeString = format_html(
self.attribute_usage_formatter_template,
self.name_not_in_view_list_1,
float(no_view_list_allocation_attribute_usage_value_1),
no_view_list_allocation_attribute_value_1,
no_view_list_percent_1,
)

usage_string_from_no_view_2: SafeString = format_html(
self.attribute_usage_formatter_template,
self.name_not_in_view_list_2,
float(no_view_list_allocation_attribute_usage_value_2),
no_view_list_allocation_attribute_value_2,
no_view_list_percent_2,
)

typename_string_from_view_1: SafeString = format_html(
self.attribute_view_list_formatter_template,
view_list_allocation_attribute_1.allocation_attribute_type.name,
view_list_allocation_attribute_1.value,
)

typename_string_from_view_2: SafeString = format_html(
self.attribute_view_list_formatter_template,
view_list_allocation_attribute_2.allocation_attribute_type.name,
view_list_allocation_attribute_2.value,
)

# Finally we can test the values...

expected_information: SafeString = format_html(
"{}{}{}{}{}{}",
typename_string_from_view_1,
usage_string_from_view_1,
typename_string_from_view_2,
usage_string_from_view_2,
usage_string_from_no_view_1,
usage_string_from_no_view_2,
)

actual_information: SafeString = tested_allocation.get_information

# with open('output.txt', 'w') as f:
# print(f'expected: \n\n{expected_information}', file=f)
# print("\n", file=f)
# print(f'actual: \n\n{actual_information}', file=f)

self.assertHTMLEqual(expected_information, actual_information)