From 6003b99d583e281cf7d4256953c7ef579e6a23dc Mon Sep 17 00:00:00 2001 From: dmartin4820 Date: Sun, 4 May 2025 16:33:21 -0700 Subject: [PATCH 1/4] feat: add model: leadership_type --- ..._leadershiptype_project_leadership_type.py | 33 +++++++++++++++++++ app/core/migrations/max_migration.txt | 2 +- app/core/models.py | 16 ++++++++- app/core/tests/conftest.py | 9 +++++ app/core/tests/test_models.py | 11 +++++++ 5 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 app/core/migrations/0036_leadershiptype_project_leadership_type.py diff --git a/app/core/migrations/0036_leadershiptype_project_leadership_type.py b/app/core/migrations/0036_leadershiptype_project_leadership_type.py new file mode 100644 index 00000000..a671be57 --- /dev/null +++ b/app/core/migrations/0036_leadershiptype_project_leadership_type.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.16 on 2025-05-05 00:52 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0035_referrertype'), + ] + + operations = [ + migrations.CreateModel( + name='LeadershipType', + fields=[ + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created at')), + ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated at')), + ('name', models.CharField(max_length=255, unique=True)), + ('description', models.TextField(blank=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='project', + name='leadership_type', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.leadershiptype'), + ), + ] diff --git a/app/core/migrations/max_migration.txt b/app/core/migrations/max_migration.txt index 403cfe75..789acf94 100644 --- a/app/core/migrations/max_migration.txt +++ b/app/core/migrations/max_migration.txt @@ -1 +1 @@ -0035_referrertype +0036_leadershiptype_project_leadership_type diff --git a/app/core/models.py b/app/core/models.py index 3e425e27..2f9888aa 100644 --- a/app/core/models.py +++ b/app/core/models.py @@ -27,6 +27,18 @@ def __repr__(self): return f"<{self.__class__.__name__} {self.uuid}>" +class LeadershipType(AbstractBaseModel): + """ + Dictionary of leadership types to be associated with a project + """ + + name = models.CharField(max_length=255, unique=True) + description = models.TextField(blank=True) + + def __str__(self): + return f"{self.name}" + + class User(PermissionsMixin, AbstractBaseUser, AbstractBaseModel): """ Table contains cognito-users & django-users. @@ -159,7 +171,9 @@ class Project(AbstractBaseModel): # location_id = models.ForeignKey("location", on_delete=models.PROTECT) google_drive_id = models.CharField(max_length=255, blank=True) # leads = models.ManyToManyField("lead") - # leadership_type_id = models.ForeignKey("leadership_type", on_delete=models.PROTECT) + leadership_type = models.ForeignKey( + LeadershipType, null=True, on_delete=models.SET_NULL + ) image_logo = models.URLField(blank=True) image_hero = models.URLField(blank=True) image_icon = models.URLField(blank=True) diff --git a/app/core/tests/conftest.py b/app/core/tests/conftest.py index 03ae1be1..5d68c2fd 100644 --- a/app/core/tests/conftest.py +++ b/app/core/tests/conftest.py @@ -11,6 +11,7 @@ from ..models import EventType from ..models import Faq from ..models import FaqViewed +from ..models import LeadershipType from ..models import Location from ..models import PermissionType from ..models import PracticeArea @@ -207,6 +208,14 @@ def faq_viewed(faq): return FaqViewed.objects.create(faq=faq) +@pytest.fixture +def leadership_type(): + return LeadershipType.objects.create( + name="This is a test leadership_type", + description="This is a test leadership_type description", + ) + + @pytest.fixture def location(): return Location.objects.create(name="Test Hack for L.A. HQ") diff --git a/app/core/tests/test_models.py b/app/core/tests/test_models.py index 406d23f2..65aae486 100644 --- a/app/core/tests/test_models.py +++ b/app/core/tests/test_models.py @@ -153,6 +153,17 @@ def test_event_type(event_type): assert event_type.description == "This is a test event_type description." +def test_leadership_type(leadership_type): + assert str(leadership_type) == "This is a test leadership_type" + assert leadership_type.description == "This is a test leadership_type description" + + +def test_leadership_type_project_relationship(project, leadership_type): + assert project.leadership_type is None + project.leadership_type = leadership_type + assert project.leadership_type == leadership_type + + def test_soc_major(soc_major): assert str(soc_major) == "Test Soc Major" From c0bfde0260c6442741a7652e72da76402f278114 Mon Sep 17 00:00:00 2001 From: dmartin4820 Date: Sun, 4 May 2025 16:37:51 -0700 Subject: [PATCH 2/4] feat: register admin: leadership_type --- app/core/admin.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/core/admin.py b/app/core/admin.py index 42f4dd3e..e34c5da8 100644 --- a/app/core/admin.py +++ b/app/core/admin.py @@ -12,6 +12,7 @@ from .models import EventType from .models import Faq from .models import FaqViewed +from .models import LeadershipType from .models import Location from .models import PermissionType from .models import PracticeArea @@ -172,6 +173,11 @@ class FaqViewed(admin.ModelAdmin): list_display = ("faq",) +@admin.register(LeadershipType) +class LeadershipType(admin.ModelAdmin): + list_display = ("name", "description") + + @admin.register(Location) class Location(admin.ModelAdmin): list_display = ( From eea683b063a58cdce09decff429d4b8771844d77 Mon Sep 17 00:00:00 2001 From: dmartin4820 Date: Sun, 4 May 2025 17:10:23 -0700 Subject: [PATCH 3/4] feat: add endpoints: leadership_type --- app/core/api/serializers.py | 14 ++++++++++++++ app/core/api/urls.py | 2 ++ app/core/api/views.py | 16 ++++++++++++++++ app/core/tests/test_api.py | 14 ++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/app/core/api/serializers.py b/app/core/api/serializers.py index 6ef165b3..0b20af01 100644 --- a/app/core/api/serializers.py +++ b/app/core/api/serializers.py @@ -8,6 +8,7 @@ from core.models import EventType from core.models import Faq from core.models import FaqViewed +from core.models import LeadershipType from core.models import Location from core.models import PermissionType from core.models import PracticeArea @@ -226,6 +227,19 @@ class Meta: ) +class LeadershipTypeSerializer(serializers.ModelSerializer): + """Used to retrieve leadership_type info""" + + class Meta: + model = LeadershipType + fields = ( + "uuid", + "name", + "description", + ) + read_only_fields = ("uuid", "created_at", "updated_at") + + class LocationSerializer(serializers.ModelSerializer): """Used to retrieve Location info""" diff --git a/app/core/api/urls.py b/app/core/api/urls.py index b21fd9a7..345fd331 100644 --- a/app/core/api/urls.py +++ b/app/core/api/urls.py @@ -8,6 +8,7 @@ from .views import EventViewSet from .views import FaqViewedViewSet from .views import FaqViewSet +from .views import LeadershipTypeViewSet from .views import LocationViewSet from .views import PermissionTypeViewSet from .views import PracticeAreaViewSet @@ -37,6 +38,7 @@ router.register(r"affiliates", AffiliateViewSet, basename="affiliate") router.register(r"faqs", FaqViewSet, basename="faq") router.register(r"faqs-viewed", FaqViewedViewSet, basename="faq-viewed") +router.register(r"leadership-types", LeadershipTypeViewSet, basename="leadership-type") router.register(r"locations", LocationViewSet, basename="location") router.register(r"program-areas", ProgramAreaViewSet, basename="program-area") router.register(r"skills", SkillViewSet, basename="skill") diff --git a/app/core/api/views.py b/app/core/api/views.py index 3e4afafb..8dda9bbb 100644 --- a/app/core/api/views.py +++ b/app/core/api/views.py @@ -18,6 +18,7 @@ from ..models import EventType from ..models import Faq from ..models import FaqViewed +from ..models import LeadershipType from ..models import Location from ..models import PermissionType from ..models import PracticeArea @@ -40,6 +41,7 @@ from .serializers import EventTypeSerializer from .serializers import FaqSerializer from .serializers import FaqViewedSerializer +from .serializers import LeadershipTypeSerializer from .serializers import LocationSerializer from .serializers import PermissionTypeSerializer from .serializers import PracticeAreaSerializer @@ -250,6 +252,20 @@ class FaqViewedViewSet(mixins.CreateModelMixin, viewsets.ReadOnlyModelViewSet): permission_classes = [IsAuthenticated] +@extend_schema_view( + list=extend_schema(description="Return a list of all locations"), + create=extend_schema(description="Create a new location"), + retrieve=extend_schema(description="Return the details of a location"), + destroy=extend_schema(description="Delete a location"), + update=extend_schema(description="Update a location"), + partial_update=extend_schema(description="Patch a location"), +) +class LeadershipTypeViewSet(viewsets.ModelViewSet): + queryset = LeadershipType.objects.all() + serializer_class = LeadershipTypeSerializer + permission_classes = [IsAuthenticated] + + @extend_schema_view( list=extend_schema(description="Return a list of all locations"), create=extend_schema(description="Create a new location"), diff --git a/app/core/tests/test_api.py b/app/core/tests/test_api.py index 4cdeae3b..94c25a79 100644 --- a/app/core/tests/test_api.py +++ b/app/core/tests/test_api.py @@ -20,6 +20,7 @@ FAQS_URL = reverse("faq-list") FAQS_VIEWED_URL = reverse("faq-viewed-list") AFFILIATE_URL = reverse("affiliate-list") +LEADERSHIP_TYPES_URL = reverse("leadership-type-list") LOCATION_URL = reverse("location-list") PROGRAM_AREAS_URL = reverse("program-area-list") REFERRER_TYPES_URL = reverse("referrer-type-list") @@ -249,6 +250,19 @@ def test_get_faq_viewed(auth_client, faq_viewed): assert res.data[0]["faq"] == faq_viewed.faq.pk +def test_create_leadership_type(auth_client): + """Test that we can create a leadership_type""" + + payload = { + "name": "Create leadership_type test", + "description": "Create leadership_type test description", + } + res = auth_client.post(LEADERSHIP_TYPES_URL, payload) + assert res.status_code == status.HTTP_201_CREATED + assert res.data["name"] == payload["name"] + assert res.data["description"] == payload["description"] + + def test_create_location(auth_client): """Test that we can create a location""" From dd7328c6dbd5a26f6532aa06fdd29e40dc762a96 Mon Sep 17 00:00:00 2001 From: dmartin4820 Date: Sun, 4 May 2025 18:16:28 -0700 Subject: [PATCH 4/4] feat: add seed data: leadership_type --- ...d explanations - LeadershipType - Data.csv | 5 ++++ .../migrations/0012_leadershiptype_seed.py | 24 +++++++++++++++++++ app/data/migrations/max_migration.txt | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 app/core/initial_data/PD_ Table and field explanations - LeadershipType - Data.csv create mode 100644 app/data/migrations/0012_leadershiptype_seed.py diff --git a/app/core/initial_data/PD_ Table and field explanations - LeadershipType - Data.csv b/app/core/initial_data/PD_ Table and field explanations - LeadershipType - Data.csv new file mode 100644 index 00000000..90ad7fad --- /dev/null +++ b/app/core/initial_data/PD_ Table and field explanations - LeadershipType - Data.csv @@ -0,0 +1,5 @@ +name,description +Mentor Led,Has a mentor in a leadership role +Peer Led,Peers run the meetings +Community Led,Community members run the meetings +Product Led,Projects diff --git a/app/data/migrations/0012_leadershiptype_seed.py b/app/data/migrations/0012_leadershiptype_seed.py new file mode 100644 index 00000000..44bf26a5 --- /dev/null +++ b/app/data/migrations/0012_leadershiptype_seed.py @@ -0,0 +1,24 @@ +from django.db import migrations + +from core.models import LeadershipType + + +def forward(__code__, __reverse_code__): + items = [ + ("Mentor Led", "Has a mentor in a leadership role"), + ("Peer Led", "Peers run the meetings"), + ("Community Led", "Community members run the meetings"), + ("Product Led", "Projects"), + ] + for name, description in items: + LeadershipType.objects.create(name=name, description=description) + + +def reverse(__code__, __reverse_code__): + LeadershipType.objects.all().delete() + + +class Migration(migrations.Migration): + dependencies = [("data", "0011_referrertype_seed")] + + operations = [migrations.RunPython(forward, reverse)] diff --git a/app/data/migrations/max_migration.txt b/app/data/migrations/max_migration.txt index 5f86367f..40417998 100644 --- a/app/data/migrations/max_migration.txt +++ b/app/data/migrations/max_migration.txt @@ -1 +1 @@ -0011_referrertype_seed +0012_leadershiptype_seed