Skip to content
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

Release 5/23/2024 #381

Merged
merged 68 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
84fa0cf
added a url name
msrezaie Apr 23, 2024
d758801
gitignore for staticfiles directory
msrezaie Apr 23, 2024
964667c
added logic for splitting the labels
msrezaie Apr 23, 2024
11e3f5e
swapped inherited classes with Unfold ones
msrezaie Apr 23, 2024
2929044
added Unfold config
msrezaie Apr 23, 2024
10761a6
added custom css and js for the new UI
msrezaie Apr 23, 2024
55500ff
modified templates
msrezaie Apr 23, 2024
e96a98c
added new package requirements
msrezaie Apr 24, 2024
e084f9c
Merge branch 'dev' into feature/admin-&-translations-ui-rework
msrezaie Apr 29, 2024
22dd7a4
1036: wip
Mdemenko1 May 2, 2024
3e3f2cd
Switching to Bulma
Mdemenko1 May 7, 2024
a254bf5
bringing back original code
Mdemenko1 May 7, 2024
9bbf958
adjustimg to the PR comments
Mdemenko1 May 8, 2024
799421d
Merge pull request #351 from Gary-Community-Ventures/marianna/1036/li…
msrezaie May 8, 2024
4e8d723
removed
msrezaie May 9, 2024
2fc6f0f
swapped classes with unfold ones
msrezaie May 9, 2024
a01c724
added navigation items
msrezaie May 9, 2024
1a9af2b
added action buttons logic
msrezaie May 9, 2024
abdcb7b
added 'used_by' model field and logic
msrezaie May 9, 2024
cac7ffe
updated css styling
msrezaie May 9, 2024
e22901d
updated dropdown menu logic
msrezaie May 9, 2024
194f95a
utilized 'used_by' field logic
msrezaie May 9, 2024
533a029
overrode default admin template
msrezaie May 9, 2024
76a5389
updated translation api templates
msrezaie May 9, 2024
bb50559
Merge branch 'dev' into feature/admin-&-translations-ui-rework
msrezaie May 9, 2024
b498ae2
Merge branch 'dev' into feature/admin-&-translations-ui-rework
msrezaie May 13, 2024
231704d
clean up
msrezaie May 13, 2024
9f4b4af
changes added
msrezaie May 16, 2024
f5ff144
fix medicaid showing results
CalebPena May 20, 2024
bfef06a
add calculator dependency injection back
CalebPena May 20, 2024
d697662
add bearer token cache
CalebPena May 20, 2024
45d9af3
add ability to use private api
CalebPena May 21, 2024
d602164
fix sim base class
CalebPena May 21, 2024
cc61f5a
remove unused import
CalebPena May 21, 2024
515501e
add website description field
CalebPena May 21, 2024
3b81e25
add translation serializer that has a default message and label
CalebPena May 21, 2024
5c7ec0c
update program and urgent need list endpoints
CalebPena May 21, 2024
b3d704e
add urgent need website description
CalebPena May 21, 2024
47d1c44
add website description to serializer
CalebPena May 21, 2024
a9db4db
filter inactive programs
CalebPena May 21, 2024
e040689
add new fields to hubspot upload
CalebPena May 22, 2024
1ae24a3
add active field to translation export
CalebPena May 22, 2024
ce63094
auto translate even when the program has been manually updated
CalebPena May 22, 2024
e49b216
fix pagination warning
CalebPena May 22, 2024
44ae627
remove phone number fields because they could not be left empty
CalebPena May 22, 2024
efd8b17
try to infer the name abbr
CalebPena May 22, 2024
58b0d49
add name back to program translation admin
CalebPena May 22, 2024
c8ed6fe
fix urgent needs warning link
CalebPena May 22, 2024
d80082a
add a placeholder for empty translations
CalebPena May 22, 2024
c097002
Merge pull request #377 from Gary-Community-Ventures/refactor/small_a…
msrezaie May 23, 2024
e79ba36
kept naming consistency
msrezaie May 23, 2024
a8eff2b
restructured manytomany fields
msrezaie May 23, 2024
5c781f9
refactored to use new logic
msrezaie May 23, 2024
68f2e61
minor changes
msrezaie May 23, 2024
d48219e
Merge pull request #376 from Gary-Community-Ventures/feat/more_hubspo…
CalebPena May 23, 2024
0918a14
Merge pull request #374 from Gary-Community-Ventures/feat/programs_li…
CalebPena May 23, 2024
7f2e126
Merge pull request #373 from Gary-Community-Ventures/feat/private_pe_api
CalebPena May 23, 2024
32fd6b5
Merge pull request #370 from Gary-Community-Ventures/bug/has_medicaid
CalebPena May 23, 2024
c393220
add child development urgent needs
CalebPena May 23, 2024
5675578
update name of Home Instruction for Parents of Preschool Youngsters
CalebPena May 23, 2024
2c258dd
Merge branch 'dev' into feature/admin-&-translations-ui-rework
msrezaie May 24, 2024
45af8d3
made adjustments to new changes
msrezaie May 24, 2024
3dc368a
Merge pull request #334 from Gary-Community-Ventures/feature/admin-&-…
CalebPena May 24, 2024
c51ebd8
update comments
CalebPena May 24, 2024
e07dea7
use bool instead of num for num children
CalebPena May 24, 2024
c70a0ab
Merge pull request #382 from Gary-Community-Ventures/feat/child_dev_u…
CalebPena May 24, 2024
e93f97f
Merge branch 'main' of https://github.com/Gary-Community-Ventures/ben…
CalebPena May 24, 2024
3a06f5a
add placeholder translations for programs
CalebPena May 24, 2024
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ db.sqlite3

# Editor directories and files
.idea
.editorconfig

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

.vscode

# Static files
staticfiles/
31 changes: 15 additions & 16 deletions authentication/admin.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import GroupAdmin
from rest_framework.authtoken.models import TokenProxy
from rest_framework.authtoken.admin import TokenAdmin
from unfold.admin import ModelAdmin
from .models import User


class CustomUserAdmin(UserAdmin):
class CustomUserAdmin(ModelAdmin):
search_fields = ("email",)
ordering = ('email_or_cell', 'email')

list_display = ('email_or_cell', 'is_staff')
list_filter = ('is_staff',)

fieldsets = (
(None, {'fields': ('email_or_cell', 'password')}),
('Personal info', {'fields': ('email', 'cell')}),
('Permissions', {'fields': ('is_staff', 'tcpa_consent', 'groups')}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email_or_cell', 'password1', 'password2', 'email', 'cell', 'tcpa_consent', 'is_staff'),
}),
)
class CustomGroupAdmin(ModelAdmin, GroupAdmin):
pass

class CustomTokenAdmin(ModelAdmin, TokenAdmin):
pass

admin.site.register(User, CustomUserAdmin)
admin.site.unregister(Group)
admin.site.register(Group, CustomGroupAdmin)
admin.site.unregister(TokenProxy)
admin.site.register(TokenProxy, CustomTokenAdmin)
24 changes: 10 additions & 14 deletions authentication/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,23 @@
from rest_framework import viewsets, permissions, mixins
from rest_framework.response import Response
from authentication.serializers import UserSerializer, UserOffersSerializer
from sentry_sdk import capture_message
from sentry_sdk import capture_exception
from integrations.services.hubspot.integration import update_send_offers_hubspot, upsert_user_hubspot
import uuid


class UserViewSet(mixins.UpdateModelMixin,
viewsets.GenericViewSet):
class UserViewSet(mixins.UpdateModelMixin, viewsets.GenericViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all().order_by('-email_or_cell')

queryset = User.objects.all().order_by("-email_or_cell")
serializer_class = UserSerializer
permission_classes = [permissions.DjangoModelPermissions]

def update(self, request, pk=None):
if pk is None:
return Response('Must have an associated screen', status=400)
return Response("Must have an associated screen", status=400)
screen = Screen.objects.get(uuid=pk)
user = screen.user
if user:
Expand All @@ -43,11 +43,8 @@ def update(self, request, pk=None):

try:
upsert_user_to_hubspot(screen, screen.user)
except Exception:
capture_message(
'HubSpot upsert failed',
level='warning',
)
except Exception as e:
capture_exception(e, level="warning")
return Response("Invalid Email", status=400)

return Response(status=204)
Expand All @@ -65,14 +62,13 @@ def upsert_user_to_hubspot(screen, user):

hubspot_id = upsert_user_hubspot(user, screen=screen)
if hubspot_id:
random_id = str(uuid.uuid4()).replace('-', '')
random_id = str(uuid.uuid4()).replace("-", "")
user.external_id = hubspot_id
user.email_or_cell = f'{hubspot_id}+{random_id}@myfriendben.org'
user.email_or_cell = f"{hubspot_id}+{random_id}@myfriendben.org"
user.first_name = None
user.last_name = None
user.cell = None
user.email = None
user.save()
else:
raise Exception('Failed to upsert user')

raise Exception("Failed to upsert user")
87 changes: 86 additions & 1 deletion benefits/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from decouple import config

from pathlib import Path
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
Expand Down Expand Up @@ -62,6 +63,13 @@
# Application definition

INSTALLED_APPS = [
"unfold",
"unfold.contrib.filters", # optional, if special filters are needed
"unfold.contrib.forms", # optional, if special form elements are needed
"unfold.contrib.import_export", # optional, if django-import-export package is used
"unfold.contrib.guardian", # optional, if django-guardian package is used
# optional, if django-simple-history package is used
"unfold.contrib.simple_history",
'authentication.apps.AuthConfig',
'corsheaders',
'screener.apps.ScreenerConfig',
Expand Down Expand Up @@ -193,7 +201,8 @@
),
'default': {
'fallbacks': ['en-us'], # defaults to PARLER_DEFAULT_LANGUAGE_CODE
'hide_untranslated': True, # the default; let .active_translations() return fallbacks too.
# the default; let .active_translations() return fallbacks too.
'hide_untranslated': True,
},
}

Expand Down Expand Up @@ -221,3 +230,79 @@
)

django_heroku.settings(locals())


# UNFOLD SETTINGS
UNFOLD = {
"SITE_HEADER": _("MFB Admin"),
"SITE_TITLE": _("MFB Admin"),
'APP_NAME': 'Benefits',
'APP_VERSION': '1.0.0',
'APP_DESCRIPTION': 'Benefits is a Django application that helps people find and apply for benefits.',
"SIDEBAR": {
"show_search": True,
"show_all_applications": True,
"navigation": [
{
"title": _("Navigation"),
"items": [
{
"title": _("Programs"),
"icon": "other_admission",
"link": reverse_lazy("admin:programs_program_changelist"),
},
{
"title": _("Urgent Needs"),
"icon": "breaking_news",
"link": reverse_lazy("admin:programs_urgentneed_changelist"),
},
{
"title": _("Navigators"),
"icon": "near_me",
"link": reverse_lazy("admin:programs_navigator_changelist"),
},
{
"title": _("Configurations"),
"icon": "tune",
"link": reverse_lazy("admin:configuration_configuration_changelist"),
},
{
"title": _("Translations"),
"icon": "translate",
"link": reverse_lazy("admin:translations_translation_changelist"),
},
]
},
{
"separator": True,
"items": [
{
"title": _("Dashboard"),
"icon": "dashboard",
"link": reverse_lazy("admin:index"),
},
{
"title": _("Translations API"),
"icon": "settings",
"link": reverse_lazy("translations_api_url"),
},
{
"title": _("Users"),
"icon": "person",
"link": reverse_lazy("admin:authentication_user_changelist"),
},
{
"title": _("Groups"),
"icon": "group",
"link": reverse_lazy("admin:auth_group_changelist"),
},
{
"title": _("Tokens"),
"icon": "key",
"link": reverse_lazy("admin:authtoken_tokenproxy_changelist"),
},
],
},
],
}
}
7 changes: 5 additions & 2 deletions configuration/admin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from .models import Configuration

class ConfigurationAdmin(admin.ModelAdmin):

class ConfigurationAdmin(ModelAdmin):
search_fields = ('name',)

admin.site.register(Configuration, ConfigurationAdmin)

admin.site.register(Configuration, ConfigurationAdmin)
84 changes: 48 additions & 36 deletions integrations/services/hubspot/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
from decouple import config
import json
import re
from authentication.models import User
from screener.models import Screen
from sentry_sdk import capture_exception, capture_message


def upsert_user_hubspot(user, screen=None):
Expand All @@ -16,18 +19,20 @@ def upsert_user_hubspot(user, screen=None):
def update_send_offers_hubspot(external_id, send_offers, send_updates):
hubspot = Hubspot()
contact = {
'ab01___send_offers': send_offers,
'ab01___send_updates': send_updates,
"ab01___send_offers": send_offers,
"ab01___send_updates": send_updates,
}
try:
hubspot.update_contact(external_id, contact)
except ApiException as e:
print(e)


class Hubspot():
class Hubspot:
MAX_HOUSEHOLD_SIZE = 8

def __init__(self):
self.api_client = HubSpot(access_token=config('HUBSPOT'))
self.api_client = HubSpot(access_token=config("HUBSPOT"))

# Hubspot has no insert or update option in their latest API, so the code
# below first attempts to create a contact. If there is already a contact
Expand All @@ -40,33 +45,30 @@ def upsert_contact(self, contact):
contact_id = api_response.id
except ApiException as e:
http_body = json.loads(e.body)
if http_body['category'] == 'CONFLICT':
print(http_body)
if http_body["category"] == "CONFLICT":
try:
contact_id = self.get_conflict_contact_id(e)
self.update_contact(contact_id, contact)
except ApiException as f:
print(f)
capture_exception(f)
return False
else:
capture_exception(e)
return False
return contact_id

def create_contact(self, user):
simple_public_object_input = SimplePublicObjectInput(
properties=user
)
simple_public_object_input = SimplePublicObjectInput(properties=user)
api_response = self.api_client.crm.contacts.basic_api.create(
simple_public_object_input=simple_public_object_input
)
return api_response

def update_contact(self, contact_id, user):
simple_public_object_input = SimplePublicObjectInput(
properties=user
)
simple_public_object_input = SimplePublicObjectInput(properties=user)
api_response = self.api_client.crm.contacts.basic_api.update(
contact_id,
simple_public_object_input=simple_public_object_input
contact_id, simple_public_object_input=simple_public_object_input
)
return api_response

Expand All @@ -83,38 +85,48 @@ def get_conflict_contact_id(self, e):
http_body = json.loads(e.body)
# strip everything out of the error message except the contact id
# https://community.hubspot.com/t5/APIs-Integrations/Contacts-v3-contact-exists-error/m-p/364629
contact_id = re.sub('[^0-9]', '', http_body['message'])
contact_id = re.sub("[^0-9]", "", http_body["message"])
return contact_id

def mfb_user_to_hubspot_contact(self, user, screen=None):
def mfb_user_to_hubspot_contact(self, user: User, screen: Screen = None):
contact = {
'email': user.email,
'firstname': user.first_name,
'lastname': user.last_name,
'phone': str(user.cell),
'benefits_screener_id': user.id,
'ab01___send_offers': user.send_offers,
'ab01___send_updates': user.send_updates,
'ab01___tcpa_consent_to_contact': user.tcpa_consent,
'hs_language': user.language_code,
'ab01___screener_id': None,
'ab01___screener_uuid': None,
'ab01___1st_mfb_completion_date': user.date_joined.date().isoformat(),
'full_name': f'{user.first_name} {user.last_name}'
"email": user.email,
"firstname": user.first_name,
"lastname": user.last_name,
"phone": str(user.cell),
"benefits_screener_id": user.id,
"ab01___send_offers": user.send_offers,
"ab01___send_updates": user.send_updates,
"ab01___tcpa_consent_to_contact": user.tcpa_consent,
"hs_language": user.language_code,
"ab01___1st_mfb_completion_date": user.date_joined.date().isoformat(),
"full_name": f"{user.first_name} {user.last_name}",
}
if screen:
contact['ab01___screener_id'] = screen.id
contact['ab01___uuid'] = str(screen.uuid)
contact["ab01___screener_id"] = screen.id
contact["ab01___uuid"] = str(screen.uuid)
contact["ab01___county"] = screen.county
contact["ab01___number_of_household_members"] = screen.household_size

members = screen.household_members.all()
if len(members) > self.MAX_HOUSEHOLD_SIZE:
capture_message(f"screen has more than {self.MAX_HOUSEHOLD_SIZE} household members", level="error")

for i, member in enumerate(members):
if i >= self.MAX_HOUSEHOLD_SIZE:
break

contact[f"ab01___hhm{i + 1}_age"] = member.age

return contact

def format_email_new_benefit(self, user, num_benefits, value_benefits):
contact = {
'id': user.external_id,
'properties': {
'ab01___number_of_new_benefits': int(num_benefits),
'ab01___new_benefit_total_value': int(value_benefits),
}
"id": user.external_id,
"properties": {
"ab01___number_of_new_benefits": int(num_benefits),
"ab01___new_benefit_total_value": int(value_benefits),
},
}

return contact
Loading