diff --git a/solution/backend/common/fields.py b/solution/backend/common/fields.py index ea9ccb78de..e09234b3ef 100644 --- a/solution/backend/common/fields.py +++ b/solution/backend/common/fields.py @@ -1,6 +1,4 @@ # Contains custom fields for use throughout eRegs - - import datetime import re @@ -37,7 +35,8 @@ def __init__(self, *args, **kwargs): ), ], }} - return super().__init__(*args, **kwargs) + + super().__init__(*args, **kwargs) class NaturalSortField(models.CharField): diff --git a/solution/backend/common/tests/__init__.py b/solution/backend/common/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/solution/backend/common/tests/test_fields.py b/solution/backend/common/tests/test_fields.py new file mode 100644 index 0000000000..f84de4a7f7 --- /dev/null +++ b/solution/backend/common/tests/test_fields.py @@ -0,0 +1,47 @@ +import unittest +from common.fields import VariableDateField +from django.core.exceptions import ValidationError + + +class VariableDateFieldTest(unittest.TestCase): + def setUp(self): + self.field = VariableDateField() + + def test_valid_date_values(self): + values = [ + "2022-07-15", + "2022-07", + "2022", + "", + None, + ] + for value in values: + self.assertEqual(self.field.clean(value, value), value) + + def test_invalid_date_values(self): + values = [ + { + "value": "abcd-07", + "error_message": "Date field must be blank or of the format \"YYYY\", \"YYYY-MM\", or \"YYYY-MM-DD\"", + }, + { + "value": "13", + "error_message": "Date field must be blank or of the format \"YYYY\", \"YYYY-MM\", or \"YYYY-MM-DD\"", + }, + { + "value": "2022-02-30", + "error_message": "30 is not a valid day for the month of 02.", + }, + ] + + for value in values: + with self.assertRaises(ValidationError) as context: + self.field.clean(value["value"], None) + self.assertIn( + value["error_message"], + str(context.exception), + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/solution/backend/regulations/admin.py b/solution/backend/regulations/admin.py index 98b442bc7d..8b2dd4bb5b 100644 --- a/solution/backend/regulations/admin.py +++ b/solution/backend/regulations/admin.py @@ -71,7 +71,13 @@ def roman_to_int(roman): @admin.register(SiteConfiguration) class SiteConfigurationAdmin(SingletonModelAdmin): - pass + fields = ( + 'allow_indexing', + ('us_code_house_gov_date_type', 'us_code_house_gov_date'), + ('ssa_gov_compilation_date_type', 'ssa_gov_compilation_date'), + ('statute_compilation_date_type', 'statute_compilation_date'), + ('us_code_annual_date_type', 'us_code_annual_date'), + ) @admin.register(StatuteLinkConfiguration) diff --git a/solution/backend/regulations/migrations/0009_auto_20230714_1506.py b/solution/backend/regulations/migrations/0009_auto_20230714_1506.py new file mode 100644 index 0000000000..887ac479e9 --- /dev/null +++ b/solution/backend/regulations/migrations/0009_auto_20230714_1506.py @@ -0,0 +1,25 @@ +# Generated by Django 3.2.19 on 2023-07-14 15:06 + +import common.fields +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('regulations', '0008_statutelinkconfiguration'), + ] + + operations = [ + migrations.AddField( + model_name='siteconfiguration', + name='date', + field=common.fields.VariableDateField(blank=True, help_text='Leave blank or enter one of: "YYYY", "YYYY-MM", or "YYYY-MM-DD".', max_length=10, null=True, validators=[common.fields.validate_date, django.core.validators.RegexValidator(message='Date field must be blank or of the format "YYYY", "YYYY-MM", or "YYYY-MM-DD". For example: 2021, 2021-01, or 2021-01-31.', regex='^\\d{4}((-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]))|(-(0[1-9]|1[0-2])))?$')]), + ), + migrations.AddField( + model_name='siteconfiguration', + name='date_type', + field=models.CharField(blank=True, choices=[('effective', 'Effective'), ('amended', 'Amended')], default='', max_length=10, null=True), + ), + ] diff --git a/solution/backend/regulations/migrations/0010_auto_20230719_1157.py b/solution/backend/regulations/migrations/0010_auto_20230719_1157.py new file mode 100644 index 0000000000..29280945d2 --- /dev/null +++ b/solution/backend/regulations/migrations/0010_auto_20230719_1157.py @@ -0,0 +1,63 @@ +# Generated by Django 3.2.19 on 2023-07-19 11:57 + +import common.fields +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('regulations', '0009_auto_20230714_1506'), + ] + + operations = [ + migrations.RemoveField( + model_name='siteconfiguration', + name='date', + ), + migrations.RemoveField( + model_name='siteconfiguration', + name='date_type', + ), + migrations.AddField( + model_name='siteconfiguration', + name='ssa_gov_compilation_date', + field=common.fields.VariableDateField(blank=True, help_text='Leave blank or enter one of: "YYYY", "YYYY-MM", or "YYYY-MM-DD".', max_length=10, null=True, validators=[common.fields.validate_date, django.core.validators.RegexValidator(message='Date field must be blank or of the format "YYYY", "YYYY-MM", or "YYYY-MM-DD". For example: 2021, 2021-01, or 2021-01-31.', regex='^\\d{4}((-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]))|(-(0[1-9]|1[0-2])))?$')], verbose_name='SSA.gov Compilation Date'), + ), + migrations.AddField( + model_name='siteconfiguration', + name='ssa_gov_compilation_date_type', + field=models.CharField(blank=True, choices=[('effective', 'Effective'), ('amended', 'Amended')], default='', max_length=10, null=True, verbose_name='SSA.gov Compilation Date Type'), + ), + migrations.AddField( + model_name='siteconfiguration', + name='statute_compilation_date', + field=common.fields.VariableDateField(blank=True, help_text='Leave blank or enter one of: "YYYY", "YYYY-MM", or "YYYY-MM-DD".', max_length=10, null=True, validators=[common.fields.validate_date, django.core.validators.RegexValidator(message='Date field must be blank or of the format "YYYY", "YYYY-MM", or "YYYY-MM-DD". For example: 2021, 2021-01, or 2021-01-31.', regex='^\\d{4}((-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]))|(-(0[1-9]|1[0-2])))?$')], verbose_name='Statute Compilation Date'), + ), + migrations.AddField( + model_name='siteconfiguration', + name='statute_compilation_date_type', + field=models.CharField(blank=True, choices=[('effective', 'Effective'), ('amended', 'Amended')], default='', max_length=10, null=True, verbose_name='Statute Compilation Date Type'), + ), + migrations.AddField( + model_name='siteconfiguration', + name='us_code_annual_date', + field=common.fields.VariableDateField(blank=True, help_text='Leave blank or enter one of: "YYYY", "YYYY-MM", or "YYYY-MM-DD".', max_length=10, null=True, validators=[common.fields.validate_date, django.core.validators.RegexValidator(message='Date field must be blank or of the format "YYYY", "YYYY-MM", or "YYYY-MM-DD". For example: 2021, 2021-01, or 2021-01-31.', regex='^\\d{4}((-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]))|(-(0[1-9]|1[0-2])))?$')], verbose_name='US Code Annual Date'), + ), + migrations.AddField( + model_name='siteconfiguration', + name='us_code_annual_date_type', + field=models.CharField(blank=True, choices=[('effective', 'Effective'), ('amended', 'Amended')], default='', max_length=10, null=True, verbose_name='US Code Annual Date Type'), + ), + migrations.AddField( + model_name='siteconfiguration', + name='us_code_house_gov_date', + field=common.fields.VariableDateField(blank=True, help_text='Leave blank or enter one of: "YYYY", "YYYY-MM", or "YYYY-MM-DD".', max_length=10, null=True, validators=[common.fields.validate_date, django.core.validators.RegexValidator(message='Date field must be blank or of the format "YYYY", "YYYY-MM", or "YYYY-MM-DD". For example: 2021, 2021-01, or 2021-01-31.', regex='^\\d{4}((-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]))|(-(0[1-9]|1[0-2])))?$')], verbose_name='US Code House.gov Date'), + ), + migrations.AddField( + model_name='siteconfiguration', + name='us_code_house_gov_date_type', + field=models.CharField(blank=True, choices=[('effective', 'Effective'), ('amended', 'Amended')], default='', max_length=10, null=True, verbose_name='US Code House.gov Date Type'), + ), + ] diff --git a/solution/backend/regulations/models.py b/solution/backend/regulations/models.py index 808836ea7f..c1b1a2502b 100644 --- a/solution/backend/regulations/models.py +++ b/solution/backend/regulations/models.py @@ -1,12 +1,10 @@ import re from django.db import models - +from common.fields import VariableDateField, NaturalSortField from django_jsonform.models.fields import ArrayField from solo.models import SingletonModel -from common.fields import NaturalSortField - ROMAN_TABLE = [ [1000, "M"], @@ -28,7 +26,63 @@ class SiteConfiguration(SingletonModel): allow_indexing = models.BooleanField(default=False, help_text="Should robots be allowed to index this website?") + DATE_TYPE_CHOICES = ( + ('effective', 'Effective'), + ('amended', 'Amended'), + ) + + date_fields = [ + { + 'name': 'us_code_house_gov', + 'verbose_name': 'US Code House.gov', + }, + { + 'name': 'ssa_gov_compilation', + 'verbose_name': 'SSA.gov Compilation', + }, + { + 'name': 'statute_compilation', + 'verbose_name': 'Statute Compilation', + }, + { + 'name': 'us_code_annual', + 'verbose_name': 'US Code Annual', + }, + ] + + for field in date_fields: + field_name = field['name'] + field_verbose_name = field['verbose_name'] + + locals()[f"{field_name}_date_type"] = models.CharField( + max_length=10, + choices=DATE_TYPE_CHOICES, + default='', + blank=True, + null=True, + verbose_name=f"{field_verbose_name} Date Type" + ) + + locals()[f"{field_name}_date"] = VariableDateField( + verbose_name=f"{field_verbose_name} Date" + ) + def __str__(self): + date_info = [] + for field in self.date_fields: + field_name = field['name'] + field_verbose_name = field['verbose_name'] + date_type = getattr(self, f"{field_name}_date_type") + date = getattr(self, f"{field_name}_date") + if date: + if date_type: + date_info.append(f"{field_verbose_name} - {date_type.capitalize()} {date}") + else: + date_info.append(f"{field_verbose_name} - Date {date}") + + if date_info: + return "; \n".join(date_info) + return "Site Configuration" class Meta: diff --git a/solution/backend/regulations/tests/test_site_configuration.py b/solution/backend/regulations/tests/test_site_configuration.py new file mode 100644 index 0000000000..77e20659a8 --- /dev/null +++ b/solution/backend/regulations/tests/test_site_configuration.py @@ -0,0 +1,63 @@ +import unittest +import pytest +from regulations.models import SiteConfiguration + + +@pytest.mark.django_db +class SiteConfigurationTest(unittest.TestCase): + def setUp(self): + SiteConfiguration.objects.all().delete() + + def test_date_fields(self): + site_config = SiteConfiguration.objects.create(pk=1) + date_fields = [ + { + 'field_name': 'us_code_house_gov', + 'date_type': 'effective', + 'date': '2023', + }, + { + 'field_name': 'ssa_gov_compilation', + 'date_type': 'amended', + 'date': 'Dec 2018', + }, + { + 'field_name': 'statute_compilation', + 'date_type': 'effective', + 'date': 'Dec 2019', + }, + { + 'field_name': 'us_code_annual', + 'date_type': 'effective', + 'date': 'Dec 2022', + }, + ] + + for field in date_fields: + field_name = field['field_name'] + date_type = field['date_type'] + date = field['date'] + + # Set the date field values + setattr(site_config, f"{field_name}_date_type", date_type) + setattr(site_config, f"{field_name}_date", date) + + # Retrieve the date field values and assert + retrieved_date_type = getattr(site_config, f"{field_name}_date_type") + retrieved_date = getattr(site_config, f"{field_name}_date") + + self.assertEqual(retrieved_date_type, date_type) + self.assertEqual(retrieved_date, date) + + def test_site_configuration_str(self): + site_config = SiteConfiguration.get_solo() + expected_str = 'Site Configuration' + self.assertEqual(str(site_config), expected_str) + + def test_site_configuration_valid_date_type(self): + site_config = SiteConfiguration.get_solo() + site_config.full_clean() # Should not raise any ValidationError + + def test_site_configuration_empty_date_type(self): + site_config = SiteConfiguration.get_solo() + site_config.full_clean() # Should not raise any ValidationError