diff --git a/backend/python/app/models/child.py b/backend/python/app/models/child.py index 930264645..7e1ca081f 100644 --- a/backend/python/app/models/child.py +++ b/backend/python/app/models/child.py @@ -1,5 +1,6 @@ from . import db from .base_mixin import BaseMixin +from .child_providers import child_providers from .children_child_behaviors import children_child_behaviors @@ -20,6 +21,9 @@ class Child(db.Model, BaseMixin): intake = db.relationship("Intake") daytime_contact = db.relationship("DaytimeContact") behaviors = db.relationship("ChildBehavior", secondary=children_child_behaviors) + providers = db.relationship( + "Provider", backref="children", secondary=child_providers + ) visit_cadences = db.relationship( "VisitCadence", backref="associated_child", cascade="all, delete" ) diff --git a/backend/python/app/models/child_providers.py b/backend/python/app/models/child_providers.py new file mode 100644 index 000000000..8bed91c39 --- /dev/null +++ b/backend/python/app/models/child_providers.py @@ -0,0 +1,8 @@ +from . import db + +child_providers = db.Table( + "child_providers", + db.metadata, + db.Column("child_id", db.ForeignKey("children.id")), + db.Column("providers_id", db.ForeignKey("providers.id")), +) diff --git a/backend/python/app/models/provider.py b/backend/python/app/models/provider.py index fb90fbcb3..927be0e0d 100644 --- a/backend/python/app/models/provider.py +++ b/backend/python/app/models/provider.py @@ -6,6 +6,7 @@ class Provider(db.Model, BaseMixin): __tablename__ = "providers" id = db.Column(db.Integer, primary_key=True) + intake_id = db.Column(db.Integer, db.ForeignKey("intakes.id"), nullable=True) name = db.Column(db.String, nullable=False) file_number = db.Column(db.String, nullable=False) primary_phone_number = db.Column(db.String, nullable=False) @@ -14,5 +15,3 @@ class Provider(db.Model, BaseMixin): address = db.Column(db.String, nullable=False) relationship_to_child = db.Column(db.String, nullable=False) additional_contact_notes = db.Column(db.String, nullable=True) - child_id = db.Column(db.Integer, db.ForeignKey("children.id"), nullable=False) - child = db.relationship("Child", backref="providers") diff --git a/backend/python/app/resources/provider_dto.py b/backend/python/app/resources/provider_dto.py index 132c4e153..8e211883c 100644 --- a/backend/python/app/resources/provider_dto.py +++ b/backend/python/app/resources/provider_dto.py @@ -4,6 +4,7 @@ class ProviderDTO: def __init__(self, **kwargs): self.id = kwargs.get("id") + self.intake_id = kwargs.get("intake_id") self.name = kwargs.get("name") self.file_number = kwargs.get("file_number") self.primary_phone_number = kwargs.get("primary_phone_number") @@ -12,7 +13,6 @@ def __init__(self, **kwargs): self.address = kwargs.get("address") self.relationship_to_child = kwargs.get("relationship_to_child") self.additional_contact_notes = kwargs.get("additional_contact_notes") - self.child_id = kwargs.get("child_id") class CreateProviderDTO(ProviderDTO): @@ -34,8 +34,6 @@ def validate(self): or not type(self.relationship_to_child) == str ): error_list.append("The relationship to child supplied is invalid") - if not self.child_id or not type(self.child_id) == int: - error_list.append("The child id supplied is invalid") # optional fields if self.secondary_phone_number and not type(self.secondary_phone_number) == str: diff --git a/backend/python/app/rest/intake_routes.py b/backend/python/app/rest/intake_routes.py index 0356183b4..979669306 100644 --- a/backend/python/app/rest/intake_routes.py +++ b/backend/python/app/rest/intake_routes.py @@ -74,7 +74,7 @@ def get_all_intakes(): just_children = child_service.get_children_by_intake_id(intake.id) new_children = [] for child in just_children: - providers = provider_service.get_providers_by_child_id(child.id) + providers = provider_service.get_providers_by_child(child.id) child_info = { "name": f"{child.first_name} {child.last_name}", "dateOfBirth": child.date_of_birth, @@ -325,7 +325,10 @@ def run_undos(): run_undos() return jsonify(error), 400 + children_providers = {} + all_providers = [] children = request.json["children"] + # children for child in children: # daytime contact daytimeContact = child["daytimeContact"] @@ -369,29 +372,15 @@ def run_undos(): return jsonify(error), 400 # provider - providers = child["provider"] - for provider in providers: - provider_obj = { - "name": provider["name"], - "file_number": provider["fileNumber"], - "primary_phone_number": provider["primaryPhoneNumber"], - "secondary_phone_number": provider["secondaryPhoneNumber"], - "email": provider["email"], - "address": provider["address"], - "relationship_to_child": provider["relationshipToChild"], - "additional_contact_notes": provider["additionalContactNotes"], - "child_id": child_response.id, - } - try: - provider_response = provider_service.create_new_provider( - CreateProviderDTO(**provider_obj) - ) - undos.append( - (provider_service, "delete_provider", provider_response.id) - ) - except Exception as error: - run_undos() - return jsonify(error), 400 + providers_by_child = child["provider"] + for provider in providers_by_child: + if provider.providerId in children_providers: + children_providers[provider["providerId"]].append(child_response.id) + else: + children_providers[provider["providerId"]] = [child_response.id] + + if not any(x.providerId == provider.providerId for x in all_providers): + all_providers.append(provider) # concerns concerns = child["childInfo"]["concerns"] @@ -415,6 +404,30 @@ def run_undos(): run_undos() return jsonify(error), 400 + # create providers + for provider in all_providers: + provider_obj = { + "name": provider["name"], + "intake_id": new_intake.id, + "file_number": provider["fileNumber"], + "primary_phone_number": provider["primaryPhoneNumber"], + "secondary_phone_number": provider["secondaryPhoneNumber"], + "email": provider["email"], + "address": provider["address"], + "relationship_to_child": provider["relationshipToChild"], + "additional_contact_notes": provider["additionalContactNotes"], + } + + try: + provider_response = provider_service.create_new_provider( + CreateProviderDTO(**provider_obj), + children_providers[provider["providerId"]], + ) + undos.append((provider_service, "delete_provider", provider_response.id)) + except Exception as error: + run_undos() + return jsonify(error), 400 + return jsonify(new_intake.__dict__), 201 diff --git a/backend/python/app/services/implementations/child_service.py b/backend/python/app/services/implementations/child_service.py index ed48279b6..64643585b 100644 --- a/backend/python/app/services/implementations/child_service.py +++ b/backend/python/app/services/implementations/child_service.py @@ -1,5 +1,6 @@ from ...models import db from ...models.child import Child +from ...models.provider import Provider from ...resources.child_dto import ChildDTO, CreateChildDTO from ..interfaces.child_service import IChildService @@ -59,3 +60,12 @@ def get_children_by_intake_id(self, intake_id): except Exception as error: self.logger.error(str(error)) raise error + + def get_children_by_provider(self, provider_id): + try: + provider = Provider.query.filter_by(id=provider_id).first() + children_dto = [ChildDTO(**child.to_dict()) for child in provider.children] + return children_dto + except Exception as error: + self.logger.error(str(error)) + raise error diff --git a/backend/python/app/services/implementations/provider_service.py b/backend/python/app/services/implementations/provider_service.py index 599875d52..1d2606b9e 100644 --- a/backend/python/app/services/implementations/provider_service.py +++ b/backend/python/app/services/implementations/provider_service.py @@ -1,6 +1,7 @@ from ...models import db from ...models.child import Child from ...models.provider import Provider +from ...resources.child_dto import ChildDTO from ...resources.provider_dto import CreateProviderDTO, ProviderDTO from ..interfaces.provider_service import IProviderService @@ -20,7 +21,7 @@ def get_all_providers(self): self.logger.error(str(error)) raise error - def create_new_provider(self, provider): + def create_new_provider(self, provider, children_ids=[]): try: if not provider: raise Exception( @@ -33,6 +34,11 @@ def create_new_provider(self, provider): raise Exception(error_list) new_provider_entry = Provider(**provider.__dict__) + + for child_id in children_ids: + child = Child.query.filter_by(id=child_id) + new_provider_entry.children.append(child) + db.session.add(new_provider_entry) db.session.commit() @@ -53,13 +59,13 @@ def delete_provider(self, provider_id): db.session.rollback() raise error - def get_providers_by_child_id(self, child_id): + def get_providers_by_child(self, child_id): try: - providers = Provider.query.filter_by(child_id=child_id) - providers_dto = [ - ProviderDTO(**provider.to_dict()) for provider in providers + child = Child.query.filter_by(id=child_id).first() + provider_dto = [ + ProviderDTO(**provider.to_dict()) for provider in child.providers ] - return providers_dto + return provider_dto except Exception as error: self.logger.error(str(error)) raise error diff --git a/backend/python/app/services/interfaces/child_service.py b/backend/python/app/services/interfaces/child_service.py index f058c70c1..dc519fa87 100644 --- a/backend/python/app/services/interfaces/child_service.py +++ b/backend/python/app/services/interfaces/child_service.py @@ -44,3 +44,14 @@ def get_children_by_intake_id(self, intake_id): :raises Exception: if an error occurs in the database layer """ pass + + @abstractmethod + def get_children_by_provider(self, id): + """Returns all children associated with a provider's ID + :param id: the ID of the provider + :type id: int + :return: list of ChildDTO + :rtype: list of ChildDTO + :raises Exception: if an error occurs at the database level + """ + pass diff --git a/backend/python/app/services/interfaces/provider_service.py b/backend/python/app/services/interfaces/provider_service.py index bf5716f0e..2ce59f564 100644 --- a/backend/python/app/services/interfaces/provider_service.py +++ b/backend/python/app/services/interfaces/provider_service.py @@ -33,7 +33,7 @@ def delete_provider(self, provider_id): pass @abstractmethod - def get_providers_by_child_id(self, child_id): + def get_providers_by_child(self, child_id): """Returns all providers associated with a child's ID :param child_id: the ID of the child :type child_id: int diff --git a/backend/python/migrations/versions/2023-12-05_142c528373ea_change_child_provider_relationship.py b/backend/python/migrations/versions/2023-12-05_142c528373ea_change_child_provider_relationship.py new file mode 100644 index 000000000..6be923d5b --- /dev/null +++ b/backend/python/migrations/versions/2023-12-05_142c528373ea_change_child_provider_relationship.py @@ -0,0 +1,52 @@ +"""Change child provider relationship + +Revision ID: 142c528373ea +Revises: db7c2f31d3ea +Create Date: 2023-12-05 00:58:54.363918 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "142c528373ea" +down_revision = "db7c2f31d3ea" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "child_providers", + sa.Column("child_id", sa.Integer(), nullable=True), + sa.Column("providers_id", sa.Integer(), nullable=True), + sa.ForeignKeyConstraint( + ["child_id"], + ["children.id"], + ), + sa.ForeignKeyConstraint( + ["providers_id"], + ["providers.id"], + ), + ) + op.add_column("providers", sa.Column("intake_id", sa.Integer(), nullable=True)) + op.drop_constraint("providers_child_id_fkey", "providers", type_="foreignkey") + op.create_foreign_key(None, "providers", "intakes", ["intake_id"], ["id"]) + op.drop_column("providers", "child_id") + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "providers", + sa.Column("child_id", sa.INTEGER(), autoincrement=False, nullable=False), + ) + op.drop_constraint(None, "providers", type_="foreignkey") + op.create_foreign_key( + "providers_child_id_fkey", "providers", "children", ["child_id"], ["id"] + ) + op.drop_column("providers", "intake_id") + op.drop_table("child_providers") + # ### end Alembic commands ### diff --git a/backend/python/tests/functional/test_provider_service.py b/backend/python/tests/functional/test_provider_service.py index 10c8286e5..9ab506f11 100644 --- a/backend/python/tests/functional/test_provider_service.py +++ b/backend/python/tests/functional/test_provider_service.py @@ -41,7 +41,6 @@ "secondary_phone_number": "1234567890", "address": "123 Main St", "relationship_to_child": "Mother", - "child_id": 1, } @@ -91,7 +90,6 @@ def test_create_new_provider_valid(provider_service): secondary_phone_number="0987654321", address="321 Main St", relationship_to_child="Father", - child_id=1, ) provider_instance = provider_service.create_new_provider(param) @@ -113,7 +111,6 @@ def test_invalid_arg(provider_service): secondary_phone_number="0987654321", address="321 Main St", relationship_to_child="Father", - child_id=1, ) with pytest.raises(Exception): provider_service.create_new_provider(param) diff --git a/backend/python/tools/db_seed.py b/backend/python/tools/db_seed.py index d25e8912f..d3b983994 100644 --- a/backend/python/tools/db_seed.py +++ b/backend/python/tools/db_seed.py @@ -111,14 +111,11 @@ def insert_test_data(): insert_values(db, "other_permitted_individuals", ("name", "phone_number", "relationship_to_child", "notes", "intake_id"), value) - # Providers - values = [ - ('Provider One', '111', '555-555-5555', '777-777-7777', 'provider1@mail.com', 'address', 'KINSHIP_PROVIDER', 'NULL', 1), - ('Provider Two', '222', '777-777-7777', '555-555-5555', 'provider@mail.com', 'address', 'KINSHIP_PROVIDER', 'NULL', 1) - ] + # Providers + values = [(1, "Karen Namen", "123123", "primary phone", "secondary phone", "test@gmail.com", "address", "FOSTER CAREGIVER")] for value in values: - insert_values(db, "providers", ("name", "file_number", "primary_phone_number", "secondary_phone_number", "email", "address", "relationship_to_child", "additional_contact_notes", "child_id"), value) + insert_values(db, "providers", ("intake_id", "name", "file_number", "primary_phone_number", "secondary_phone_number", "email", "address", "relationship_to_child", "additional_contact_notes"), value) values_sheets = [(999, 'Zhang' , 'csw', 'cpw', 'fcc'), @@ -131,7 +128,7 @@ def insert_test_data(): values_records = [ (999, 999, 'FULL', '2023-01-01', '08:00:00', '17:00:00', 'Location Value', 'PRESENT', 'MOM', 30, 15, 10, 1, 'Comments Value'), (1000, 1000, 'PARTIAL', '2023-01-02', '09:00:00', '18:00:00', 'Location Value', 'NO_SHOW', 'DAD', 45, 20, 12, 2, 'Comments Value'), -] + ] for value in values_records: insert_values(db, "attendance_records", ("id", "attendance_sheet_id", "supervision", "date", "start_time", "end_time", "location", "attendance", "attending_family", "staff_transport_time_min", "driver_transport_time_min", "foster_parent_transport_time_min", "child_family_support_worker_id", "comments"), value) diff --git a/frontend/src/components/intake/NewProviderModal.tsx b/frontend/src/components/intake/NewProviderModal.tsx index 74807fd08..81edc143b 100644 --- a/frontend/src/components/intake/NewProviderModal.tsx +++ b/frontend/src/components/intake/NewProviderModal.tsx @@ -14,6 +14,7 @@ export type ProviderDetails = { contactNotes?: string; address: string; relationship: string; + providerId: number; }; export type Providers = ProviderDetails[]; @@ -23,6 +24,7 @@ type NewProviderProps = { onClick: (newProvider: ProviderDetails) => void; onClose: () => void; provider: ProviderDetails; + providerId: number; }; const NewProviderModal = ({ @@ -30,6 +32,7 @@ const NewProviderModal = ({ onClick, onClose, provider, + providerId, }: NewProviderProps): React.ReactElement => { const [providerName, setProviderName] = useState(""); const [providerFileNo, setProviderFileNo] = useState(""); @@ -234,6 +237,7 @@ const NewProviderModal = ({ relationship: relationshipChanged ? relationship : provider.relationship, + providerId, }; onClick(newProvider); handleClose(); diff --git a/frontend/src/components/intake/child-information/ChildProviderForm.tsx b/frontend/src/components/intake/child-information/ChildProviderForm.tsx index 2bd0b2ee6..60578327a 100644 --- a/frontend/src/components/intake/child-information/ChildProviderForm.tsx +++ b/frontend/src/components/intake/child-information/ChildProviderForm.tsx @@ -78,6 +78,7 @@ const ChildProviderForm = ({ contactNotes: "", address: "", relationship: "", + providerId: 0, }; return ( @@ -110,6 +111,9 @@ const ChildProviderForm = ({ provider={ selectedIndex >= 0 ? providers[selectedIndex] : emptyProvider } + providerId={ + selectedIndex >= 0 ? selectedIndex : allProviders.length - 1 + } />