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

Restrict multiple submission from API, fixed odoo server error and display name bugs, filtered dashboards #20

Merged
merged 3 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion src/openg2p_portal_api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ class Initializer(Initializer):
def initialize(self, **kwargs):
super().initialize()
# Initialize all Services, Controllers, any utils here.
PartnerService()
MembershipService()
ProgramService()
FormService()
PartnerService()

DiscoveryController().post_init()
ProgramController().post_init()
Expand Down
23 changes: 11 additions & 12 deletions src/openg2p_portal_api/controllers/auth_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from openg2p_fastapi_auth.controllers.auth_controller import AuthController
from openg2p_fastapi_auth.models.orm.login_provider import LoginProvider
from openg2p_fastapi_common.errors.http_exceptions import UnauthorizedError
from sqlalchemy.exc import IntegrityError

from ..config import Settings
from ..dependencies import JwtBearerAuth
Expand All @@ -16,7 +17,7 @@
PartnerPhoneNoORM,
)
from ..models.orm.reg_id_orm import RegIDORM, RegIDTypeORM
from ..models.profile import Profile
from ..models.profile import GetProfile, UpdateProfile
from ..services.partner_service import PartnerService

_config = Settings.get_config()
Expand All @@ -37,13 +38,12 @@ def __init__(self, **kwargs):
self.router.add_api_route(
"/profile",
self.get_profile,
responses={200: {"model": Profile}},
responses={200: {"model": GetProfile}},
methods=["GET"],
)
self.router.add_api_route(
"/profile",
self.update_profile,
responses={200: {"model": Profile}},
methods=["PUT"],
)

Expand Down Expand Up @@ -125,7 +125,7 @@ async def get_profile(
}
)

return Profile(
return GetProfile(
id=partner_data.id,
ids=partner_ids,
email=partner_data.email,
Expand All @@ -142,7 +142,7 @@ async def get_profile(

async def update_profile(
self,
userdata: Profile,
userdata: UpdateProfile,
auth: Annotated[AuthCredentials, Depends(JwtBearerAuth())],
):
"""
Expand All @@ -158,14 +158,13 @@ async def update_profile(

Confirmation or updated profile data after the update.
"""
if userdata.id is None or userdata.id != auth.partner_id:
raise UnauthorizedError(
message="Unauthorized. Partner Not Found in Registry."
try:
await self.partner_service.update_partner_info(
auth.partner_id, userdata.model_dump(exclude={"id"})
)

return await self.partner_service.update_partner_data(
auth.partner_id, userdata.model_dump(exclude={"id"})
)
except IntegrityError:
return "Could not add to registrant to program!!"
return "Updated the partner info"

async def get_login_providers_db(self) -> List[LoginProvider]:
return [
Expand Down
2 changes: 1 addition & 1 deletion src/openg2p_portal_api/models/orm/partner_orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class PartnerORM(BaseORMModelWithId):
birth_place: Mapped[str] = mapped_column()
phone: Mapped[str] = mapped_column()
company_id: Mapped[Optional[int]] = mapped_column()
registration_date: Mapped[date] = mapped_column(Date())
registration_date: Mapped[date] = mapped_column(Date(), default=date.today)
reg_ids: Mapped[Optional[List[RegIDORM]]] = relationship(back_populates="partner")

create_date: Mapped[datetime] = mapped_column(DateTime(), default=datetime.utcnow)
Expand Down
12 changes: 11 additions & 1 deletion src/openg2p_portal_api/models/orm/program_orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class ProgramORM(BaseORMModelWithId):
description: Mapped[str] = mapped_column(String())
state: Mapped[str] = mapped_column(String())
is_multiple_form_submission: Mapped[str] = mapped_column()
is_reimbursement_program: Mapped[bool] = mapped_column()
active: Mapped[bool] = mapped_column()

membership: Mapped[Optional[List["ProgramMembershipORM"]]] = relationship(
back_populates="program"
Expand All @@ -40,7 +42,15 @@ async def get_all_programs(cls) -> List["ProgramORM"]:
async with async_session_maker() as session:
stmt = (
select(cls)
.filter(cls.state != "inactive", cls.state != "ended")
.filter(
cls.state != "inactive",
cls.state != "ended",
cls.active.is_(True),
or_(
cls.is_reimbursement_program.is_(False),
cls.is_reimbursement_program.is_(None),
),
)
.options(selectinload(cls.membership))
.order_by(cls.id.asc())
)
Expand Down
17 changes: 11 additions & 6 deletions src/openg2p_portal_api/models/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,22 @@ class PhoneNumber(BaseModel):


class Profile(BaseModel):
model_config = ConfigDict()

id: Optional[int] = None
ids: List[RegistrantID]
ids: Optional[List[RegistrantID]] = None
email: Optional[str] = None
gender: Optional[str] = None
# address: Optional[dict] = {}
bank_ids: List[BankDetails]
bank_ids: Optional[List[BankDetails]] = None
addl_name: Optional[str] = None
given_name: Optional[str] = None
family_name: Optional[str] = None
birthdate: Optional[date] = None
phone_numbers: List[PhoneNumber]
phone_numbers: Optional[List[PhoneNumber]] = None
birth_place: Optional[str] = None


class UpdateProfile(Profile):
pass


class GetProfile(Profile):
id: Optional[int] = None
2 changes: 2 additions & 0 deletions src/openg2p_portal_api/models/program.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class Program(ProgramBase):
is_portal_form_mapped: Optional[bool] = False
is_multiple_form_submission: Optional[bool] = False
last_application_status: Optional[str] = None
is_reimbursement_program: Optional[bool] = None
active: Optional[bool] = None

@validator("is_portal_form_mapped", pre=True, always=True)
def is_program_form_mapped(cls, v, values):
Expand Down
79 changes: 34 additions & 45 deletions src/openg2p_portal_api/services/form_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@

from openg2p_fastapi_common.context import dbengine
from openg2p_fastapi_common.service import BaseService
from sqlalchemy import text
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import async_sessionmaker

from ..context import partner_fields_cache
from ..models.form import ProgramForm
from ..models.orm.partner_orm import PartnerORM
from ..models.orm.program_orm import ProgramORM
from ..models.orm.program_registrant_info_orm import (
ProgramRegistrantInfoDraftORM,
ProgramRegistrantInfoORM,
)
from .membership_service import MembershipService
from .partner_service import PartnerService


class FormService(BaseService):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.membership_service = MembershipService.get_component()
self.partner_service = PartnerService.get_component()

async def get_program_form(self, program_id: int, registrant_id: int):
response_dict = {}
Expand Down Expand Up @@ -96,9 +96,26 @@ async def submit_application_form(
):
async_session_maker = async_sessionmaker(dbengine.get())
async with async_session_maker() as session:
program_membership_id = await self.membership_service.check_and_create_mem(
program_id, registrant_id
existing_application = await session.execute(
select(ProgramRegistrantInfoORM).filter(
ProgramRegistrantInfoORM.program_id == program_id,
ProgramRegistrantInfoORM.registrant_id == registrant_id,
ProgramRegistrantInfoORM.state.in_(
["active", "inprogress", "applied"]
),
)
)
if existing_application.scalars().first():
return "Error: There is already an active or in-progress application for this program."

try:
program_membership_id = (
await self.membership_service.check_and_create_mem(
program_id, registrant_id
)
)
except ValueError as e:
return str(e)
get_draft_reg_info = (
await ProgramRegistrantInfoDraftORM.get_draft_reg_info_by_id(
program_id, registrant_id
Expand All @@ -107,17 +124,12 @@ async def submit_application_form(
application_id = self._compute_application_id()
create_date = datetime.now()

partner_info = await PartnerORM.get_partner_data(registrant_id)
if partner_info:
updated_partner_info = await self.update_partner_info(
session, partner_info, form_data.program_registrant_info
)

cleaned_program_registrant_info = self.clean_program_registrant_info(
form_data.program_registrant_info, updated_partner_info
)
else:
cleaned_program_registrant_info = form_data.program_registrant_info
updated_partner_info = await self.partner_service.update_partner_info(
registrant_id, form_data.program_registrant_info, session=session
)
cleaned_program_registrant_info = self.clean_program_registrant_info(
form_data.program_registrant_info, updated_partner_info
)

program_registrant_info = ProgramRegistrantInfoORM(
program_id=program_id,
Expand Down Expand Up @@ -151,38 +163,15 @@ def _compute_application_id(self):
random_number = str(random.randint(1, 100000))
return d + m + y + random_number.zfill(5)

async def update_partner_info(self, session, partner_info, program_registrant_info):
# Update partner_info with fields from program_registrant_info
updated_fields = {}
partner_fields = await self.get_partner_fields()
for key, value in program_registrant_info.items():
# if hasattr(partner_info, key) and getattr(partner_info, key) != value:
if key in partner_fields:
updated_fields[key] = value
if updated_fields:
set_clause = ", ".join(
[f"{key} = '{value}'" for key, value in updated_fields.items()]
)
await session.execute(
text(
f"UPDATE {PartnerORM.__tablename__} SET {set_clause} WHERE id='{partner_info.id}'"
)
)
return updated_fields

def clean_program_registrant_info(self, program_registrant_info, updated_fields):
def clean_program_registrant_info(
self, program_registrant_info, updated_partner_fields
):
# Remove updated fields from program_registrant_info
if not updated_partner_fields:
return program_registrant_info
cleaned_info = {
key: value
for key, value in program_registrant_info.items()
if key not in updated_fields
if key not in updated_partner_fields
}
return cleaned_info

async def get_partner_fields(self):
partner_field = partner_fields_cache.get()
if partner_field:
return partner_field
partner_field = await PartnerORM.get_partner_fields()
partner_fields_cache.set(partner_field)
return partner_field
3 changes: 2 additions & 1 deletion src/openg2p_portal_api/services/membership_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class MembershipService(BaseService):
def __init__(self, **kwargs):
super().__init__(**kwargs)

async def check_and_create_mem(self, programid: int, partnerid: int):
async def check_and_create_mem(self, programid: int, partnerid: int) -> int:
async_session_maker = async_sessionmaker(dbengine.get())
async with async_session_maker() as session:
membership = await ProgramMembershipORM.get_membership_by_id(
Expand All @@ -25,6 +25,7 @@ async def check_and_create_mem(self, programid: int, partnerid: int):
try:
session.add(membership)
await session.commit()
await session.refresh(membership)
except IntegrityError:
return "Could not add to registrant to program!!"

Expand Down
Loading
Loading