mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
Merge pull request #1166 from Hestia-Homes/main
Pashub fetcher dynamically instantiates sharepoint client based on received sharepoint site
This commit is contained in:
commit
d65d7dcf51
11 changed files with 63 additions and 9 deletions
4
.github/workflows/_deploy_lambda.yml
vendored
4
.github/workflows/_deploy_lambda.yml
vendored
|
|
@ -76,6 +76,8 @@ on:
|
||||||
required: false
|
required: false
|
||||||
TF_VAR_social_housing_wave_3_sharepoint_id:
|
TF_VAR_social_housing_wave_3_sharepoint_id:
|
||||||
required: false
|
required: false
|
||||||
|
TF_VAR_eco_sharepoint_id:
|
||||||
|
required: false
|
||||||
TF_VAR_pashub_email:
|
TF_VAR_pashub_email:
|
||||||
required: false
|
required: false
|
||||||
TF_VAR_pashub_password:
|
TF_VAR_pashub_password:
|
||||||
|
|
@ -159,6 +161,7 @@ jobs:
|
||||||
TF_VAR_osmosis_acd_sharepoint_id: ${{ secrets.TF_VAR_osmosis_acd_sharepoint_id }}
|
TF_VAR_osmosis_acd_sharepoint_id: ${{ secrets.TF_VAR_osmosis_acd_sharepoint_id }}
|
||||||
TF_VAR_private_pay_sharepoint_id: ${{ secrets.TF_VAR_private_pay_sharepoint_id }}
|
TF_VAR_private_pay_sharepoint_id: ${{ secrets.TF_VAR_private_pay_sharepoint_id }}
|
||||||
TF_VAR_social_housing_wave_3_sharepoint_id: ${{ secrets.TF_VAR_social_housing_wave_3_sharepoint_id }}
|
TF_VAR_social_housing_wave_3_sharepoint_id: ${{ secrets.TF_VAR_social_housing_wave_3_sharepoint_id }}
|
||||||
|
TF_VAR_eco_sharepoint_id: ${{ secrets.TF_VAR_eco_sharepoint_id }}
|
||||||
TF_VAR_pashub_email: ${{ secrets.TF_VAR_pashub_email }}
|
TF_VAR_pashub_email: ${{ secrets.TF_VAR_pashub_email }}
|
||||||
TF_VAR_pashub_password: ${{ secrets.TF_VAR_pashub_password }}
|
TF_VAR_pashub_password: ${{ secrets.TF_VAR_pashub_password }}
|
||||||
TF_VAR_pashub_coordination_email: ${{ secrets.TF_VAR_pashub_coordination_email }}
|
TF_VAR_pashub_coordination_email: ${{ secrets.TF_VAR_pashub_coordination_email }}
|
||||||
|
|
@ -210,6 +213,7 @@ jobs:
|
||||||
TF_VAR_osmosis_acd_sharepoint_id: ${{ secrets.TF_VAR_osmosis_acd_sharepoint_id }}
|
TF_VAR_osmosis_acd_sharepoint_id: ${{ secrets.TF_VAR_osmosis_acd_sharepoint_id }}
|
||||||
TF_VAR_private_pay_sharepoint_id: ${{ secrets.TF_VAR_private_pay_sharepoint_id }}
|
TF_VAR_private_pay_sharepoint_id: ${{ secrets.TF_VAR_private_pay_sharepoint_id }}
|
||||||
TF_VAR_social_housing_wave_3_sharepoint_id: ${{ secrets.TF_VAR_social_housing_wave_3_sharepoint_id }}
|
TF_VAR_social_housing_wave_3_sharepoint_id: ${{ secrets.TF_VAR_social_housing_wave_3_sharepoint_id }}
|
||||||
|
TF_VAR_eco_sharepoint_id: ${{ secrets.TF_VAR_eco_sharepoint_id }}
|
||||||
TF_VAR_pashub_email: ${{ secrets.TF_VAR_pashub_email }}
|
TF_VAR_pashub_email: ${{ secrets.TF_VAR_pashub_email }}
|
||||||
TF_VAR_pashub_password: ${{ secrets.TF_VAR_pashub_password }}
|
TF_VAR_pashub_password: ${{ secrets.TF_VAR_pashub_password }}
|
||||||
TF_VAR_pashub_coordination_email: ${{ secrets.TF_VAR_pashub_coordination_email }}
|
TF_VAR_pashub_coordination_email: ${{ secrets.TF_VAR_pashub_coordination_email }}
|
||||||
|
|
|
||||||
1
.github/workflows/deploy_terraform.yml
vendored
1
.github/workflows/deploy_terraform.yml
vendored
|
|
@ -448,6 +448,7 @@ jobs:
|
||||||
TF_VAR_osmosis_acd_sharepoint_id: ${{ secrets.OSMOSIS_ACD_SHAREPOINT_ID }}
|
TF_VAR_osmosis_acd_sharepoint_id: ${{ secrets.OSMOSIS_ACD_SHAREPOINT_ID }}
|
||||||
TF_VAR_private_pay_sharepoint_id: ${{ secrets.PRIVATE_PAY_SHAREPOINT_ID }}
|
TF_VAR_private_pay_sharepoint_id: ${{ secrets.PRIVATE_PAY_SHAREPOINT_ID }}
|
||||||
TF_VAR_social_housing_wave_3_sharepoint_id: ${{ secrets.SOCIAL_HOUSING_WAVE_3_SHAREPOINT_ID }}
|
TF_VAR_social_housing_wave_3_sharepoint_id: ${{ secrets.SOCIAL_HOUSING_WAVE_3_SHAREPOINT_ID }}
|
||||||
|
TF_VAR_eco_sharepoint_id: ${{ secrets.TF_VAR_eco_sharepoint_id }}
|
||||||
TF_VAR_pashub_email: ${{ secrets.PASHUB_EMAIL }}
|
TF_VAR_pashub_email: ${{ secrets.PASHUB_EMAIL }}
|
||||||
TF_VAR_pashub_password: ${{ secrets.PASHUB_PASSWORD }}
|
TF_VAR_pashub_password: ${{ secrets.PASHUB_PASSWORD }}
|
||||||
TF_VAR_pashub_coordination_email: ${{ secrets.PASHUB_COORDINATION_EMAIL }}
|
TF_VAR_pashub_coordination_email: ${{ secrets.PASHUB_COORDINATION_EMAIL }}
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ class Settings(BaseSettings):
|
||||||
OSMOSIS_ACD_SHAREPOINT_ID: Optional[str] = None
|
OSMOSIS_ACD_SHAREPOINT_ID: Optional[str] = None
|
||||||
PRIVATE_PAY_SHAREPOINT_ID: Optional[str] = None
|
PRIVATE_PAY_SHAREPOINT_ID: Optional[str] = None
|
||||||
SOCIAL_HOUSING_WAVE_3_SHAREPOINT_ID: Optional[str] = None
|
SOCIAL_HOUSING_WAVE_3_SHAREPOINT_ID: Optional[str] = None
|
||||||
|
ECO_SHAREPOINT_ID: Optional[str] = None
|
||||||
OPENAI_API_KEY: Optional[str] = None
|
OPENAI_API_KEY: Optional[str] = None
|
||||||
|
|
||||||
# Pas Hub
|
# Pas Hub
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,6 @@ def handler(body: Dict[str, Any], context: Any) -> List[str]:
|
||||||
if (not pashub_email) or (not pashub_password):
|
if (not pashub_email) or (not pashub_password):
|
||||||
raise ValueError("Pas Hub credentials not provided")
|
raise ValueError("Pas Hub credentials not provided")
|
||||||
|
|
||||||
sharepoint_client = DomnaSharepointClient(
|
|
||||||
sharepoint_location=DomnaSites.SOCIAL_HOUSING_WAVE_3
|
|
||||||
)
|
|
||||||
|
|
||||||
if coordination_hub_email and coordination_hub_password:
|
if coordination_hub_email and coordination_hub_password:
|
||||||
_coord_email, _coord_password = (
|
_coord_email, _coord_password = (
|
||||||
coordination_hub_email,
|
coordination_hub_email,
|
||||||
|
|
@ -57,6 +53,16 @@ def handler(body: Dict[str, Any], context: Any) -> List[str]:
|
||||||
payload = PashubToAraTriggerRequest.model_validate(body)
|
payload = PashubToAraTriggerRequest.model_validate(body)
|
||||||
logger.debug("Successfully validated request body")
|
logger.debug("Successfully validated request body")
|
||||||
|
|
||||||
|
sharepoint_client: Optional[DomnaSharepointClient] = None
|
||||||
|
if payload.sharepoint_site is not None:
|
||||||
|
try:
|
||||||
|
resolved_site = DomnaSites[payload.sharepoint_site]
|
||||||
|
sharepoint_client = DomnaSharepointClient(sharepoint_location=resolved_site)
|
||||||
|
except KeyError:
|
||||||
|
logger.warning(
|
||||||
|
f"Unrecognised sharepoint_site '{payload.sharepoint_site}'; skipping SharePoint upload"
|
||||||
|
)
|
||||||
|
|
||||||
service = PashubService(
|
service = PashubService(
|
||||||
pashub_client=get_pashub_client(pashub_email, pashub_password),
|
pashub_client=get_pashub_client(pashub_email, pashub_password),
|
||||||
sharepoint_client=sharepoint_client,
|
sharepoint_client=sharepoint_client,
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class PashubService:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
pashub_client: PashubClient,
|
pashub_client: PashubClient,
|
||||||
sharepoint_client: DomnaSharepointClient,
|
sharepoint_client: Optional[DomnaSharepointClient],
|
||||||
s3_bucket: str,
|
s3_bucket: str,
|
||||||
coordination_client_factory: Optional[Callable[[], PashubClient]] = None,
|
coordination_client_factory: Optional[Callable[[], PashubClient]] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
@ -111,7 +111,7 @@ class PashubService:
|
||||||
default_file_type=FileTypeEnum.OTHER.value,
|
default_file_type=FileTypeEnum.OTHER.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
if request.sharepoint_link and request.address:
|
if self._sharepoint_client and request.sharepoint_link and request.address:
|
||||||
folder_name = request.address.split("|")[0].strip()
|
folder_name = request.address.split("|")[0].strip()
|
||||||
folders = self._sharepoint_client.get_folders_in_path(request.sharepoint_link)
|
folders = self._sharepoint_client.get_folders_in_path(request.sharepoint_link)
|
||||||
match = next(
|
match = next(
|
||||||
|
|
@ -207,6 +207,7 @@ class PashubService:
|
||||||
property_folder_path: str,
|
property_folder_path: str,
|
||||||
files: List[DownloadedFile],
|
files: List[DownloadedFile],
|
||||||
) -> None:
|
) -> None:
|
||||||
|
assert self._sharepoint_client is not None
|
||||||
for df in files:
|
for df in files:
|
||||||
filename = os.path.basename(df.file_path)
|
filename = os.path.basename(df.file_path)
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ class PashubToAraTriggerRequest(BaseModel):
|
||||||
hubspot_listing_id: Optional[int] = None
|
hubspot_listing_id: Optional[int] = None
|
||||||
hubspot_deal_id: Optional[str] = None
|
hubspot_deal_id: Optional[str] = None
|
||||||
|
|
||||||
|
sharepoint_site: Optional[str] = None
|
||||||
|
|
||||||
get_other_files: bool = False
|
get_other_files: bool = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
||||||
|
|
@ -616,6 +616,33 @@ def test_sharepoint_skips_upload_when_folder_not_found() -> None:
|
||||||
mock_logger.warning.assert_called()
|
mock_logger.warning.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_sharepoint_skips_upload_when_sharepoint_client_is_none() -> None:
|
||||||
|
# Arrange
|
||||||
|
mock_client = MagicMock(spec=PashubClient)
|
||||||
|
mock_client.get_uprn_by_job_id.return_value = None
|
||||||
|
mock_client.get_evidence_files_by_job_id.return_value = make_downloaded(
|
||||||
|
core=["/tmp/core.pdf"]
|
||||||
|
)
|
||||||
|
|
||||||
|
service = PashubService(
|
||||||
|
pashub_client=mock_client,
|
||||||
|
sharepoint_client=None,
|
||||||
|
s3_bucket="test-bucket",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act — should not raise AttributeError on None._sharepoint_client
|
||||||
|
with patch("backend.pashub_fetcher.pashub_service.os.remove"):
|
||||||
|
result = service.run(
|
||||||
|
make_request(
|
||||||
|
sharepoint_link="Retrofit/Properties",
|
||||||
|
address="123 Main St | deal",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert result == ["/tmp/core.pdf"]
|
||||||
|
|
||||||
|
|
||||||
def test_run_warns_and_continues_when_site_notes_parsing_fails() -> None:
|
def test_run_warns_and_continues_when_site_notes_parsing_fails() -> None:
|
||||||
mock_client = MagicMock(spec=PashubClient)
|
mock_client = MagicMock(spec=PashubClient)
|
||||||
mock_client.get_uprn_by_job_id.return_value = None
|
mock_client.get_uprn_by_job_id.return_value = None
|
||||||
|
|
|
||||||
|
|
@ -52,12 +52,16 @@ EXCEL_PATH: str = os.path.join(
|
||||||
"local_run_02-06-2026/ECO_Approach_Coordination_Design_KN.xlsx",
|
"local_run_02-06-2026/ECO_Approach_Coordination_Design_KN.xlsx",
|
||||||
)
|
)
|
||||||
|
|
||||||
SHAREPOINT_PROPERTIES_FOLDER: str = ""
|
SHAREPOINT_PROPERTIES_FOLDER: str = (
|
||||||
|
"Housing Associations/- Client Shared Folders/Abri/Abri Property Folders (Full PAS Info)"
|
||||||
|
)
|
||||||
|
|
||||||
|
SHAREPOINT_SITE: str = "ECO"
|
||||||
|
|
||||||
|
|
||||||
def _build_requests(excel_path: str) -> list[PashubToAraTriggerRequest]:
|
def _build_requests(excel_path: str) -> list[PashubToAraTriggerRequest]:
|
||||||
wb = load_workbook(excel_path, data_only=True)
|
wb = load_workbook(excel_path, data_only=True)
|
||||||
ws = wb.worksheets[0]
|
ws = wb.worksheets[1]
|
||||||
|
|
||||||
headers: dict[str, int] = {}
|
headers: dict[str, int] = {}
|
||||||
for col in range(1, ws.max_column + 1):
|
for col in range(1, ws.max_column + 1):
|
||||||
|
|
@ -102,6 +106,7 @@ def _build_requests(excel_path: str) -> list[PashubToAraTriggerRequest]:
|
||||||
address=address,
|
address=address,
|
||||||
deal_stage=deal_stage,
|
deal_stage=deal_stage,
|
||||||
sharepoint_link=SHAREPOINT_PROPERTIES_FOLDER or None,
|
sharepoint_link=SHAREPOINT_PROPERTIES_FOLDER or None,
|
||||||
|
sharepoint_site=SHAREPOINT_SITE,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -123,7 +128,7 @@ def main() -> None:
|
||||||
for request in trigger_requests:
|
for request in trigger_requests:
|
||||||
action: str = "DRY RUN" if DRY_RUN else "SENDING"
|
action: str = "DRY RUN" if DRY_RUN else "SENDING"
|
||||||
logger.info(
|
logger.info(
|
||||||
f"[{action}] deal_id={request.hubspot_deal_id} pashub_link={request.pashub_link}"
|
f"[{action}] deal_id={request.hubspot_deal_id} pashub_link={request.pashub_link} sharepoint_link={request.sharepoint_link}"
|
||||||
)
|
)
|
||||||
|
|
||||||
if not DRY_RUN:
|
if not DRY_RUN:
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ module "lambda" {
|
||||||
OSMOSIS_ACD_SHAREPOINT_ID = var.osmosis_acd_sharepoint_id
|
OSMOSIS_ACD_SHAREPOINT_ID = var.osmosis_acd_sharepoint_id
|
||||||
PRIVATE_PAY_SHAREPOINT_ID = var.private_pay_sharepoint_id
|
PRIVATE_PAY_SHAREPOINT_ID = var.private_pay_sharepoint_id
|
||||||
SOCIAL_HOUSING_WAVE_3_SHAREPOINT_ID = var.social_housing_wave_3_sharepoint_id
|
SOCIAL_HOUSING_WAVE_3_SHAREPOINT_ID = var.social_housing_wave_3_sharepoint_id
|
||||||
|
ECO_SHAREPOINT_ID = var.eco_sharepoint_id
|
||||||
PASHUB_EMAIL = var.pashub_email
|
PASHUB_EMAIL = var.pashub_email
|
||||||
PASHUB_PASSWORD = var.pashub_password
|
PASHUB_PASSWORD = var.pashub_password
|
||||||
PASHUB_COORDINATION_EMAIL = var.pashub_coordination_email
|
PASHUB_COORDINATION_EMAIL = var.pashub_coordination_email
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,11 @@ variable "social_housing_wave_3_sharepoint_id" {
|
||||||
sensitive = true
|
sensitive = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "eco_sharepoint_id" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
variable "pashub_email" {
|
variable "pashub_email" {
|
||||||
type = string
|
type = string
|
||||||
sensitive = true
|
sensitive = true
|
||||||
|
|
|
||||||
|
|
@ -9,3 +9,4 @@ class DomnaSites(Enum):
|
||||||
OSMOSIS_ACD = os.getenv("OSMOSIS_ACD_SHAREPOINT_ID")
|
OSMOSIS_ACD = os.getenv("OSMOSIS_ACD_SHAREPOINT_ID")
|
||||||
PRIVATE_PAY = os.getenv("PRIVATE_PAY_SHAREPOINT_ID")
|
PRIVATE_PAY = os.getenv("PRIVATE_PAY_SHAREPOINT_ID")
|
||||||
SOCIAL_HOUSING_WAVE_3 = os.getenv("SOCIAL_HOUSING_WAVE_3_SHAREPOINT_ID")
|
SOCIAL_HOUSING_WAVE_3 = os.getenv("SOCIAL_HOUSING_WAVE_3_SHAREPOINT_ID")
|
||||||
|
ECO = os.getenv("ECO_SHAREPOINT_ID")
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue