Initial commit: MISF VOB download and upload automation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
lubna-fatimaandClaude Sonnet 4.6 committed 2026-06-16 14:55:29 +05:30
commit c2dec96b1f
43 files changed
+10944

No files matched your search

+86
View File
@@ -0,0 +1,86 @@
"""
Create Input/input.xlsx (if missing) and refresh Input/input.xlsx.example.
Run from repository root:
python scripts/bootstrap_input_xlsx.py
"""
from __future__ import annotations
from pathlib import Path
import pandas as pd
PROJECT_ROOT = Path(__file__).resolve().parent.parent
INPUT_DIR = PROJECT_ROOT / "Input"
CONFIG_PATH = INPUT_DIR / "input.xlsx"
EXAMPLE_PATH = INPUT_DIR / "input.xlsx.example"
FACILITY_KEYWORDS = [
"amaya", "brighton place san diego", "brighton place sd", "brighton place spring valley",
"brighton place-spring valley", "granite hill", "anberry", "arbor hills", "bayberry",
"bradley court", "castle manor", "friendship", "gramercy", "kearny", "lakeside",
"newport", "siena", "stanford court", "temecula", "walnut", "balboa", "cottonwood",
"country hills", "la mesa", "university care", "blue oak", "bridgewood", "capistrano",
"eden", "hillcrest heights", "la casa via", "torrey pines", "bradley gardens", "horizon",
"community care", "jacob", "the shores", "valley vista", "valle vista", "golden hill",
"grossmont post acute", "lemon grove", "mission hills", "parkside", "somerset",
"the springs", "victoria", "reo vista", "san diego post acute", "villa las palmas",
"ridgeview", "windsor gardens", "windsor rosewood",
]
FACILITIES = [k.title() for k in FACILITY_KEYWORDS]
FACILITIES.append("REPLACE_WITH_54TH_FACILITY_NAME")
DEFAULT_PLANNER_URL = (
"https://planner.cloud.microsoft/webui/mytasks/assignedtome/view/grid"
"?tid=0c15ad2c-19dd-4ded-9cd1-20d344386414"
)
SHEET1_ROWS = [
("Proxy IP", ""),
("Proxy Port", ""),
("Proxy Username", ""),
("Proxy Password", ""),
("OneDrive Username", ""),
("OneDrive Password", ""),
("Microsoft TOTP Secret", ""),
("Planner URL", DEFAULT_PLANNER_URL),
(
"OneDrive URL",
"https://netorg393890-my.sharepoint.com/personal/joy_accesspsychology_org"
"/_layouts/15/onedrive.aspx?id=%2Fpersonal%2Fjoy%5Faccesspsychology%5Forg%2FDocuments"
"%2FAccess%20Facility%20Spreadsheets3",
),
("pdf_folder_path", str(PROJECT_ROOT / "Input" / "PDFs")),
]
def _build_workbook() -> tuple[pd.DataFrame, pd.DataFrame]:
sheet1 = pd.DataFrame(SHEET1_ROWS, columns=["Key", "Value"])
facilities = pd.DataFrame({"Facility Name": FACILITIES})
return sheet1, facilities
def _write(path: Path, sheet1: pd.DataFrame, facilities: pd.DataFrame) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
with pd.ExcelWriter(path, engine="openpyxl") as writer:
sheet1.to_excel(writer, sheet_name="Sheet1", index=False, header=False)
facilities.to_excel(writer, sheet_name="Facilities", index=False)
def main() -> None:
sheet1, facilities = _build_workbook()
_write(EXAMPLE_PATH, sheet1, facilities)
print(f"Wrote template -> {EXAMPLE_PATH} ({len(FACILITIES)} facilities)")
if CONFIG_PATH.exists():
print(f"Skipped (already exists): {CONFIG_PATH}")
return
_write(CONFIG_PATH, sheet1, facilities)
print(f"Created local config -> {CONFIG_PATH}")
print("Edit input.xlsx with your credentials before running run_download.py")
if __name__ == "__main__":
main()
+88
View File
@@ -0,0 +1,88 @@
# Build MISF_VOB_Download.exe and install at project root (double-click to run).
# Output: <project root>\MISF_VOB_Download.exe + _internal\
$ErrorActionPreference = "Stop"
$Root = Split-Path -Parent $PSScriptRoot
Set-Location $Root
Write-Host "=== MISF VOB Download - production build (project root) ===" -ForegroundColor Cyan
$PortableChrome = Join-Path $Root "chrome-win\chrome-win\chrome.exe"
$PortableDriver = Join-Path $Root "chrome-win\chrome-win\chromedriver.exe"
if (-not (Test-Path $PortableChrome)) {
throw "Portable Chromium not found: $PortableChrome (required for production build)"
}
if (-not (Test-Path $PortableDriver)) {
Write-Warning "Bundled chromedriver missing: $PortableDriver (will download at runtime)"
} else {
Write-Host "Using portable Chromium: $PortableChrome"
}
if (-not (Test-Path ".venv\Scripts\python.exe")) {
Write-Host "Creating virtual environment..."
python -m venv .venv
}
$Python = Join-Path $Root ".venv\Scripts\python.exe"
& $Python -m pip install --upgrade pip -q
& $Python -m pip install -r requirements.txt -q
& $Python -m pip install "pyinstaller>=6.10" -q
Write-Host "Running PyInstaller..."
& $Python -m PyInstaller --noconfirm MISF_VOB_Download.spec
if ($LASTEXITCODE -ne 0) {
throw "PyInstaller failed with exit code $LASTEXITCODE"
}
$BuiltExe = Join-Path $Root "dist\MISF_VOB_Download.exe"
$BuiltDir = Join-Path $Root "dist\MISF_VOB_Download"
$BuiltDirExe = Join-Path $BuiltDir "MISF_VOB_Download.exe"
if (Test-Path $BuiltExe) {
Write-Host "Installing one-file build to project root..."
$ExeDest = Join-Path $Root "MISF_VOB_Download.exe"
Get-Process -Name "MISF_VOB_Download" -ErrorAction SilentlyContinue | Stop-Process -Force
Start-Sleep -Seconds 1
Copy-Item $BuiltExe $ExeDest -Force
Remove-Item $BuiltExe -Force
} elseif (Test-Path $BuiltDirExe) {
Write-Host "Installing folder build to project root..."
$ExeDest = Join-Path $Root "MISF_VOB_Download.exe"
Get-Process -Name "MISF_VOB_Download" -ErrorAction SilentlyContinue | Stop-Process -Force
Start-Sleep -Seconds 1
$InternalDest = Join-Path $Root "_internal"
Copy-Item $BuiltDirExe $ExeDest -Force
if (Test-Path $InternalDest) {
Remove-Item $InternalDest -Recurse -Force
}
Move-Item (Join-Path $BuiltDir "_internal") $InternalDest -Force
Remove-Item $BuiltDir -Recurse -Force
} else {
throw "Expected build output not found: $BuiltExe or $BuiltDirExe"
}
$DistFolder = Join-Path $Root "dist"
if (Test-Path $DistFolder) {
$remaining = @(Get-ChildItem $DistFolder -Force -ErrorAction SilentlyContinue)
if ($remaining.Count -eq 0) {
Remove-Item $DistFolder -Force
}
}
$InputDir = Join-Path $Root "Input"
$OutputDir = Join-Path $Root "Output"
New-Item -ItemType Directory -Force -Path (Join-Path $InputDir "PDFs") | Out-Null
New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
Copy-Item (Join-Path $Root "docs\PRODUCTION_README.txt") (Join-Path $Root "README_PRODUCTION.txt") -Force
Write-Host ""
Write-Host "Build complete." -ForegroundColor Green
Write-Host " Run: $ExeDest"
Write-Host ""
Write-Host "First-time setup:" -ForegroundColor Yellow
Write-Host " 1. Copy Input\input.xlsx.example to Input\input.xlsx and fill credentials"
Write-Host " 2. Add facility names on the Facilities sheet"
Write-Host " 3. Keep chrome-win\ next to the .exe"
Write-Host " 4. Double-click MISF_VOB_Download.exe in this folder"
Write-Host ""
Write-Host 'See README_PRODUCTION.txt. Exe is at project root (no dist folder).'
+68
View File
@@ -0,0 +1,68 @@
"""Print config status (no browser). Run: python scripts/validate_config.py"""
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT / "src"))
from config import ( # noqa: E402
CONFIG_FILE,
DEFAULT_PLANNER_URL,
EXPECTED_FACILITY_COUNT,
credentials_setup_message,
missing_onedrive_credentials,
read_app_config,
read_facility_list,
)
from totp_utils import validate_totp_secret # noqa: E402
def main() -> int:
path = CONFIG_FILE if CONFIG_FILE.exists() else ROOT / "Input" / "input.xlsx.example"
if not path.exists():
print(f"[FAIL] Missing {path}")
print(" copy Input\\input.xlsx.example Input\\input.xlsx")
return 1
cfg = read_app_config(path)
missing = missing_onedrive_credentials(
cfg.onedrive_username,
cfg.onedrive_password,
cfg.microsoft_totp_secret,
)
if missing and path == CONFIG_FILE:
print("[FAIL] Credentials not configured:")
print(credentials_setup_message(missing))
return 1
try:
validate_totp_secret(cfg.microsoft_totp_secret or "JBSWY3DPEHPK3PXP")
except ValueError as exc:
if cfg.microsoft_totp_secret:
print(f"[FAIL] TOTP secret: {exc}")
return 1
print(f"[WARN] TOTP not set — format check skipped ({exc})")
facilities = read_facility_list(path, warn_if_not_expected_count=True)
planner = cfg.planner_url or DEFAULT_PLANNER_URL
if not planner.startswith("https://"):
print(f"[FAIL] Planner URL must be https — got: {planner[:80]}")
return 1
print(f"[OK] Config file: {path.name}")
if cfg.onedrive_username:
print(f"[OK] Credentials set for {cfg.onedrive_username}")
else:
print("[OK] Template mode (no credentials in example file)")
print(f"[OK] Planner URL: {planner[:70]}...")
print(f"[OK] PDF folder: {cfg.pdf_folder_path or '(default Input/PDFs)'}")
print(f"[OK] {len(facilities)} facilities (expected {EXPECTED_FACILITY_COUNT})")
if len(facilities) != EXPECTED_FACILITY_COUNT:
print("[WARN] Facility count differs from expected 54 — verify Facilities sheet")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+71
View File
@@ -0,0 +1,71 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Pre-flight validation for UiPath MISF Planner Download pilot (mirrors Python validate_config + output checks).
#>
param(
[string]$ProjectRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path,
[int]$ExpectedFacilities = 54,
[switch]$CheckOutput
)
$ErrorActionPreference = "Stop"
$configPath = Join-Path $ProjectRoot "Input\input.xlsx"
$uipathProject = Join-Path $ProjectRoot "uipath\MISF_Planner_Download\project.json"
$today = Get-Date -Format "yyyy-MM-dd"
Write-Host "MISF UiPath pilot validation"
Write-Host "Project root: $ProjectRoot"
if (-not (Test-Path $configPath)) {
Write-Error "Missing config: $configPath (copy Input\input.xlsx.example)"
}
if (-not (Test-Path $uipathProject)) {
Write-Error "Missing UiPath project: $uipathProject"
}
# Run Python config validator if available
$pyValidate = Join-Path $ProjectRoot "scripts\validate_config.py"
if (Test-Path $pyValidate) {
Write-Host "Running Python validate_config.py..."
python $pyValidate
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
}
# Verify UiPath workflow files exist
$requiredWorkflows = @(
"Main.xaml",
"01_ReadConfig.xaml",
"02_LoginPlanner.xaml",
"03_ProcessFacility.xaml",
"04_DownloadTaskAttachments.xaml",
"05_SetTaskInProgress.xaml",
"06_WriteSummary.xaml",
"Helpers\GenerateTotp.xaml",
"Helpers\SanitizeFolderName.xaml",
"Selectors\PlannerSelectors.json"
)
$uipathDir = Join-Path $ProjectRoot "uipath\MISF_Planner_Download"
foreach ($wf in $requiredWorkflows) {
$p = Join-Path $uipathDir $wf
if (-not (Test-Path $p)) {
Write-Error "Missing workflow file: $p"
}
}
Write-Host "All UiPath workflow files present."
if ($CheckOutput) {
$summary = Join-Path $ProjectRoot "Output\$today\Summary\Download_Summary_$today.xlsx"
$original = Join-Path $ProjectRoot "Output\$today\Original"
if (-not (Test-Path $summary)) {
Write-Warning "No summary yet: $summary (run UiPath Main with in_FacilityLimit=2)"
} else {
Write-Host "Summary found: $summary"
}
if (Test-Path $original) {
$pdfCount = (Get-ChildItem -Path $original -Recurse -Filter "*.pdf" -ErrorAction SilentlyContinue).Count
Write-Host "PDFs under Original: $pdfCount"
}
}
Write-Host "Pilot pre-flight OK."