From 27c82755ea22b63d38a4a2580df055d971138b12 Mon Sep 17 00:00:00 2001 From: Caleb Date: Mon, 14 Oct 2024 12:12:46 -0600 Subject: [PATCH 01/18] uncomment category logic --- screener/views.py | 50 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/screener/views.py b/screener/views.py index 1381561e..088e31fa 100644 --- a/screener/views.py +++ b/screener/views.py @@ -198,9 +198,7 @@ def eligibility_results(screen: Screen, batch=False): excluded_programs = [p.id for p in referrer.remove_programs.all()] all_programs = ( - Program.objects.filter(active=True) - # NOTE: uncomment when categories are ready - # .filter(active=True, category_v2__isnull=False) + Program.objects.filter(active=True, category_v2__isnull=False) .prefetch_related( "legal_status_required", "fpl", @@ -220,7 +218,8 @@ def eligibility_results(screen: Screen, batch=False): *translations_prefetch_name("translation_overrides__", TranslationOverride.objects.translated_fields), "category_v2", *translations_prefetch_name("category_v2__", ProgramCategory.objects.translated_fields), - ).exclude(id__in=excluded_programs) + ) + .exclude(id__in=excluded_programs) ) data = [] @@ -380,28 +379,27 @@ def sort_first(program): ) categories = {} - # NOTE: uncomment when categories are ready - # for program in all_programs: - # category = program.category_v2 - # if category.id in categories: - # continue - # - # CategoryCalculator = ProgramCategoryCapCalculator - # if category.calculator is not None and category.calculator != "": - # CategoryCalculator = category_cap_calculators[category.calculator] - # - # calculator = CategoryCalculator(program_eligibility) - # - # caps = [] - # for cap in calculator.caps(): - # caps.append({"programs": cap.programs, "cap": cap.cap}) - # - # categories[category.id] = { - # "icon": category.icon, - # "name": default_message(category.name), - # "description": default_message(category.description), - # "cap": caps, - # } + for program in all_programs: + category = program.category_v2 + if category.id in categories: + continue + + CategoryCalculator = ProgramCategoryCapCalculator + if category.calculator is not None and category.calculator != "": + CategoryCalculator = category_cap_calculators[category.calculator] + + calculator = CategoryCalculator(program_eligibility) + + caps = [] + for cap in calculator.caps(): + caps.append({"programs": cap.programs, "cap": cap.cap}) + + categories[category.id] = { + "icon": category.icon, + "name": default_message(category.name), + "description": default_message(category.description), + "cap": caps, + } ProgramEligibilitySnapshot.objects.bulk_create(program_snapshots) snapshot.had_error = False From f6244810d40f161c72569e581d37ca9aebd9085c Mon Sep 17 00:00:00 2001 From: Caleb Date: Mon, 14 Oct 2024 13:08:44 -0600 Subject: [PATCH 02/18] remove old category field --- programs/admin.py | 3 -- .../management/commands/remove_programs.py | 1 - .../0093_remove_program_category.py | 17 +++++++++++ programs/models.py | 4 --- programs/serializers.py | 27 ++++++++++++++--- programs/urls.py | 1 + programs/views.py | 29 +++++++++---------- screener/serializers.py | 1 - screener/views.py | 3 +- translations/templates/programs/list.html | 5 ---- translations/templates/programs/program.html | 3 -- 11 files changed, 56 insertions(+), 38 deletions(-) create mode 100644 programs/migrations/0093_remove_program_category.py diff --git a/programs/admin.py b/programs/admin.py index e72b5d53..dd219d66 100644 --- a/programs/admin.py +++ b/programs/admin.py @@ -41,7 +41,6 @@ def action_buttons(self, obj): description_short = obj.description_short learn_more_link = obj.learn_more_link apply_button_link = obj.apply_button_link - category = obj.category estimated_delivery_time = obj.estimated_delivery_time estimated_application_time = obj.estimated_application_time value_type = obj.value_type @@ -56,7 +55,6 @@ def action_buttons(self, obj): Name Description Short Description - Category Learn More Link Apply Button Link Estimated Delivery Time @@ -70,7 +68,6 @@ def action_buttons(self, obj): reverse("translation_admin_url", args=[name.id]), reverse("translation_admin_url", args=[description.id]), reverse("translation_admin_url", args=[description_short.id]), - reverse("translation_admin_url", args=[category.id]), reverse("translation_admin_url", args=[learn_more_link.id]), reverse("translation_admin_url", args=[apply_button_link.id]), reverse("translation_admin_url", args=[estimated_delivery_time.id]), diff --git a/programs/management/commands/remove_programs.py b/programs/management/commands/remove_programs.py index bb16524c..09874b26 100644 --- a/programs/management/commands/remove_programs.py +++ b/programs/management/commands/remove_programs.py @@ -19,7 +19,6 @@ def handle(self, *args, **options): "value_type", "estimated_delivery_time", "estimated_application_time", - "category", "warning", ) diff --git a/programs/migrations/0093_remove_program_category.py b/programs/migrations/0093_remove_program_category.py new file mode 100644 index 00000000..bcd6ffc7 --- /dev/null +++ b/programs/migrations/0093_remove_program_category.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.15 on 2024-10-14 18:13 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("programs", "0092_programcategory_icon"), + ] + + operations = [ + migrations.RemoveField( + model_name="program", + name="category", + ), + ] diff --git a/programs/models.py b/programs/models.py index b8590033..de431599 100644 --- a/programs/models.py +++ b/programs/models.py @@ -205,7 +205,6 @@ class ProgramManager(models.Manager): "value_type", "estimated_delivery_time", "estimated_application_time", - "category", "estimated_value", "website_description", ) @@ -368,9 +367,6 @@ class Program(models.Model): null=False, on_delete=models.PROTECT, ) - category = models.ForeignKey( - Translation, related_name="program_category", blank=False, null=False, on_delete=models.PROTECT - ) estimated_value = models.ForeignKey( Translation, related_name="program_estimated_value", diff --git a/programs/serializers.py b/programs/serializers.py index de6a3157..1c581dab 100644 --- a/programs/serializers.py +++ b/programs/serializers.py @@ -1,4 +1,4 @@ -from programs.models import Program, UrgentNeed, Navigator +from programs.models import Program, ProgramCategory, UrgentNeed, Navigator from rest_framework import serializers from translations.serializers import ModelTranslationSerializer @@ -9,16 +9,35 @@ class Meta: fields = "__all__" +class ProgramSerializerMeta: + model = Program + fields = ("id", "name", "website_description") + + class ProgramSerializer(serializers.ModelSerializer): name = ModelTranslationSerializer() website_description = ModelTranslationSerializer() - category = ModelTranslationSerializer() - class Meta: - model = Program + class Meta(ProgramSerializerMeta): + pass + + +class ProgramSerializerWithCategory(ProgramSerializer): + category = ModelTranslationSerializer(source="category_v2.name") + + class Meta(ProgramSerializerMeta): fields = ("id", "name", "website_description", "category") +class ProgramCategorySerializer(serializers.ModelSerializer): + programs = ProgramSerializer(many=True) + name = ModelTranslationSerializer() + + class Meta: + model = ProgramCategory + fields = ("id", "name", "programs") + + class UrgentNeedAPISerializer(serializers.ModelSerializer): name = ModelTranslationSerializer() website_description = ModelTranslationSerializer() diff --git a/programs/urls.py b/programs/urls.py index ddebede9..f502e920 100644 --- a/programs/urls.py +++ b/programs/urls.py @@ -4,6 +4,7 @@ router = routers.DefaultRouter() router.register(r"programs", views.ProgramViewSet) +router.register(r"program_categories", views.ProgramCategoryViewSet) router.register(r"navigators", views.NavigatorViewSet) router.register(r"urgent-needs", views.UrgentNeedViewSet) diff --git a/programs/views.py b/programs/views.py index 8d16cfc5..06f587e0 100644 --- a/programs/views.py +++ b/programs/views.py @@ -1,34 +1,33 @@ -from programs.models import Program, Navigator, UrgentNeed +from programs.models import Program, Navigator, ProgramCategory, UrgentNeed from rest_framework import viewsets, mixins from rest_framework import permissions -from programs.serializers import ProgramSerializer, NavigatorAPISerializer, UrgentNeedAPISerializer +from programs.serializers import ( + ProgramCategorySerializer, + NavigatorAPISerializer, + ProgramSerializerWithCategory, + UrgentNeedAPISerializer, +) class ProgramViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet): - """ - API endpoint that allows programs to be viewed or edited. - """ + queryset = Program.objects.filter(active=True, category_v2__isnull=False) + serializer_class = ProgramSerializerWithCategory + permission_classes = [permissions.IsAuthenticated] + - queryset = Program.objects.filter(active=True) - serializer_class = ProgramSerializer +class ProgramCategoryViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet): + queryset = ProgramCategory.objects.filter(programs__isnull=False) + serializer_class = ProgramCategorySerializer permission_classes = [permissions.IsAuthenticated] class NavigatorViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet): - """ - API endpoint that allows programs to be viewed or edited. - """ - queryset = Navigator.objects.all() serializer_class = NavigatorAPISerializer permission_classes = [permissions.IsAuthenticated] class UrgentNeedViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet): - """ - API endpoint that allows programs to be viewed or edited. - """ - queryset = UrgentNeed.objects.filter(active=True) serializer_class = UrgentNeedAPISerializer permission_classes = [permissions.IsAuthenticated] diff --git a/screener/serializers.py b/screener/serializers.py index 9959b654..2947605e 100644 --- a/screener/serializers.py +++ b/screener/serializers.py @@ -263,7 +263,6 @@ class EligibilitySerializer(serializers.Serializer): estimated_delivery_time = TranslationSerializer() estimated_application_time = TranslationSerializer() legal_status_required = serializers.ListField() - category = TranslationSerializer() eligible = serializers.BooleanField() failed_tests = serializers.ListField() passed_tests = serializers.ListField() diff --git a/screener/views.py b/screener/views.py index 088e31fa..648a3d8c 100644 --- a/screener/views.py +++ b/screener/views.py @@ -26,7 +26,7 @@ MessageSerializer, ResultsSerializer, ) -from programs.programs.policyengine.policy_engine import all_eligibility, calc_pe_eligibility +from programs.programs.policyengine.policy_engine import calc_pe_eligibility from programs.util import DependencyError, Dependencies from programs.programs.urgent_needs.urgent_need_functions import urgent_need_functions from programs.models import ( @@ -363,7 +363,6 @@ def sort_first(program): "learn_more_link": program_translations.get_translation("learn_more_link"), "apply_button_link": program_translations.get_translation("apply_button_link"), "legal_status_required": legal_status, - "category": program_translations.get_translation("category"), "estimated_value_override": program_translations.get_translation("estimated_value"), "eligible": eligibility.eligible, "failed_tests": eligibility.fail_messages, diff --git a/translations/templates/programs/list.html b/translations/templates/programs/list.html index f3393030..f544ab4f 100644 --- a/translations/templates/programs/list.html +++ b/translations/templates/programs/list.html @@ -80,11 +80,6 @@ class="dropdown-item" >Estimated application time - Category Program Actions > Estimated application time - - Category - Website description Date: Mon, 14 Oct 2024 15:57:40 -0600 Subject: [PATCH 03/18] stop sending duplicate categories --- programs/serializers.py | 2 +- programs/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/serializers.py b/programs/serializers.py index 1c581dab..45fcf31b 100644 --- a/programs/serializers.py +++ b/programs/serializers.py @@ -35,7 +35,7 @@ class ProgramCategorySerializer(serializers.ModelSerializer): class Meta: model = ProgramCategory - fields = ("id", "name", "programs") + fields = ("id", "name", "icon", "programs") class UrgentNeedAPISerializer(serializers.ModelSerializer): diff --git a/programs/views.py b/programs/views.py index 06f587e0..d5ede550 100644 --- a/programs/views.py +++ b/programs/views.py @@ -16,7 +16,7 @@ class ProgramViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, viewsets. class ProgramCategoryViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet): - queryset = ProgramCategory.objects.filter(programs__isnull=False) + queryset = ProgramCategory.objects.filter(programs__isnull=False).distinct() serializer_class = ProgramCategorySerializer permission_classes = [permissions.IsAuthenticated] From 61d693782a68df0340c30804f7d79db2ff7e2b3c Mon Sep 17 00:00:00 2001 From: Caleb Date: Tue, 15 Oct 2024 13:19:33 -0600 Subject: [PATCH 04/18] update api --- programs/programs/categories/base.py | 7 ++++++- programs/programs/categories/co/__init__.py | 7 +++++-- .../categories/co/{preschool.py => caps.py} | 4 ++++ screener/serializers.py | 5 +++-- screener/views.py | 13 ++++++++----- 5 files changed, 26 insertions(+), 10 deletions(-) rename programs/programs/categories/co/{preschool.py => caps.py} (60%) diff --git a/programs/programs/categories/base.py b/programs/programs/categories/base.py index 2dda2e0c..1b1c31fc 100644 --- a/programs/programs/categories/base.py +++ b/programs/programs/categories/base.py @@ -46,7 +46,12 @@ def calc_max_cap(self, cap: CategoryCap, values: list[int]): return max(*values) def calc_average_cap(self, cap: CategoryCap, values: list[int]): - return sum(values) / len(values) + non_0_values = [v for v in values if v > 0] + + if len(non_0_values) == 0: + return 0 + + return sum(non_0_values) / len(non_0_values) def _handle_caps(self, caps: list[CategoryCap], func: Callable[[CategoryCap, list[int]], int]) -> list[CategoryCap]: """ diff --git a/programs/programs/categories/co/__init__.py b/programs/programs/categories/co/__init__.py index 15c3bca4..271e003e 100644 --- a/programs/programs/categories/co/__init__.py +++ b/programs/programs/categories/co/__init__.py @@ -1,4 +1,7 @@ -from programs.programs.categories.co.preschool import PreschoolCategoryCap +from programs.programs.categories.co.caps import HealthCareCategoryCap, PreschoolCategoryCap from ..base import ProgramCategoryCapCalculator -co_category_cap_calculators: dict[str, type[ProgramCategoryCapCalculator]] = {"co_preschool": PreschoolCategoryCap} +co_category_cap_calculators: dict[str, type[ProgramCategoryCapCalculator]] = { + "co_preschool": PreschoolCategoryCap, + "co_health_care": HealthCareCategoryCap, +} diff --git a/programs/programs/categories/co/preschool.py b/programs/programs/categories/co/caps.py similarity index 60% rename from programs/programs/categories/co/preschool.py rename to programs/programs/categories/co/caps.py index b1ac5971..e9bb5ea7 100644 --- a/programs/programs/categories/co/preschool.py +++ b/programs/programs/categories/co/caps.py @@ -3,3 +3,7 @@ class PreschoolCategoryCap(ProgramCategoryCapCalculator): static_caps = [CategoryCap(["dpp", "upk", "chs"], cap=8_640, member_cap=True)] + + +class HealthCareCategoryCap(ProgramCategoryCapCalculator): + average_caps = [CategoryCap(["cfhc", "awd_medicaid", "cwd_medicaid"], member_cap=True)] diff --git a/screener/serializers.py b/screener/serializers.py index 2947605e..9eb0c004 100644 --- a/screener/serializers.py +++ b/screener/serializers.py @@ -274,7 +274,6 @@ class EligibilitySerializer(serializers.Serializer): multiple_tax_units = serializers.BooleanField() estimated_value_override = TranslationSerializer() warning_messages = TranslationSerializer(many=True) - category_id = serializers.CharField() class Meta: fields = "__all__" @@ -293,10 +292,12 @@ class ProgramCategoryCapSerializer(serializers.Serializer): class ProgramCategorySerializer(serializers.Serializer): + external_name = serializers.CharField() icon = serializers.CharField() name = TranslationSerializer() description = TranslationSerializer() caps = ProgramCategoryCapSerializer(many=True) + programs = serializers.ListField(child=serializers.IntegerField()) class UrgentNeedSerializer(serializers.Serializer): @@ -314,4 +315,4 @@ class ResultsSerializer(serializers.Serializer): default_language = serializers.CharField() missing_programs = serializers.BooleanField() validations = ValidationSerializer(many=True) - program_categories = serializers.DictField(child=ProgramCategorySerializer()) + program_categories = ProgramCategorySerializer(many=True) diff --git a/screener/views.py b/screener/views.py index 648a3d8c..67cdc5fe 100644 --- a/screener/views.py +++ b/screener/views.py @@ -373,14 +373,14 @@ def sort_first(program): "low_confidence": program.low_confidence, "documents": [default_message(d.text) for d in program.documents.all()], "warning_messages": [default_message(w.message) for w in warnings], - "category_id": None if program.category_v2 is None else str(program.category_v2.id), } ) - categories = {} + category_map = {} for program in all_programs: category = program.category_v2 - if category.id in categories: + if category.id in category_map: + category_map[category.id]["programs"].append(program.id) continue CategoryCalculator = ProgramCategoryCapCalculator @@ -393,12 +393,15 @@ def sort_first(program): for cap in calculator.caps(): caps.append({"programs": cap.programs, "cap": cap.cap}) - categories[category.id] = { + category_map[category.id] = { + "id": category.external_name, "icon": category.icon, "name": default_message(category.name), "description": default_message(category.description), - "cap": caps, + "caps": caps, + "programs": [program.id], } + categories = list(category_map.values()) ProgramEligibilitySnapshot.objects.bulk_create(program_snapshots) snapshot.had_error = False From df0627f4414efcdfc495a827c7a16284e511bdb8 Mon Sep 17 00:00:00 2001 From: Caleb Date: Tue, 15 Oct 2024 13:19:45 -0600 Subject: [PATCH 05/18] format --- programs/programs/categories/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/programs/categories/base.py b/programs/programs/categories/base.py index 1b1c31fc..a187cc93 100644 --- a/programs/programs/categories/base.py +++ b/programs/programs/categories/base.py @@ -49,7 +49,7 @@ def calc_average_cap(self, cap: CategoryCap, values: list[int]): non_0_values = [v for v in values if v > 0] if len(non_0_values) == 0: - return 0 + return 0 return sum(non_0_values) / len(non_0_values) From 8cb1517d4d0c4f35f3bb02a893b58690ef08a24b Mon Sep 17 00:00:00 2001 From: Caleb Date: Tue, 15 Oct 2024 13:37:51 -0600 Subject: [PATCH 06/18] rename category_v2 to category --- ...0094_rename_category_v2_program_category.py | 18 ++++++++++++++++++ programs/models.py | 6 +++--- programs/serializers.py | 2 +- programs/views.py | 2 +- screener/views.py | 8 ++++---- 5 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 programs/migrations/0094_rename_category_v2_program_category.py diff --git a/programs/migrations/0094_rename_category_v2_program_category.py b/programs/migrations/0094_rename_category_v2_program_category.py new file mode 100644 index 00000000..3656a0c3 --- /dev/null +++ b/programs/migrations/0094_rename_category_v2_program_category.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.15 on 2024-10-15 19:32 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("programs", "0093_remove_program_category"), + ] + + operations = [ + migrations.RenameField( + model_name="program", + old_name="category_v2", + new_name="category", + ), + ] diff --git a/programs/models.py b/programs/models.py index de431599..5cfb0e74 100644 --- a/programs/models.py +++ b/programs/models.py @@ -271,7 +271,7 @@ def to_model_data(self) -> DataType: "low_confidence": program.low_confidence, "name_abbreviated": program.name_abbreviated, "documents": [d.external_name for d in program.documents.all()], - "category": program.category_v2.external_name, + "category": program.category.external_name, } def from_model_data(self, data: DataType): @@ -315,7 +315,7 @@ def from_model_data(self, data: DataType): # get program category program_category = ProgramCategory.objects.get(external_name=data["category"]) - program.category_v2 = program_category + program.category = program_category program.save() @@ -335,7 +335,7 @@ class Program(models.Model): active = models.BooleanField(blank=True, default=True) low_confidence = models.BooleanField(blank=True, null=False, default=False) fpl = models.ForeignKey(FederalPoveryLimit, related_name="fpl", blank=True, null=True, on_delete=models.SET_NULL) - category_v2 = models.ForeignKey( + category = models.ForeignKey( ProgramCategory, related_name="programs", blank=True, null=True, on_delete=models.SET_NULL ) diff --git a/programs/serializers.py b/programs/serializers.py index 45fcf31b..7901ac01 100644 --- a/programs/serializers.py +++ b/programs/serializers.py @@ -23,7 +23,7 @@ class Meta(ProgramSerializerMeta): class ProgramSerializerWithCategory(ProgramSerializer): - category = ModelTranslationSerializer(source="category_v2.name") + category = ModelTranslationSerializer(source="category.name") class Meta(ProgramSerializerMeta): fields = ("id", "name", "website_description", "category") diff --git a/programs/views.py b/programs/views.py index d5ede550..8283481a 100644 --- a/programs/views.py +++ b/programs/views.py @@ -10,7 +10,7 @@ class ProgramViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet): - queryset = Program.objects.filter(active=True, category_v2__isnull=False) + queryset = Program.objects.filter(active=True, category__isnull=False) serializer_class = ProgramSerializerWithCategory permission_classes = [permissions.IsAuthenticated] diff --git a/screener/views.py b/screener/views.py index 67cdc5fe..e07b6ba1 100644 --- a/screener/views.py +++ b/screener/views.py @@ -198,7 +198,7 @@ def eligibility_results(screen: Screen, batch=False): excluded_programs = [p.id for p in referrer.remove_programs.all()] all_programs = ( - Program.objects.filter(active=True, category_v2__isnull=False) + Program.objects.filter(active=True, category__isnull=False) .prefetch_related( "legal_status_required", "fpl", @@ -216,8 +216,8 @@ def eligibility_results(screen: Screen, batch=False): "translation_overrides", "translation_overrides__counties", *translations_prefetch_name("translation_overrides__", TranslationOverride.objects.translated_fields), - "category_v2", - *translations_prefetch_name("category_v2__", ProgramCategory.objects.translated_fields), + "category", + *translations_prefetch_name("category__", ProgramCategory.objects.translated_fields), ) .exclude(id__in=excluded_programs) ) @@ -378,7 +378,7 @@ def sort_first(program): category_map = {} for program in all_programs: - category = program.category_v2 + category = program.category if category.id in category_map: category_map[category.id]["programs"].append(program.id) continue From 70db683113f6088be9e748850ac78c51bcf42403 Mon Sep 17 00:00:00 2001 From: Caleb Date: Fri, 18 Oct 2024 14:10:06 -0600 Subject: [PATCH 07/18] change sheet name for eoc --- programs/programs/urgent_needs/urgent_need_functions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/programs/programs/urgent_needs/urgent_need_functions.py b/programs/programs/urgent_needs/urgent_need_functions.py index 21cd9267..2875515f 100644 --- a/programs/programs/urgent_needs/urgent_need_functions.py +++ b/programs/programs/urgent_needs/urgent_need_functions.py @@ -177,7 +177,7 @@ def eligible(self): class EocIncomeLimitCache(GoogleSheetsCache): default = {} sheet_id = "1T4RSc9jXRV5kzdhbK5uCQXqgtLDWt-wdh2R4JVsK33o" - range_name = "'2023'!A2:I65" + range_name = "'current'!A2:I65" def update(self): data = super().update() @@ -206,6 +206,7 @@ def eligible(self): # has rent or mortgage expense has_rent_or_mortgage = self.screen.has_expense(["rent", "mortgage"]) + print('done') return income < income_limit and has_rent_or_mortgage From 2a56e314c262bd541afb93e18b2d67bea0ced529 Mon Sep 17 00:00:00 2001 From: Caleb Date: Fri, 18 Oct 2024 14:15:46 -0600 Subject: [PATCH 08/18] remove print statement --- programs/programs/urgent_needs/urgent_need_functions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/programs/programs/urgent_needs/urgent_need_functions.py b/programs/programs/urgent_needs/urgent_need_functions.py index 2875515f..58ea08eb 100644 --- a/programs/programs/urgent_needs/urgent_need_functions.py +++ b/programs/programs/urgent_needs/urgent_need_functions.py @@ -206,7 +206,6 @@ def eligible(self): # has rent or mortgage expense has_rent_or_mortgage = self.screen.has_expense(["rent", "mortgage"]) - print('done') return income < income_limit and has_rent_or_mortgage From b0616223242f5b0f4e7aa825822a32ff6469197c Mon Sep 17 00:00:00 2001 From: Ivonne Hernandez Date: Mon, 21 Oct 2024 09:46:29 -0600 Subject: [PATCH 09/18] Move test referrer to bottom of list, move dhs under jeffco --- configuration/management/commands/add_config.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/configuration/management/commands/add_config.py b/configuration/management/commands/add_config.py index 23f14d45..c97aa187 100644 --- a/configuration/management/commands/add_config.py +++ b/configuration/management/commands/add_config.py @@ -120,6 +120,7 @@ class Command(BaseCommand): "cch": "Colorado Coalition for the Homeless", "frca": "Family Resource Center Association", "jeffcoHS": "Jeffco Human Services", + "dhs": "Denver Human Services", "gac": "Get Ahead Colorado", "bia": "Benefits in Action", "arapahoectypublichealth": "Arapahoe County Public Health", @@ -127,16 +128,15 @@ class Command(BaseCommand): "_label": "referralOptions.fircsummitresourcecenter", "_default_message": "FIRC Summit Resource Center", }, - "dhs": "Denver Human Services", "ccig": "Colorado Community Insight Group", "eaglecounty": "Eagle County", + "searchEngine": {"_label": "referralOptions.searchEngine", "_default_message": "Google or other search engine"}, + "socialMedia": {"_label": "referralOptions.socialMedia", "_default_message": "Social Media"}, + "other": {"_label": "referralOptions.other", "_default_message": "Other"}, "testOrProspect": { "_label": "referralOptions.testOrProspect", "_default_message": "Test / Prospective Partner", }, - "searchEngine": {"_label": "referralOptions.searchEngine", "_default_message": "Google or other search engine"}, - "socialMedia": {"_label": "referralOptions.socialMedia", "_default_message": "Social Media"}, - "other": {"_label": "referralOptions.other", "_default_message": "Other"}, } language_options = { From 3b1911ae44308f2dd5f3e1cad34dafb28a5ba990 Mon Sep 17 00:00:00 2001 From: Caleb Date: Mon, 21 Oct 2024 09:48:31 -0600 Subject: [PATCH 10/18] fix export script --- programs/models.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/programs/models.py b/programs/models.py index 5cfb0e74..825a38d6 100644 --- a/programs/models.py +++ b/programs/models.py @@ -250,7 +250,7 @@ class ProgramDataController(ModelDataController["Program"]): "active": bool, "low_confidence": bool, "documents": list[str], - "category": str, + "category": Optional[str], }, ) @@ -271,7 +271,7 @@ def to_model_data(self) -> DataType: "low_confidence": program.low_confidence, "name_abbreviated": program.name_abbreviated, "documents": [d.external_name for d in program.documents.all()], - "category": program.category.external_name, + "category": program.category.external_name if program.category is not None else None, } def from_model_data(self, data: DataType): @@ -314,7 +314,9 @@ def from_model_data(self, data: DataType): program.documents.set(documents) # get program category - program_category = ProgramCategory.objects.get(external_name=data["category"]) + program_category = None + if data["category"] is not None: + program_category = ProgramCategory.objects.get(external_name=data["category"]) program.category = program_category program.save() From 5fd8d8958d2c0dcda90a74395604ff62bb7f4456 Mon Sep 17 00:00:00 2001 From: Caleb Date: Thu, 24 Oct 2024 14:08:29 -0600 Subject: [PATCH 11/18] update medicaid and chp to count pregnant people as 2 for household size --- programs/programs/co/pe/member.py | 1 + programs/programs/federal/pe/member.py | 1 + .../policyengine/calculators/dependencies/member.py | 7 +++++++ 3 files changed, 9 insertions(+) diff --git a/programs/programs/co/pe/member.py b/programs/programs/co/pe/member.py index 5eb19e6a..f0ad9ed3 100644 --- a/programs/programs/co/pe/member.py +++ b/programs/programs/co/pe/member.py @@ -53,6 +53,7 @@ class Chp(PolicyEngineMembersCalculator): pe_inputs = [ dependency.member.AgeDependency, dependency.member.PregnancyDependency, + dependency.member.ExpectedChilrenPregnancyDependency, dependency.household.CoStateCode, *dependency.irs_gross_income, ] diff --git a/programs/programs/federal/pe/member.py b/programs/programs/federal/pe/member.py index 8522ab6c..bbdf24ac 100644 --- a/programs/programs/federal/pe/member.py +++ b/programs/programs/federal/pe/member.py @@ -15,6 +15,7 @@ class Wic(PolicyEngineMembersCalculator): pe_name = "wic" pe_inputs = [ dependency.member.PregnancyDependency, + dependency.member.ExpectedChilrenPregnancyDependency, dependency.member.AgeDependency, dependency.spm.SchoolMealCountableIncomeDependency, ] diff --git a/programs/programs/policyengine/calculators/dependencies/member.py b/programs/programs/policyengine/calculators/dependencies/member.py index ec71b4e7..920763af 100644 --- a/programs/programs/policyengine/calculators/dependencies/member.py +++ b/programs/programs/policyengine/calculators/dependencies/member.py @@ -17,6 +17,13 @@ def value(self): return self.member.pregnant or False +class ExpectedChilrenPregnancyDependency(Member): + field = "current_pregnancies" + + def value(self): + return 1 if self.member.pregnant else 0 + + class FullTimeCollegeStudentDependency(Member): field = "is_full_time_college_student" From 27b0c4432d5494a859a5d8fc75fd4bd97abae059 Mon Sep 17 00:00:00 2001 From: Caleb Date: Thu, 24 Oct 2024 15:09:16 -0600 Subject: [PATCH 12/18] update upb with presumptive eligibility rules --- .../co/utility_bill_pay/calculator.py | 22 ++++--------------- programs/programs/helpers.py | 2 ++ screener/views.py | 22 ++++++++++++++----- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/programs/programs/co/utility_bill_pay/calculator.py b/programs/programs/co/utility_bill_pay/calculator.py index 188378e9..39b48040 100644 --- a/programs/programs/co/utility_bill_pay/calculator.py +++ b/programs/programs/co/utility_bill_pay/calculator.py @@ -1,21 +1,9 @@ from programs.programs.calc import Eligibility, ProgramCalculator -import programs.programs.messages as messages class UtilityBillPay(ProgramCalculator): - income_limits = ( - 36_983, - 48_362, - 59_742, - 71_122, - 82_501, - 93_881, - 96_014, - 101_120, - ) - presumptive_eligibility = ("snap", "ssi", "andcs", "tanf", "wic") + presumptive_eligibility = ("snap", "ssi", "andcs", "tanf", "wic", "co_medicaid", "emergency_medicaid", "chp") amount = 350 - dependencies = ["household_size", "income_amount", "income_frequency"] def household_eligible(self, e: Eligibility): # has other programs @@ -23,12 +11,10 @@ def household_eligible(self, e: Eligibility): for benefit in UtilityBillPay.presumptive_eligibility: if self.screen.has_benefit(benefit): presumptive_eligible = True + elif benefit in self.data and self.data[benefit].eligible: + presumptive_eligible = True - # income - income = int(self.screen.calc_gross_income("yearly", ["all"])) - income_limit = UtilityBillPay.income_limits[self.screen.household_size - 1] - - e.condition(income < income_limit or presumptive_eligible, messages.income(income, income_limit)) + e.condition(presumptive_eligible) # has rent or mortgage expense has_rent_or_mortgage = self.screen.has_expense(["rent", "mortgage"]) diff --git a/programs/programs/helpers.py b/programs/programs/helpers.py index 10763ba6..74ea9f94 100644 --- a/programs/programs/helpers.py +++ b/programs/programs/helpers.py @@ -10,6 +10,8 @@ def medicaid_eligible(data: dict[str, Eligibility]): if name in data: return data[name].eligible + return False + def snap_ineligible_student(screen: Screen, member: HouseholdMember): if not member.student: diff --git a/screener/views.py b/screener/views.py index e07b6ba1..f81d205a 100644 --- a/screener/views.py +++ b/screener/views.py @@ -1,6 +1,7 @@ from typing import Optional from django.http import HttpResponse from django.shortcuts import get_object_or_404 +from rest_framework.relations import reverse from integrations.services.communications import MessageUser from programs.programs import categories from programs.programs.helpers import STATE_MEDICAID_OPTIONS @@ -251,17 +252,28 @@ def eligibility_results(screen: Screen, batch=False): pe_programs = pe_calculators.keys() def sort_first(program): - calc_first = ("tanf", "ssi", "nslp", "leap", "chp", *STATE_MEDICAID_OPTIONS) + calc_order = ( + "tanf", + "ssi", + "nslp", + "leap", + "chp", + *STATE_MEDICAID_OPTIONS, + "emergency_medicaid", + "wic", + "andcs", + ) - if program.name_abbreviated in calc_first: - return 0 - else: - return 1 + if program.name_abbreviated not in calc_order: + return len(calc_order) + + return calc_order.index(program.name_abbreviated) missing_programs = False # make certain benifits calculate first so that they can be used in other benefits all_programs = sorted(all_programs, key=sort_first) + print(all_programs) program_snapshots = [] From 2e83c5cf268eef0e77ed715c210ac2db306c8ad5 Mon Sep 17 00:00:00 2001 From: Caleb Date: Thu, 24 Oct 2024 15:15:19 -0600 Subject: [PATCH 13/18] remove print --- screener/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/screener/views.py b/screener/views.py index f81d205a..958862a6 100644 --- a/screener/views.py +++ b/screener/views.py @@ -273,7 +273,6 @@ def sort_first(program): # make certain benifits calculate first so that they can be used in other benefits all_programs = sorted(all_programs, key=sort_first) - print(all_programs) program_snapshots = [] From e2ee1d50d1500d1581f3aae5bc8ea13033023e32 Mon Sep 17 00:00:00 2001 From: Caleb Date: Fri, 25 Oct 2024 09:42:40 -0600 Subject: [PATCH 14/18] fix typo --- programs/programs/co/pe/member.py | 2 +- programs/programs/federal/pe/member.py | 2 +- .../programs/policyengine/calculators/dependencies/member.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/programs/programs/co/pe/member.py b/programs/programs/co/pe/member.py index f0ad9ed3..006b9c97 100644 --- a/programs/programs/co/pe/member.py +++ b/programs/programs/co/pe/member.py @@ -53,7 +53,7 @@ class Chp(PolicyEngineMembersCalculator): pe_inputs = [ dependency.member.AgeDependency, dependency.member.PregnancyDependency, - dependency.member.ExpectedChilrenPregnancyDependency, + dependency.member.ExpectedChildrenPregnancyDependency, dependency.household.CoStateCode, *dependency.irs_gross_income, ] diff --git a/programs/programs/federal/pe/member.py b/programs/programs/federal/pe/member.py index bbdf24ac..d29474a3 100644 --- a/programs/programs/federal/pe/member.py +++ b/programs/programs/federal/pe/member.py @@ -15,7 +15,7 @@ class Wic(PolicyEngineMembersCalculator): pe_name = "wic" pe_inputs = [ dependency.member.PregnancyDependency, - dependency.member.ExpectedChilrenPregnancyDependency, + dependency.member.ExpectedChildrenPregnancyDependency, dependency.member.AgeDependency, dependency.spm.SchoolMealCountableIncomeDependency, ] diff --git a/programs/programs/policyengine/calculators/dependencies/member.py b/programs/programs/policyengine/calculators/dependencies/member.py index 920763af..0459f160 100644 --- a/programs/programs/policyengine/calculators/dependencies/member.py +++ b/programs/programs/policyengine/calculators/dependencies/member.py @@ -17,7 +17,7 @@ def value(self): return self.member.pregnant or False -class ExpectedChilrenPregnancyDependency(Member): +class ExpectedChildrenPregnancyDependency(Member): field = "current_pregnancies" def value(self): From 17fbe154f8dac88e1f3bd25e631825f75b4941fe Mon Sep 17 00:00:00 2001 From: Caleb Date: Fri, 25 Oct 2024 09:49:44 -0600 Subject: [PATCH 15/18] update python --- runtime.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime.txt b/runtime.txt index c6f7782f..57f55885 100644 --- a/runtime.txt +++ b/runtime.txt @@ -1 +1 @@ -python-3.9.13 +python-3.9.20 From 39413ebf3b20d582fa65fd59196e673131728c35 Mon Sep 17 00:00:00 2001 From: Caleb Date: Fri, 25 Oct 2024 10:28:49 -0600 Subject: [PATCH 16/18] update ubp value --- programs/programs/co/utility_bill_pay/calculator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/programs/co/utility_bill_pay/calculator.py b/programs/programs/co/utility_bill_pay/calculator.py index 39b48040..ee8e9b36 100644 --- a/programs/programs/co/utility_bill_pay/calculator.py +++ b/programs/programs/co/utility_bill_pay/calculator.py @@ -3,7 +3,7 @@ class UtilityBillPay(ProgramCalculator): presumptive_eligibility = ("snap", "ssi", "andcs", "tanf", "wic", "co_medicaid", "emergency_medicaid", "chp") - amount = 350 + amount = 400 def household_eligible(self, e: Eligibility): # has other programs From d474326776f39d92278eee368fea3317931502f2 Mon Sep 17 00:00:00 2001 From: Caleb Date: Fri, 25 Oct 2024 11:10:06 -0600 Subject: [PATCH 17/18] change cap type for health care --- programs/programs/categories/co/caps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/programs/categories/co/caps.py b/programs/programs/categories/co/caps.py index e9bb5ea7..ce0867ba 100644 --- a/programs/programs/categories/co/caps.py +++ b/programs/programs/categories/co/caps.py @@ -6,4 +6,4 @@ class PreschoolCategoryCap(ProgramCategoryCapCalculator): class HealthCareCategoryCap(ProgramCategoryCapCalculator): - average_caps = [CategoryCap(["cfhc", "awd_medicaid", "cwd_medicaid"], member_cap=True)] + max_caps = [CategoryCap(["cfhc", "awd_medicaid", "cwd_medicaid"], member_cap=True)] From 2c10b8762af239a2f5241a80c7f1c5d3dde06713 Mon Sep 17 00:00:00 2001 From: Caleb Date: Fri, 25 Oct 2024 11:53:23 -0600 Subject: [PATCH 18/18] only show active programs on current benefits page --- programs/serializers.py | 5 ++++- programs/views.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/programs/serializers.py b/programs/serializers.py index 7901ac01..14da2fb1 100644 --- a/programs/serializers.py +++ b/programs/serializers.py @@ -30,13 +30,16 @@ class Meta(ProgramSerializerMeta): class ProgramCategorySerializer(serializers.ModelSerializer): - programs = ProgramSerializer(many=True) + programs = serializers.SerializerMethodField() name = ModelTranslationSerializer() class Meta: model = ProgramCategory fields = ("id", "name", "icon", "programs") + def get_programs(self, obj: ProgramCategory): + return ProgramSerializer(obj.programs.filter(active=True), many=True).data + class UrgentNeedAPISerializer(serializers.ModelSerializer): name = ModelTranslationSerializer() diff --git a/programs/views.py b/programs/views.py index 8283481a..fec2263b 100644 --- a/programs/views.py +++ b/programs/views.py @@ -16,7 +16,7 @@ class ProgramViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, viewsets. class ProgramCategoryViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet): - queryset = ProgramCategory.objects.filter(programs__isnull=False).distinct() + queryset = ProgramCategory.objects.filter(programs__isnull=False, programs__active=True).distinct() serializer_class = ProgramCategorySerializer permission_classes = [permissions.IsAuthenticated]