diff --git a/server/apps/conftest.py b/server/apps/conftest.py index 95d0eec84..16e7b098d 100644 --- a/server/apps/conftest.py +++ b/server/apps/conftest.py @@ -93,9 +93,11 @@ def haztrak_profile_factory(db, user_factory): def create_profile( user: Optional[User] = None, - ) -> RcraProfile: + admin_rcrainfo_profile: Optional[RcraProfile] = None, + ) -> HaztrakProfile: return HaztrakProfile.objects.create( user=user or user_factory(), + admin_rcrainfo_profile=admin_rcrainfo_profile, ) yield create_profile @@ -206,10 +208,12 @@ def site_factory(db, rcra_site_factory): def create_site( rcra_site: Optional[RcraSite] = None, name: Optional[str] = "my site name", + admin_rcrainfo_profile: Optional[RcraProfile] = None, ) -> Site: return Site.objects.create( rcra_site=rcra_site or rcra_site_factory(), name=name, + admin_rcrainfo_profile=admin_rcrainfo_profile, ) yield create_site diff --git a/server/apps/core/migrations/0004_haztrakprofile_admin_rcrainfo_profile.py b/server/apps/core/migrations/0004_haztrakprofile_admin_rcrainfo_profile.py new file mode 100644 index 000000000..3cc5b87ae --- /dev/null +++ b/server/apps/core/migrations/0004_haztrakprofile_admin_rcrainfo_profile.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.7 on 2023-11-08 17:29 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0003_alter_haztrakprofile_user"), + ] + + operations = [ + migrations.AddField( + model_name="haztrakprofile", + name="admin_rcrainfo_profile", + field=models.ForeignKey( + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="core.rcraprofile", + ), + ), + ] diff --git a/server/apps/core/models.py b/server/apps/core/models.py index aa361b764..99a3604fc 100644 --- a/server/apps/core/models.py +++ b/server/apps/core/models.py @@ -1,5 +1,6 @@ from django.contrib.auth.models import AbstractUser from django.db import models +from django.utils.functional import cached_property from haztrak import settings @@ -36,9 +37,12 @@ class Meta: on_delete=models.CASCADE, related_name="haztrak_profile", ) - - def __str__(self): - return f"{self.user.username}" + admin_rcrainfo_profile = models.ForeignKey( + "RcraProfile", + on_delete=models.SET_NULL, + null=True, + default=None, + ) class RcraProfile(CoreBaseModel): @@ -80,8 +84,8 @@ def __str__(self): return f"{self.user.username}" @property - def is_api_user(self) -> bool: + def has_api_credentials(self) -> bool: """Returns true if the use has Rcrainfo API credentials""" - if self.rcra_username and self.rcra_api_id and self.rcra_api_key: + if self.rcra_api_id and self.rcra_api_key: return True return False diff --git a/server/apps/core/serializers.py b/server/apps/core/serializers.py index 921d612c8..f5b6a0d13 100644 --- a/server/apps/core/serializers.py +++ b/server/apps/core/serializers.py @@ -84,7 +84,7 @@ class RcraProfileSerializer(ModelSerializer): required=False, ) apiUser = serializers.BooleanField( - source="is_api_user", + source="has_api_credentials", required=False, allow_null=False, ) diff --git a/server/apps/core/services/rcrainfo_service.py b/server/apps/core/services/rcrainfo_service.py index 58f748fa1..9de22a6b4 100644 --- a/server/apps/core/services/rcrainfo_service.py +++ b/server/apps/core/services/rcrainfo_service.py @@ -43,7 +43,7 @@ def __init__( def has_api_user(self) -> bool: """returns boolean if the assigned API user has credentials""" try: - return self.profile.is_api_user + return self.profile.has_api_credentials except AttributeError: return False diff --git a/server/apps/sites/admin.py b/server/apps/sites/admin.py index 5d579440a..31ddf961e 100644 --- a/server/apps/sites/admin.py +++ b/server/apps/sites/admin.py @@ -25,7 +25,7 @@ class SitePermissionAdmin(admin.ModelAdmin): @admin.register(Site) class SiteAdmin(admin.ModelAdmin): - list_display = ["__str__", "related_handler", "last_rcra_sync"] + list_display = ["__str__", "related_handler", "last_rcrainfo_manifest_sync"] list_display_links = ["__str__", "related_handler"] @admin.display(description="EPA Site") @@ -61,7 +61,7 @@ def related_user(self, user): return format_html("{}", url, user) def api_user(self, profile: RcraProfile) -> bool: - return profile.is_api_user + return profile.has_api_credentials api_user.boolean = True api_user.short_description = "Rcrainfo API User" diff --git a/server/apps/sites/migrations/0003_alter_site_last_rcrainfo_manifest_sync.py b/server/apps/sites/migrations/0003_alter_site_last_rcrainfo_manifest_sync.py new file mode 100644 index 000000000..5dafd2b50 --- /dev/null +++ b/server/apps/sites/migrations/0003_alter_site_last_rcrainfo_manifest_sync.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.7 on 2023-11-08 19:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("sites", "0002_sitepermissions"), + ] + + operations = [ + migrations.AlterField( + model_name="site", + name="last_rcra_sync", + field=models.DateTimeField( + blank=True, null=True, verbose_name="last RCRAInfo manifest sync date" + ), + ), + ] diff --git a/server/apps/sites/migrations/0004_site_admin_rcrainfo_profile.py b/server/apps/sites/migrations/0004_site_admin_rcrainfo_profile.py new file mode 100644 index 000000000..75581fe00 --- /dev/null +++ b/server/apps/sites/migrations/0004_site_admin_rcrainfo_profile.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.7 on 2023-11-08 19:38 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0004_haztrakprofile_admin_rcrainfo_profile"), + ("sites", "0003_alter_site_last_rcrainfo_manifest_sync"), + ] + + operations = [ + migrations.AddField( + model_name="site", + name="admin_rcrainfo_profile", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="core.rcraprofile", + ), + ), + ] diff --git a/server/apps/sites/migrations/0005_rename_last_rcra_sync_site_last_rcrainfo_manifest_sync.py b/server/apps/sites/migrations/0005_rename_last_rcra_sync_site_last_rcrainfo_manifest_sync.py new file mode 100644 index 000000000..b545836c1 --- /dev/null +++ b/server/apps/sites/migrations/0005_rename_last_rcra_sync_site_last_rcrainfo_manifest_sync.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.7 on 2023-11-08 20:02 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("sites", "0004_site_admin_rcrainfo_profile"), + ] + + operations = [ + migrations.RenameField( + model_name="site", + old_name="last_rcra_sync", + new_name="last_rcrainfo_manifest_sync", + ), + ] diff --git a/server/apps/sites/models/site_models.py b/server/apps/sites/models/site_models.py index da7a5556f..f868c4513 100644 --- a/server/apps/sites/models/site_models.py +++ b/server/apps/sites/models/site_models.py @@ -195,11 +195,23 @@ class Meta: to=RcraSite, on_delete=models.CASCADE, ) - last_rcra_sync = models.DateTimeField( - verbose_name="last sync with RCRAInfo", + last_rcrainfo_manifest_sync = models.DateTimeField( + verbose_name="last RCRAInfo manifest sync date", null=True, blank=True, ) + admin_rcrainfo_profile = models.ForeignKey( + "core.RcraProfile", + on_delete=models.SET_NULL, + null=True, + ) + + @property + def admin_has_rcrainfo_api_credentials(self) -> bool: + """Returns True if the admin user has RcraInfo API credentials""" + if self.admin_rcrainfo_profile.has_api_credentials: + return True + return False def __str__(self): """Used in StringRelated fields in serializer classes""" diff --git a/server/apps/sites/services/site_services.py b/server/apps/sites/services/site_services.py index ccdbfb1f6..c82639d7f 100644 --- a/server/apps/sites/services/site_services.py +++ b/server/apps/sites/services/site_services.py @@ -53,13 +53,13 @@ def sync_manifests(self, *, site_id: str) -> PullManifestsResult: try: site = Site.objects.get(rcra_site__epa_id=site_id) updated_mtn = self._get_updated_mtn( - site_id=site.rcra_site.epa_id, last_sync_date=site.last_rcra_sync + site_id=site.rcra_site.epa_id, last_sync_date=site.last_rcrainfo_manifest_sync ) updated_mtn = updated_mtn[:15] # temporary limit to 15 logger.info(f"Pulling {updated_mtn} from RCRAInfo") manifest = ManifestService(username=self.username, rcrainfo=self.rcrainfo) results: PullManifestsResult = manifest.pull_manifests(tracking_numbers=updated_mtn) - site.last_rcra_sync = datetime.now(UTC) + site.last_rcrainfo_manifest_sync = datetime.now(UTC) site.save() return results except Site.DoesNotExist: diff --git a/server/apps/sites/tests/test_models.py b/server/apps/sites/tests/test_models.py index 2a5d84782..cfb4a3f3a 100644 --- a/server/apps/sites/tests/test_models.py +++ b/server/apps/sites/tests/test_models.py @@ -3,7 +3,7 @@ from django.db import IntegrityError from apps.core.models import RcraProfile -from apps.sites.models import Address, Contact +from apps.sites.models import Address, Contact, Site @pytest.mark.django_db @@ -42,21 +42,48 @@ def test_rcra_profile_saves(self, rcra_profile_factory): rcra_profile = rcra_profile_factory() assert isinstance(rcra_profile, RcraProfile) - @pytest.mark.parametrize("rcra_username", ["username", None]) @pytest.mark.parametrize("rcra_api_id", ["id", None]) @pytest.mark.parametrize("rcra_api_key", ["key", None]) def test_rcra_profile_is_not_api_user_if_one_missing( - self, rcra_profile_factory, rcra_username, rcra_api_id, rcra_api_key + self, rcra_profile_factory, rcra_api_id, rcra_api_key ): """If any of the three are None, the user should not be considered an API user""" # Arrange expected = True - if rcra_username is None or rcra_api_id is None or rcra_api_key is None: + if rcra_api_id is None or rcra_api_key is None: expected = False - rcra_profile = rcra_profile_factory( - rcra_username=rcra_username, rcra_api_id=rcra_api_id, rcra_api_key=rcra_api_key - ) + rcra_profile = rcra_profile_factory(rcra_api_id=rcra_api_id, rcra_api_key=rcra_api_key) # Act - api_user = rcra_profile.is_api_user + api_user = rcra_profile.has_api_credentials # Assert assert api_user is expected + + +class TestHaztrakSiteModel: + def test_haztrak_site_model_factory(self, site_factory): + haztrak_site = site_factory() + assert isinstance(haztrak_site, Site) + + def test_returns_true_if_admin_has_provided_api_credentials( + self, site_factory, rcra_profile_factory, user_factory + ): + admin = user_factory(username="admin") + admin_rcrainfo_profile = rcra_profile_factory( + user=admin, + rcra_api_id="mock_id", + rcra_api_key="mock_key", + ) + haztrak_profile = site_factory(admin_rcrainfo_profile=admin_rcrainfo_profile) + assert haztrak_profile.admin_has_rcrainfo_api_credentials + + def test_returns_false_if_admin_has_not_provided_api_credentials( + self, site_factory, rcra_profile_factory, user_factory + ): + admin = user_factory(username="admin") + admin_rcrainfo_profile = rcra_profile_factory( + user=admin, + rcra_api_id=None, + rcra_api_key=None, + ) + haztrak_profile = site_factory(admin_rcrainfo_profile=admin_rcrainfo_profile) + assert not haztrak_profile.admin_has_rcrainfo_api_credentials diff --git a/server/apps/trak/admin.py b/server/apps/trak/admin.py index 7308d54f2..f21bfe921 100644 --- a/server/apps/trak/admin.py +++ b/server/apps/trak/admin.py @@ -20,7 +20,7 @@ class IsApiUser(admin.SimpleListFilter): title = "API User" - parameter_name = "is_api_user" + parameter_name = "has_api_credentials" def lookups(self, request, model_admin): return ("True", True), ("False", False) diff --git a/server/fixtures/dev_data.yaml b/server/fixtures/dev_data.yaml index e24e0abf6..bb655f387 100644 --- a/server/fixtures/dev_data.yaml +++ b/server/fixtures/dev_data.yaml @@ -138,7 +138,7 @@ fields: name: VA TEST GEN 2021 rcra_site: 1 - last_rcra_sync: null + last_rcrainfo_manifest_sync: null - model: sites.rcrasitepermission pk: 1 fields: