param( [switch]$NoPause ) $ErrorActionPreference = "Stop" $Root = Split-Path -Parent $PSScriptRoot $Venv = Join-Path $Root ".venv" $VenvPython = Join-Path $Venv "Scripts\python.exe" $WheelDir = Join-Path $Root "vendor\wheels" $Req = Join-Path $Root "requirements.txt" function Write-Step { param([string]$Message) Write-Host "" Write-Host "==> $Message" -ForegroundColor Cyan } function Wait-BeforeExit { if (-not $NoPause) { Write-Host "" Read-Host "Press Enter to exit" } } function Get-PythonCandidate { $candidates = @( @{ Exe = "py"; Args = @("-3") }, @{ Exe = "python"; Args = @() }, @{ Exe = "python3"; Args = @() } ) foreach ($candidate in $candidates) { $command = Get-Command $candidate.Exe -ErrorAction SilentlyContinue if (-not $command) { continue } try { $args = @($candidate.Args) + @("-c", "import sys; print(sys.executable); print(sys.version_info.major); print(sys.version_info.minor)") $output = & $candidate.Exe @args 2>$null if ($LASTEXITCODE -ne 0 -or $output.Count -lt 3) { continue } $major = [int]$output[1] $minor = [int]$output[2] if ($major -gt 3 -or ($major -eq 3 -and $minor -ge 10)) { return [pscustomobject]@{ Exe = $candidate.Exe Args = $candidate.Args Path = $output[0] Version = "$major.$minor" } } } catch { continue } } return $null } function Install-PythonWithWinget { $winget = Get-Command winget -ErrorAction SilentlyContinue if (-not $winget) { throw "Python 3.10+ was not found, and winget is not available. Install Python 3.10+ manually and run this script again." } Write-Step "Python was not found. Installing Python 3.12 with winget" & winget install --id Python.Python.3.12 -e --source winget --accept-package-agreements --accept-source-agreements if ($LASTEXITCODE -ne 0) { throw "winget failed to install Python. Check network access or install Python 3.10+ manually." } } function Ensure-Python { $python = Get-PythonCandidate if ($python) { Write-Host "Python found: $($python.Path), version $($python.Version)" return $python } Install-PythonWithWinget $python = Get-PythonCandidate if (-not $python) { throw "Python is still unavailable after installation. Reopen PowerShell or check PATH." } Write-Host "Python ready: $($python.Path), version $($python.Version)" return $python } function Invoke-Python { param( [object]$Python, [string[]]$Arguments ) $allArgs = @($Python.Args) + $Arguments & $Python.Exe @allArgs if ($LASTEXITCODE -ne 0) { throw "Python command failed: $($Python.Exe) $($allArgs -join ' ')" } } function Ensure-Venv { param([object]$Python) if (-not (Test-Path $VenvPython)) { Write-Step "Creating virtual environment" Invoke-Python -Python $Python -Arguments @("-m", "venv", $Venv) } else { Write-Host "Virtual environment already exists: $Venv" } if (-not (Test-Path $VenvPython)) { throw "Failed to create virtual environment: $VenvPython does not exist." } } function Install-Requirements { if (-not (Test-Path $Req)) { throw "requirements.txt was not found: $Req" } Write-Step "Installing dependencies" $wheels = @() if (Test-Path $WheelDir) { $wheels = Get-ChildItem -Path $WheelDir -Filter *.whl -File -ErrorAction SilentlyContinue } if ($wheels.Count -gt 0) { Write-Host "Offline wheel directory found: $WheelDir" & $VenvPython -m pip install --no-index --find-links $WheelDir -r $Req } else { Write-Host "No offline wheels found. Installing dependencies from the network." & $VenvPython -m pip install -r $Req } if ($LASTEXITCODE -ne 0) { throw "Dependency installation failed. If the server is offline, run scripts\download_wheels.ps1 on an online machine first and copy vendor\wheels to this server." } } function Test-ApplicationImport { Write-Step "Verifying environment" & $VenvPython -c "import fastapi, uvicorn, xlrd, xlwt, xlutils, httpx, yaml; print('Dependency verification passed')" if ($LASTEXITCODE -ne 0) { throw "Dependency verification failed." } } try { Set-Location $Root Write-Step "Preparing PCFAnalysis environment" $python = Ensure-Python Ensure-Venv -Python $python Install-Requirements Test-ApplicationImport Write-Host "" Write-Host "Environment installation completed. Run scripts\run.ps1 to start the service." -ForegroundColor Green } catch { Write-Host "" Write-Host "Installation failed:" -ForegroundColor Red Write-Host $_.Exception.Message -ForegroundColor Red exit 1 } finally { Wait-BeforeExit }