first commit

This commit is contained in:
2024-11-07 17:44:29 +03:00
commit 643d6d8f65
247 changed files with 420800 additions and 0 deletions

View File

@@ -0,0 +1,220 @@
import typing
from fastapi import status, HTTPException
from fastapi.responses import JSONResponse
from databases import (
Build,
BuildDecisionBook,
Companies,
OccupantTypes,
)
from validations import (
InsertDecisionBook,
ListOptions,
)
from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject
from api_validations.core_response import return_json_response_from_alchemy
from api_library.date_time_actions.date_functions import DateTimeLocal
class DecisionBookListEventMethods(MethodToEvent):
event_type = "SELECT"
__event_keys__ = {
"5c10d6ae-2aee-4243-a7c3-94826d028d13": "building_decision_book_list",
}
@classmethod
def building_decision_book_list(
cls,
list_options: ListOptions,
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
):
records = []
if isinstance(token_dict, EmployeeTokenObject):
build_id_list_query = Build.select_action(
employee_id=token_dict.selected_company.employee_id
)
build_id_list = build_id_list_query.all()
if not build_id_list:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"No building is match with given Employee UUID {token_dict.selected_company.employee_uu_id}",
)
records = BuildDecisionBook.filter_active(
BuildDecisionBook.build_id.in_([build.id for build in build_id_list]),
*BuildDecisionBook.get_smart_query(list_options.query),
)
elif isinstance(token_dict, OccupantTokenObject):
records = BuildDecisionBook.filter_active(
BuildDecisionBook.build_id == token_dict.selected_occupant.build_id,
*BuildDecisionBook.get_smart_query(list_options.query),
)
return return_json_response_from_alchemy(
response=records, pagination=list_options
)
class DecisionBookCreateEventMethods(MethodToEvent):
event_type = "CREATE"
__event_keys__ = {
"0a68cb44-271a-4829-81f6-cd99a5f326b4": "building_decision_book_create",
}
@classmethod
def building_decision_book_create(
cls,
data: InsertDecisionBook,
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
):
data_dict = data.excluded_dump()
if isinstance(token_dict, EmployeeTokenObject):
Build.pre_query = Build.select_action(
employee_id=token_dict.selected_company.employee_id
)
build = Build.filter_active(
Build.uu_id == data.build_uu_id,
)
if not build.data:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Emloyee UUID {token_dict.selected_company.employee_uu_id} has no build with given UUID {data_dict.get('build_uu_id')}",
)
data_dict["build_id"] = build.data[0].id
if data.resp_company_uu_id:
Companies.pre_query = Companies.select_action(
duty_id_list=[
token_dict.selected_company.duty_id,
token_dict.selected_company.bulk_duties_id,
]
)
company = Companies.filter_active(
Companies.uu_id == data.resp_company_uu_id
)
if not company.data:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Duty UUID {token_dict.selected_company.duty_uu_id} has no company with given UUID {data_dict.get('resp_company_uu_id')}",
)
data_dict["resp_company_id"] = company.data[0].id
data_dict["resp_company_uu_id"] = str(company.data[0].uu_id)
build_object = build.data[0]
decision_period_date = DateTimeLocal.get(build_object.decision_period_date)
data_dict["expiry_starts"] = DateTimeLocal.get(
DateTimeLocal.now().date().year,
int(decision_period_date.date().month),
int(decision_period_date.date().day),
)
data_dict["expiry_ends"] = str(
data_dict["expiry_starts"].shift(years=1, days=-1)
)
data_dict["expiry_starts"] = str(data_dict["expiry_starts"])
build_decision_book = BuildDecisionBook.find_or_create(**data_dict)
return JSONResponse(
status_code=status.HTTP_200_OK,
content=dict(
message="Decision Book has created",
completed=True,
data=build_decision_book.get_dict(),
),
)
elif isinstance(token_dict, OccupantTokenObject):
occupant_manager = OccupantTypes.find_one(
occupant_category_type="BU", occupant_code="BU-MNG"
)
if not token_dict.selected_occupant.occupant_type_id == occupant_manager.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Only Build Manager can create decision book",
)
occupant_build = Build.find_one(id=token_dict.selected_occupant.build_id)
occupant_company = Companies.find_one(
id=token_dict.selected_occupant.responsible_company_id
)
data_dict["build_id"] = occupant_build.id
data_dict["build_uu_id"] = str(occupant_build.uu_id)
data_dict["resp_company_id"] = occupant_company.id
data_dict["resp_company_uu_id"] = str(occupant_company.uu_id)
decision_period_date = DateTimeLocal.get(
occupant_build.decision_period_date
)
data_dict["expiry_starts"] = DateTimeLocal.get(
DateTimeLocal.now().date().year,
int(decision_period_date.date().month),
int(decision_period_date.date().day),
)
data_dict["expiry_ends"] = str(
data_dict["expiry_starts"].shift(years=1, days=-1)
)
data_dict["expiry_starts"] = str(data_dict["expiry_starts"])
build_decision_book = BuildDecisionBook.find_or_create(**data_dict)
return JSONResponse(
status_code=status.HTTP_200_OK,
content=dict(
message="Decision Book has created",
completed=True,
data=build_decision_book.get_dict(),
),
)
class DecisionBookUpdateEventMethods(MethodToEvent):
event_type = "UPDATE"
__event_keys__ = {
"6bc7035c-3b53-4c0a-8cc9-1ec9c6af1e29": "building_decision_book_update",
}
@classmethod
def building_decision_book_update(cls, data: InsertDecisionBook, token_dict: dict):
return
class DecisionBookPatchEventMethods(MethodToEvent):
event_type = "PATCH"
__event_keys__ = {
"7b58ed84-9a65-4588-994d-30df8366b050": "building_decision_book_patch",
}
@classmethod
def building_decision_book_patch(cls, data: InsertDecisionBook, token_dict: dict):
return
class DecisionBookApprovalEventMethods(MethodToEvent):
event_type = "UPDATE"
__event_keys__ = {
"fc745142-3437-4ca2-89fa-c5a3e2b5c6c2": "building_decision_book_approval",
}
@classmethod
def building_decision_book_approval(cls, data, token_dict):
return
DecisionBookListEventMethod = DecisionBookListEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/list")
)
DecisionBookCreateEventMethod = DecisionBookCreateEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/create")
)
DecisionBookUpdateEventMethod = DecisionBookUpdateEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/update")
)
DecisionBookPatchEventMethod = DecisionBookPatchEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/patch")
)
DecisionBookApprovalEventMethod = DecisionBookApprovalEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/approval")
)

View File

@@ -0,0 +1,473 @@
import typing
from fastapi import status, HTTPException
from fastapi.responses import JSONResponse
from databases import (
Build,
BuildParts,
BuildDecisionBook,
BuildDecisionBookItems,
BuildDecisionBookPerson,
BuildDecisionBookPayments,
BuildDecisionBookProjects,
BuildDecisionBookProjectPerson,
ApiEnumDropdown,
OccupantTypes,
Companies,
BuildLivingSpace,
)
from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject
from api_validations.core_response import return_json_response_from_alchemy
from api_library.date_time_actions.date_functions import DateTimeLocal
from validations import (
InsertBuildDecisionBookItems,
ListDecisionBook,
)
class DecisionBookDecisionBookItemsListEventMethods(MethodToEvent):
event_type = "SELECT"
__event_keys__ = {
"eb36de59-8268-4d96-80b6-5d01c12bf0b1": "building_decision_book_items_list",
}
@classmethod
def building_decision_book_items_list(
cls,
data: ListDecisionBook,
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
):
decision_book = BuildDecisionBook.find_one(uu_id=data.build_decision_book_uu_id)
if not decision_book:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"No Decision Book is match with given UUID {data.build_decision_book_uu_id}",
)
if isinstance(token_dict, EmployeeTokenObject):
Build.pre_query = Build.select_action(
employee_id=token_dict.selected_company.employee_id,
filter_expr=[
Build.uu_id == decision_book.build_decision_book_uu_id,
],
)
reachable_building = Build.filter_active()
if not reachable_building.data:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"No building is match with given Employee UUID {token_dict.selected_company.employee_uu_id}",
)
Companies.pre_query = Companies.select_action(
duty_id_list=[
token_dict.selected_company.duty_id,
token_dict.selected_company.bulk_duties_id,
],
filter_expr=[Companies.id == decision_book.company_id],
)
reachable_companies = Companies.filter_active()
if not reachable_companies.data:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"No company is match with given Employee UUID {token_dict.selected_company.employee_uu_id}",
)
records = BuildDecisionBookItems.filter_active(
BuildDecisionBookItems.build_decision_book_id == decision_book.id
)
return JSONResponse(
status_code=status.HTTP_200_OK,
content=dict(
total_count=records.count,
count=len(records.data),
message=f"Decision Book Items has found from given Decision Book UUID {data.build_decision_book_uu_id}",
completed=True,
data=[record.get_dict() for record in records.data],
),
)
else:
# BuildDecisionBookItems.pre_query = BuildDecisionBookItems.select_action(
# occupant_id=token_dict.occupant_list["occupant_id"]
# )
# records = BuildDecisionBookItems.filter_active(
# *BuildDecisionBookItems.get_smart_query(list_options.query)
# )
# return return_json_response_from_alchemy(response=records, pagination=list_options)
return
class DecisionBookDecisionBookItemsCreateEventMethods(MethodToEvent):
event_type = "CREATE"
__event_keys__ = {
"dce10509-0da5-46fb-af3c-a81d54d5481c": "building_decision_book_items_create",
}
@classmethod
def iterate_over_build_parts(
cls,
build_parts_list,
payment_types,
local_date,
end_date,
unit_price,
unit_type,
book_payment_dict,
unit_price_is_fixed
):
start_date, payment_return_dict = local_date, {}
for build_part_single in build_parts_list:
local_date = start_date
while local_date.is_between(start_date, end_date, "[]"):
local_date = DateTimeLocal.find_last_day_of_month(local_date)
payment_amount = unit_price
if not unit_price_is_fixed:
unit_amount = str(build_part_single.due_part_key).replace(" ", "")
unit_amount = unit_amount.replace(str(unit_type).upper(), "")
payment_amount = abs(unit_price * float(unit_amount)) * -1
payment_amount = -1 * (abs(payment_amount) + (50 - float(abs(payment_amount)) % 50))
BuildDecisionBookPayments.create(
build_parts_id=build_part_single.id,
build_parts_uu_id=str(build_part_single.uu_id),
payment_amount=payment_amount,
payment_types_id=payment_types.id,
payment_types_uu_id=str(payment_types.uu_id),
process_date=str(local_date),
process_date_m=int(local_date.month),
process_date_y=int(local_date.year),
period_time=f"{local_date.year}-{str(local_date.month).zfill(2)}",
**book_payment_dict
)
local_date = local_date.shift(days=2)
part_key = str(build_part_single.due_part_key).upper()
if part_key not in payment_return_dict:
payment_return_dict[part_key] = payment_amount
return payment_return_dict
@classmethod
def create_payment_records_for_each_build_part(
cls,
data_info_type,
build_id,
unit_price,
unit_type,
decision_book,
decision_book_item,
unit_price_is_fixed,
currency,
debit_start_date: str = None,
debit_end_date: str = None,
):
build_parts_list = BuildParts.filter_active(
BuildParts.human_livable == True,
BuildParts.build_id == build_id,
)
book_payment_dict = dict(
payment_plan_time_periods=str(data_info_type.key),
build_decision_book_item_id=decision_book_item.id,
build_decision_book_item_uu_id=str(decision_book_item.uu_id),
is_confirmed=True,
currency=currency,
)
payment_types = ApiEnumDropdown.get_debit_search(search_debit="DT-D")
if data_info_type.key == "BDT-D":
local_date = DateTimeLocal.get(DateTimeLocal.get(decision_book.expiry_starts).date())
end_date = DateTimeLocal.get(DateTimeLocal.get(decision_book.expiry_ends).date())
return cls.iterate_over_build_parts(
build_parts_list=build_parts_list.data,
payment_types=payment_types,
local_date=local_date,
end_date=end_date,
unit_price=unit_price,
unit_type=unit_type,
unit_price_is_fixed=unit_price_is_fixed,
book_payment_dict=book_payment_dict,
)
elif data_info_type.key == "BDT-A":
local_date = DateTimeLocal.get(DateTimeLocal.get(debit_start_date).date())
end_date = DateTimeLocal.get(DateTimeLocal.get(debit_end_date).date())
return cls.iterate_over_build_parts(
build_parts_list=build_parts_list.data,
payment_types=payment_types,
local_date=local_date,
end_date=end_date,
unit_price=unit_price,
unit_type=unit_type,
unit_price_is_fixed=unit_price_is_fixed,
book_payment_dict=book_payment_dict,
)
elif data_info_type.key == "BDT-R" or data_info_type.key == "BDT-L":
local_date = DateTimeLocal.get(DateTimeLocal.get(debit_start_date).date())
end_date = DateTimeLocal.get(DateTimeLocal.get(debit_end_date).date())
decision_date = DateTimeLocal.get(decision_book.expiry_starts).date()
meeting_date = DateTimeLocal.get(decision_book.meeting_date).date()
already_book_projects = BuildDecisionBookProjects.filter_all(
BuildDecisionBookProjects.build_decision_book_id==decision_book.id,
BuildDecisionBookProjects.project_type==f"{decision_book.decision_type}_{data_info_type.key}",
)
management_room = BuildParts.find_one(
build_id=build_id, part_no=0, active=True, is_confirmed=True
)
occupant_man = OccupantTypes.find_one(
occupant_code="MT-VPR", occupant_category_type="MT"
)
manager_living_space = BuildLivingSpace.filter_by_active(
build_parts_id=management_room.id, occupant_type=occupant_man.id,
)
if not manager_living_space.data:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"{occupant_man.occupant_description} Living Space is not found. Check manager living space and try again",
)
already_book_project_count = already_book_projects.count + 1
manager_living_space = manager_living_space.get(1)
book_project_dict = dict(
project_no=f"{data_info_type.key}_{decision_date.year}_{already_book_project_count}",
project_name=f"{str(meeting_date)}_{decision_book.decision_type} Project {already_book_projects.count + 1}",
project_start_date=str(local_date),
project_stop_date=str(end_date),
project_type=f"{decision_book.decision_type}_{data_info_type.key}",
project_note=f"Fill later",
build_decision_book_id=decision_book.id,
build_decision_book_uu_id=str(decision_book.uu_id),
build_decision_book_item_id=decision_book_item.id,
build_decision_book_item_uu_id=str(decision_book_item.uu_id),
project_response_living_space_id=manager_living_space.id,
project_response_living_space_uu_id=str(manager_living_space.uu_id),
)
book_project_created = BuildDecisionBookProjects.find_or_create(**book_project_dict)
if not book_project_created.is_found:
decision_book_item.update(
item_comment=f"{book_project_created.project_no}_{book_project_created.project_name} "
f"is assigned to {occupant_man.occupant_description}"
)
project_lead = ApiEnumDropdown.find_one(
key="PTT-LDR", enum_class="ProjectTeamTypes"
)
project_person = BuildDecisionBookProjectPerson.find_or_create(
build_decision_book_project_id=book_project_created.id,
build_decision_book_project_uu_id=str(book_project_created.uu_id),
living_space_id=manager_living_space.id,
living_space_uu_id=str(manager_living_space.uu_id),
project_team_type_id=project_lead.id,
project_team_type_uu_id=str(project_lead.uu_id),
)
elif data_info_type.key == "BDT-SF":
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="BDT-SF is not implemented yet. Check info type and try again",
)
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Payment Info Type is not valid. Check payment type and try again",
)
@classmethod
def building_decision_book_items_create(
cls,
data: InsertBuildDecisionBookItems,
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
):
if isinstance(token_dict, EmployeeTokenObject):
raise HTTPException(
status_code=status.HTTP_406_NOT_ACCEPTABLE,
detail="No employee can reach this event. An notification is send to admin about event registration",
)
elif isinstance(token_dict, OccupantTokenObject):
data_dict = data.dump()
occupant_wrt = OccupantTypes.find_one(
occupant_code="MT-WRT", occupant_category_type="MT"
)
if token_dict.selected_occupant.occupant_type_id != occupant_wrt.id:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Only WRITER can create decision book item. Check your occupant type and try again",
)
decision_book_person = BuildDecisionBookPerson.find_one(token=data.token)
decision_book = BuildDecisionBook.find_one(
id=decision_book_person.build_decision_book_id
)
BuildDecisionBookItems.check_meeting_is_valid_to_start_add_attendance(
decision_book=decision_book,
token_dict=token_dict,
)
book_items = BuildDecisionBookItems.filter_active(
BuildDecisionBookItems.build_decision_book_id == decision_book.id,
)
data_dict["item_order"] = int(book_items.count) + 1
data_dict["build_decision_book_id"] = decision_book.id
data_dict["build_decision_book_uu_id"] = str(decision_book.uu_id)
data_dict["is_confirmed"] = True
data_info_types, data_info_type = ApiEnumDropdown.due_type_search(), None
for info_type in data_info_types:
if str(info_type.uu_id) == data_dict["info_type_uu_id"]:
data_info_type = info_type
break
if not data_info_type:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Info Type is not valid. Check info type and try again",
)
if str(data_info_type.key).upper() in ["BDT-A", "BDT-R", "BDT-L", "BDT-SF"]:
if not data_dict["debit_start_date"] or not data_dict["debit_end_date"]:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Debit Start Date and Debit End Date is required for this payment type. "
"Check debit start date and debit end date and try again",
)
data_dict["info_type_id"] = data_info_type.id
data_dict["info_type_uu_id"] = str(data_info_type.uu_id)
unit_price, unit_type = float(data_dict["unit_price"]), str(data_dict["unit_type"])
debit_start_date, debit_end_date = data_dict["debit_start_date"], data_dict["debit_end_date"]
currency = data_dict["currency"]
del (
data_dict["token"],
data_dict["unit_price"],
data_dict["unit_type"],
data_dict["unit_price_is_fixed"],
data_dict["debit_start_date"],
data_dict["debit_end_date"],
data_dict["currency"]
)
if new_decision_book_item := BuildDecisionBookItems.find_or_create(**data_dict):
if new_decision_book_item.is_found:
return JSONResponse(
status_code=status.HTTP_200_OK,
content=dict(
message=f"Decision Book Item is already exist for given Decision Book UUID {decision_book.uu_id}",
completed=True,
data=new_decision_book_item.get_dict(),
),
)
if created_payment_records_dict := cls.create_payment_records_for_each_build_part(
data_info_type=data_info_type,
build_id=decision_book.build_id,
unit_price=unit_price,
unit_type=unit_type.upper(),
decision_book=decision_book,
decision_book_item=new_decision_book_item,
unit_price_is_fixed=data.unit_price_is_fixed,
debit_start_date=debit_start_date,
debit_end_date=debit_end_date,
currency=currency
):
if data_info_type.key == "BDT-A" or data_info_type.key == "BDT-D":
if data_info_type.key == "BDT-D":
item_comment = "Regular Payment Plan : "
else:
item_comment = "Additional Payment Plan : "
for key, value in dict(sorted(
created_payment_records_dict.items(),
key=lambda x: x[1],
reverse=True
)).items():
item_comment += f" {key} | {abs(float(value))} {currency}, "
item_comment = item_comment[:-2]
new_decision_book_item.update(
item_comment=item_comment
)
new_decision_book_item.update(is_payment_created=True)
return JSONResponse(
status_code=status.HTTP_200_OK,
content=dict(
message=f"Decision Book Item has created for given Decision Book UUID {decision_book.uu_id}",
completed=True,
data=new_decision_book_item.get_dict(),
),
)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Decision Book Item is not created. Check info type and info no and try again. Unique constraint index is implemented for info type and info no",
)
class DecisionBookDecisionBookItemsUpdateEventMethods(MethodToEvent):
event_type = "UPDATE"
__event_keys__ = {
"f0fdfe1b-806b-4175-ad50-a1a165c0dfb7": "building_decision_book_items_update",
}
@classmethod
def building_decision_book_items_update(
cls,
data: InsertBuildDecisionBookItems,
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
):
return
class DecisionBookDecisionBookItemsPatchEventMethods(MethodToEvent):
event_type = "PATCH"
__event_keys__ = {
"42328809-b516-477b-82cc-2d6fadf28843": "building_decision_book_items_patch",
}
@classmethod
def building_decision_book_items_patch(
cls,
data: InsertBuildDecisionBookItems,
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
):
return
DecisionBookDecisionBookItemsListEventMethod = (
DecisionBookDecisionBookItemsListEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/items/list")
)
)
DecisionBookDecisionBookItemsCreateEventMethod = (
DecisionBookDecisionBookItemsCreateEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/items/create")
)
)
DecisionBookDecisionBookItemsUpdateEventMethod = (
DecisionBookDecisionBookItemsUpdateEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/items/update")
)
)
DecisionBookDecisionBookItemsPatchEventMethod = (
DecisionBookDecisionBookItemsPatchEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/items/patch")
)
)
# if data.info_no is None:
# data_dict["info_no"] = data_dict["item_order"]
# elif data.occupant_types_uu_id:
# data_dict["info_type"] = None
# if occupant_type := OccupantTypes.find_one(
# uu_id=data.occupant_types_uu_id
# ):
# if (
# str(data.occupant_types_uu_id)
# in OccupantTypes.get_manager_occupant_type()
# ):
# if BuildDecisionBookItems.find_one(
# build_decision_book_id=decision_book.id,
# occupant_type=occupant_type.id,
# ):
# raise HTTPException(
# status_code=status.HTTP_400_BAD_REQUEST,
# detail="This Occupant Type is already exist for given Decision Book UUID. Check occupant type and try again",
# )
# data_dict["occupant_type"] = occupant_type.id
# data_dict["occupant_type_uu_id"] = str(occupant_type.uu_id)

View File

@@ -0,0 +1,5 @@
from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject
class DecisionBookDecisionBookItemsDebitsEvents(MethodToEvent): ...

View File

@@ -0,0 +1,443 @@
import typing
from fastapi import status, HTTPException
from fastapi.responses import JSONResponse
from databases import (
BuildDecisionBook,
BuildDecisionBookPerson,
BuildDecisionBookInvitations,
Users,
BuildLivingSpace,
BuildParts,
BuildDecisionBookPersonOccupants,
People,
OccupantTypes,
)
from api_validations import (
ListOptions,
RemoveDecisionBookPerson,
DecisionBookDecisionBookInvitationsAttend,
DecisionBookDecisionBookInvitationsUpdate,
DecisionBookDecisionBookInvitationsAssign,
)
from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject
from api_validations.core_response import return_json_response_from_alchemy
class DecisionBookPersonListEventMethods(MethodToEvent):
event_type = "SELECT"
__event_keys__ = {
"ea324dc0-3b08-4896-9040-7fa0401a176f": "building_decision_book_person_list",
}
@classmethod
def building_decision_book_person_list(
cls, data: ListOptions, token_dict: EmployeeTokenObject
):
return
class DecisionBookPersonAddEventMethods(MethodToEvent):
event_type = "CREATE"
__event_keys__ = {
"e346f720-880b-4b07-93d6-9ac76fbbaa33": "building_decision_book_person_add",
}
@classmethod
def building_decision_book_person_add(
cls,
data: DecisionBookDecisionBookInvitationsUpdate,
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
):
if isinstance(token_dict, EmployeeTokenObject):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Employee cannot create decision book invitations",
)
elif isinstance(token_dict, OccupantTokenObject):
decision_book = BuildDecisionBook.find_one(
uu_id=data.build_decision_book_uu_id
)
if not decision_book:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"No Decision Book is match with given UUID {data.build_decision_book_uu_id}",
)
manager_occupant_type = OccupantTypes.find_or_abort(
occupant_code="BU-MNG", occupant_category_type="BU"
)
if (
not manager_occupant_type.uu_id
== token_dict.selected_occupant.occupant_type_uu_id
):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Only Build Manager can update the invitation",
)
assign_occupant_type = OccupantTypes.find_or_abort(
uu_id=data.occupant_type_uu_id,
occupant_category_type="MT",
)
if not assign_occupant_type:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Occupant type must be a Meeting Type {data.occupant_type_uu_id} is not a MT type occupant",
)
manger_book_person = BuildDecisionBookPerson.find_one(
token=data.token,
build_decision_book_uu_id=data.build_decision_book_uu_id,
is_confirmed=True,
active=True,
)
if not manger_book_person:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Manager person not found. Please check token",
)
book_invite = BuildDecisionBookInvitations.find_one(
id=manger_book_person.invite_id,
build_id=token_dict.selected_occupant.build_id,
)
if not book_invite:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Invitation not found. Please check token",
)
selected_book_person = BuildDecisionBookPerson.find_one(
invite_id=book_invite.id,
person_uu_id=data.person_uu_id,
is_confirmed=True,
active=True,
)
if not selected_book_person:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Selected person is not in the invitation list. Please check {data.person_uu_id}",
)
selected_book_person.update(
occupant_type=assign_occupant_type.id,
occupant_type_uu_id=str(assign_occupant_type.uu_id),
)
return JSONResponse(
status_code=status.HTTP_200_OK,
content={
"completed": True,
"message": "Invitation is updated",
"data": selected_book_person.get_dict(),
},
)
class DecisionBookPersonRemoveEventMethods(MethodToEvent):
event_type = "DELETE"
__event_keys__ = {
"30588869-04cd-48ea-ad00-0e4f8dd7f735": "building_decision_book_people_remove",
}
@classmethod
def building_decision_book_people_remove(
cls, data: RemoveDecisionBookPerson, token_dict: EmployeeTokenObject
):
return
class DecisionBookPersonAttendEventMethods(MethodToEvent):
event_type = "UPDATE"
__event_keys__ = {
"bdcba521-0116-441c-ace1-84c5b68c86c7": "decision_book_invitations_attend",
}
@classmethod
def decision_book_invitations_attend(
cls,
data: DecisionBookDecisionBookInvitationsAttend,
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
):
if isinstance(token_dict, EmployeeTokenObject):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Employee cannot create decision book invitations",
)
token_user = Users.find_one(id=token_dict.user_id)
invitation_person = BuildDecisionBookPerson.find_one(
token=data.token, active=True, is_confirmed=True
)
if not invitation_person:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Invitation for the this specific user is not found. Please check token",
)
# is_token_valid = token_dict.person_id == invitation_person.person_id
# if not invitation_person.vicarious_person_id:
# if not invitation_person or not is_token_valid:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail=f"Invitation for the user {token_user.email} is not valid. Please check token",
# )
# todo check if vicarious person is valid
invitation = BuildDecisionBookInvitations.find_one(
id=invitation_person.invite_id,
build_id=token_dict.selected_occupant.build_id,
)
if not invitation:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Invitation not found. Please check invitation uuid : {invitation.uu_id}",
)
invitation_person.update(
is_attending=bool(data.is_attend), vicarious_person_id=None
)
return JSONResponse(
status_code=status.HTTP_200_OK,
content={
"completed": True,
"message": "Attendance is updated. Thank you for your response",
"data": {
"is_attending": bool(data.is_attend),
"user_uuid": str(token_user.uu_id),
"invitation_uuid": str(invitation.uu_id),
},
},
)
class DecisionBookPersonAssignOccupantEventMethods(MethodToEvent):
event_type = "UPDATE"
__event_keys__ = {
"c0b65098-9c79-4212-b1d0-c7e7836cf141": "decision_book_invitations_assign_occupant",
}
@classmethod
def decision_book_invitations_assign_occupant(
cls,
data: DecisionBookDecisionBookInvitationsAssign,
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
):
if isinstance(token_dict, EmployeeTokenObject):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Employee cannot create decision book invitations",
)
book_person_manager = BuildDecisionBookPerson.find_one(
token=data.token,
build_living_space_id=token_dict.selected_occupant.living_space_id,
active=True,
is_confirmed=True,
)
manager_occupant_type = OccupantTypes.find_or_abort(
occupant_code="BU-MNG", occupant_category_type="BU"
)
book_person_manager.check_occupant_type(manager_occupant_type)
# supervisor_occupant_type = OccupantTypes.find_or_abort(occupant_code="BU-SPV", occupant_category_type="BU")
# book_person_supervisor.check_occupant_type(supervisor_occupant_type)
invitation = BuildDecisionBookInvitations.find_one(
id=book_person_manager.invite_id,
build_id=token_dict.selected_occupant.build_id,
active=True,
is_confirmed=True,
)
if not invitation:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Invitation not found. Please check token",
)
assign_occupant_type = OccupantTypes.find_or_abort(
uu_id=data.occupant_type_uu_id, is_confirmed=True, active=True
)
if not assign_occupant_type:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Occupant type must be a Meeting Type {data.occupant_type_uu_id} is not a MT type occupant",
)
build_parts_of_token = BuildParts.filter_active(
BuildParts.build_id == token_dict.selected_occupant.build_id,
)
selected_living_space = BuildLivingSpace.filter_active(
BuildLivingSpace.uu_id == data.build_living_space_uu_id,
BuildLivingSpace.build_parts_id.in_(
[build.id for build in build_parts_of_token.data]
),
)
if not selected_living_space.data:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Person not found. Please check person uuid",
)
selected_living_space = selected_living_space.get(1)
book_person_to_assign: BuildDecisionBookPerson = (
BuildDecisionBookPerson.find_one(
build_living_space_id=selected_living_space.id,
invite_id=invitation.id,
active=True,
is_confirmed=True,
)
)
if not book_person_to_assign:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Person not found in the invitation list. Please check person uuid: {data.person_uu_id}",
)
if not book_person_to_assign.is_attending:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Person is declined the invitation. This person is not attending the meeting. "
f"Please check person uuid: {data.person_uu_id}. Invite UUID: {invitation.uu_id}",
)
if assign_occupant_type.occupant_code in ("MT-PRS", "MT-WRT"):
occupant_type_unique = OccupantTypes.find_or_abort(
occupant_code=assign_occupant_type.occupant_code,
occupant_category_type="MT",
)
if assigned_book_person_occupant := BuildDecisionBookPersonOccupants.find_one(
invite_id=invitation.id,
occupant_type_id=occupant_type_unique.id,
active=True,
is_confirmed=True,
):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Only one person can be assigned to {assign_occupant_type.occupant_code} type"
f" {assigned_book_person_occupant.uu_id} is already assigned",
)
if assign_occupant_type.occupant_code == "BU-MNG":
person_occupant_manager = BuildDecisionBookPersonOccupants.find_one(
invite_id=invitation.id,
occupant_type_id=manager_occupant_type.id,
active=True,
is_confirmed=True,
)
person_occupant_manager.delete(destroy=True)
book_person_to_assign.add_occupant_type(
occupant_type=assign_occupant_type,
build_living_space_id=selected_living_space.id,
)
return JSONResponse(
status_code=status.HTTP_200_OK,
content={
"completed": True,
"message": "Invitation is updated",
"data": book_person_to_assign.get_dict(),
},
)
DecisionBookPersonListEventMethod = DecisionBookPersonListEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/people/list")
)
DecisionBookPersonAddEventMethod = DecisionBookPersonAddEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/people/add")
)
DecisionBookPersonRemoveEventMethod = DecisionBookPersonRemoveEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/people/remove")
)
DecisionBookPersonAttendEventMethod = DecisionBookPersonAttendEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/invitations/attend")
)
DecisionBookPersonAssignOccupantEventMethod = (
DecisionBookPersonAssignOccupantEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/invitations/assign")
)
)
# Build.pre_query = Build.select_action(
# employee_id=token_dict.selected_company.employee_id,
# filter_expr=[
# Build.id == decision_book.build_id,
# ],
# )
# reachable_building = Build.filter_active()
# if not reachable_building.data:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail=f"No building is match with given Employee UUID {token_dict.selected_company.employee_uu_id}",
# )
#
# Companies.pre_query = Companies.select_action(
# duty_id_list=[
# token_dict.selected_company.duty_id,
# token_dict.selected_company.bulk_duties_id,
# ],
# filter_expr=[Companies.id == decision_book.resp_company_id],
# )
# reachable_companies = Companies.filter_active()
# if not reachable_companies.data:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail=f"No company is match with given Employee UUID {token_dict.selected_company.employee_uu_id}",
# )
# person_to_add = People.find_one(uu_id=data.person_uu_id)
# if not person_to_add:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail=f"No person is match with given UUID {data.person_uu_id}",
# )
# management_typecode = ApiEnumDropdown.filter_active(
# ApiEnumDropdown.uu_id == data.management_typecode_uu_id,
# ApiEnumDropdown.enum_class.in_("BuildManagementType", "BuildDuesTypes"),
# )
# if not management_typecode.data:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail=f"No management type is match with given UUID {data.management_typecode_uu_id}",
# )
# condition_to_met = (
# data.management_typecode_uu_id
# in ApiEnumDropdown.get_management_type_codes_list()
# )
# any_type_already = BuildDecisionBookPerson.find_one(
# build_decision_book_id=decision_book.id,
# management_typecode=data.management_typecode_uu_id,
# )
# if condition_to_met and any_type_already:
# raise HTTPException(
# status_code=status.HTTP_400_BAD_REQUEST,
# detail=f"Management type is already exist in given Decision Book UUID {data.build_decision_book_uu_id}.",
# )
#
# data_dict.pop("build_decision_book_uu_id", None)
# data_dict.pop("person_uu_id", None)
# data_dict["management_typecode"] = data_dict.pop(
# "management_typecode_uu_id", None
# )
# data_dict["build_decision_book_id"] = decision_book.id
# data_dict["person_id"] = person_to_add.id
#
# created = BuildDecisionBookPerson.find_or_create(**data_dict)
# if created.is_found:
# raise HTTPException(
# status_code=status.HTTP_400_BAD_REQUEST,
# detail=f"Decision Book Person is already exist in given Decision Book UUID {data.build_decision_book_uu_id}.",
# )
# return JSONResponse(
# status_code=status.HTTP_200_OK,
# content=dict(
# message=f"Decision Book Person has added for given Decision Book UUID {data.build_decision_book_uu_id}",
# completed=True,
# data=created.get_dict(),
# ),
# )

View File

@@ -0,0 +1,286 @@
import typing
from fastapi import status, HTTPException
from fastapi.responses import JSONResponse
from databases import (
Build,
BuildLivingSpace,
BuildParts,
BuildDecisionBookInvitations,
BuildDecisionBookPerson,
BuildDecisionBook,
BuildDecisionBookPersonOccupants,
OccupantTypes,
Users,
ApiEnumDropdown,
)
from api_validations import (
DecisionBookDecisionBookInvitationsUpdate,
DecisionBookDecisionBookInvitations,
)
from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject
from api_validations.core_response import return_json_response_from_alchemy
from api_library.date_time_actions.date_functions import DateTimeLocal
class BuildDecisionBookInvitationsListEventMethods(MethodToEvent):
event_type = "SELECT"
__event_keys__ = {
"e2277528-8c9c-4c0c-ae64-3ce80cae664b": "decision_book_invitations_list",
}
@classmethod
def decision_book_invitations_list(
cls,
data,
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
):
return
class BuildDecisionBookInvitationsCreateEventMethods(MethodToEvent):
event_type = "CREATE"
__event_keys__ = {
"d0bfa20c-841d-421c-98e6-d308f938d16a": "decision_book_invitations_create",
}
@classmethod
def decision_book_invitations_create(
cls,
data: DecisionBookDecisionBookInvitations,
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
):
if isinstance(token_dict, EmployeeTokenObject):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Employee cannot create decision book invitations",
)
# Check token posses the occupant type of Build Manager
occupant_manager = OccupantTypes.find_one(
occupant_category_type="BU", occupant_code="BU-MNG"
)
if not token_dict.selected_occupant.occupant_type_id == occupant_manager.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Only Build Manager can create decision book",
)
# Check decision book is valid for this token and building
decision_book = BuildDecisionBook.find_one(
uu_id=data.build_decision_book_uu_id,
build_id=token_dict.selected_occupant.build_id,
)
if not decision_book:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Decision book not found. Please create decision book first",
)
occupant_building = Build.find_one(id=token_dict.selected_occupant.build_id)
# Check meeting type is valid
meeting_type = ApiEnumDropdown.find_one(
enum_class="MeetingTypes",
)
if not meeting_type:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Meeting type not found",
)
# Check planned decision book date is valid
if (
not DateTimeLocal.get(data.planned_date).date()
>= DateTimeLocal.shift(days=1).date()
):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Planned date must be greater than today",
)
# Create an invitation for specific invitation type to start invite sending process
book_invitation = BuildDecisionBookInvitations.find_or_create(
build_id=token_dict.selected_occupant.build_id,
build_uu_id=token_dict.selected_occupant.build_uuid,
decision_book_id=decision_book.id,
decision_book_uu_id=str(decision_book.uu_id),
invitation_type=meeting_type.key,
living_part_count=occupant_building.livable_part_count,
living_part_percentage=0.51,
message=data.message,
planned_date=data.planned_date,
planned_date_expires=str(
DateTimeLocal.get(data.planned_date).shift(days=15).date()
),
expiry_ends=str(DateTimeLocal.get(data.planned_date).shift(days=15).date()),
is_confirmed=True,
)
if book_invitation.is_found:
detail_message = (
f"Invitation with: {str(book_invitation.planned_date)} already exists"
f" for {book_invitation.invitation_type}"
)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=detail_message,
)
# Get all the parts of the building that is occupant in token
build_parts = BuildParts.filter_active(
BuildParts.build_id == occupant_building.id
)
# Get all build living spaces that is found in building with distinct person id
occupants = OccupantTypes.filter_all()
build_living_spaces_people = (
BuildLivingSpace.filter_active(
BuildLivingSpace.build_parts_id.in_(
[build_part.id for build_part in build_parts.data]
),
BuildLivingSpace.occupant_type.in_(
[occupant.id for occupant in occupants.data]
),
filter_records=False,
)
.query.distinct(BuildLivingSpace.person_id)
.all()
)
if not build_living_spaces_people:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="No living spaces found for the building",
)
print(
f"Tnvite UUID : {book_invitation.uu_id} Message : {data.message} Planned date : {data.planned_date}"
)
# Send invitation to all the users as attend and update the manager as build manager
attendance_occupant_type = OccupantTypes.find_or_abort(
occupant_code="MT-ATT", occupant_category_type="MT"
)
manager_occupant_type = OccupantTypes.find_or_abort(
occupant_code="BU-MNG", occupant_category_type="BU"
)
build_decision_book_person_dict = dict(
build_decision_book_id=decision_book.id,
build_decision_book_uu_id=str(decision_book.uu_id),
invite_id=book_invitation.id,
invite_uu_id=str(book_invitation.uu_id),
send_date=str(DateTimeLocal.now().date()),
expiry_starts=decision_book.expiry_starts,
expiry_ends=decision_book.expiry_ends,
is_confirmed=True,
)
# Check if the invitation is already created at database
for build_living_spaces_user in build_living_spaces_people:
if invite := BuildDecisionBookPerson.find_one(
invite_id=book_invitation.id,
build_living_space_id=build_living_spaces_user.id,
):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Invitation already send to {build_living_spaces_user.email} "
f"for invite {invite.uu_id} date : {str(book_invitation.planned_date)}",
)
invitations_person = BuildDecisionBookPerson.find_or_create(
**build_decision_book_person_dict,
build_living_space_id=build_living_spaces_user.id,
build_living_space_uu_id=str(build_living_spaces_user.uu_id),
person_id=build_living_spaces_user.person_id,
token=Users.generate_token(40),
)
invitations_person.add_occupant_type(occupant_type=attendance_occupant_type)
if invitations_person and not invitations_person.is_found:
print(f'"{invitations_person.token}",')
spaces_user = Users.find_one(
active=True,
is_confirmed=True,
person_id=build_living_spaces_user.person_id,
)
# print(
# f"Invitation is send : {spaces_user.email} "
# f"Token : {invitations_person.token} "
# f"Send Date : {str(invitations_person.send_date.date())}"
# )
manager_living_spaces = BuildLivingSpace.filter_active(
BuildLivingSpace.person_id == token_dict.person_id,
)
manager_people = BuildDecisionBookPerson.filter_active(
BuildDecisionBookPerson.invite_id == book_invitation.id,
BuildDecisionBookPerson.build_living_space_id.in_(
[
manager_living_space.id
for manager_living_space in manager_living_spaces.data
]
),
)
manager_people_occupants = BuildDecisionBookPersonOccupants.filter_active(
BuildDecisionBookPersonOccupants.build_decision_book_person_id
== manager_people.get(1).id
)
dlt = [
occupants.delete(destroy=True)
for occupants in manager_people_occupants.data
]
dlt = [occupants.delete(destroy=True) for occupants in manager_people.data]
if book_person_manager := BuildDecisionBookPerson.find_or_create(
**build_decision_book_person_dict,
build_living_space_id=token_dict.selected_occupant.living_space_id,
build_living_space_uu_id=str(
token_dict.selected_occupant.living_space_uu_id
),
person_id=token_dict.person_id,
token=Users.generate_token(40),
):
book_person_manager.add_occupant_type(occupant_type=manager_occupant_type)
print(f"Manager Token : {book_person_manager.token}")
return JSONResponse(
status_code=status.HTTP_200_OK,
content={
"completed": True,
"message": "Invitation are send to people related with building",
"data": book_invitation.get_dict(),
},
)
class BuildDecisionBookInvitationsUpdateEventMethods(MethodToEvent):
event_type = "UPDATE"
__event_keys__ = {
"92413636-53a8-4a05-842c-1485a64e00d1": "decision_book_invitations_attend",
}
@classmethod
def decision_book_invitations_attend(
cls,
data: DecisionBookDecisionBookInvitationsUpdate,
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
):
return
BuildDecisionBookInvitationsListEventMethod = (
BuildDecisionBookInvitationsListEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/invite/list")
)
)
BuildDecisionBookInvitationsCreateEventMethod = (
BuildDecisionBookInvitationsCreateEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/invite/create")
)
)
BuildDecisionBookInvitationsUpdateEventMethod = (
BuildDecisionBookInvitationsUpdateEventMethods(
action=ActionsSchema(endpoint="/build/decision_book/invite/update")
)
)

View File

@@ -0,0 +1,6 @@
from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject
from api_validations.core_response import return_json_response_from_alchemy
class ProjectDecisionBookProjectDecisionBookEvents(MethodToEvent): ...

View File

@@ -0,0 +1,6 @@
from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject
from api_validations.core_response import return_json_response_from_alchemy
class ProjectDecisionBookProjectDecisionBookPersonEvents(MethodToEvent): ...