2 Commits

Author SHA1 Message Date
Ovidiu U
fdcf253ca7 Skip placeholder-coordinate postcodes (lat >= 90) in ONSPD import
ONS marks non-geographic postcodes (no grid reference) with a placeholder
latitude of 99.999999. The "Latest Centroids" export shipped ~12,789 such
rows, which were imported as real postcodes pointing at lat 99.99 and would
poison nearest-station distance maths. Drop them at ingest, with a test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 09:26:56 +01:00
Ovidiu U
cf373a85f9 Add deploy.sh for repeatable VPS deploys
One-command deploy: maintenance mode, checkout ref (branch or tag), conditional
composer install / npm build (only when their inputs changed), migrate, cache
rebuilds, queue restart, back up. Aborts and stays in maintenance mode on
failure. Mirrors docs/ops/deployment.md §8.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 09:13:35 +01:00
3 changed files with 94 additions and 0 deletions

View File

@@ -109,6 +109,13 @@ final class ImportPostcodes extends Command
continue; continue;
} }
// ONS marks non-geographic postcodes (no grid reference) with a
// placeholder latitude of 99.999999 — drop them so they don't
// poison nearest-station distance maths with a bogus location.
if (abs((float) $lat) >= 90) {
continue;
}
$pcd = strtoupper(preg_replace('/\s+/', '', (string) $row[$columns[$pcdColumn]])); $pcd = strtoupper(preg_replace('/\s+/', '', (string) $row[$columns[$pcdColumn]]));
if ($pcd === '' || strlen($pcd) < 5) { if ($pcd === '' || strlen($pcd) < 5) {

72
deploy.sh Executable file
View File

@@ -0,0 +1,72 @@
#!/usr/bin/env bash
#
# FuelAlert deploy script — run on the VPS from the project root:
#
# ./deploy.sh # deploy the latest main
# ./deploy.sh v0.1.3 # deploy a specific tag
#
# It puts the site in maintenance mode, updates the code, runs migrations and
# cache rebuilds, restarts the queue, then brings the site back up. composer
# install and npm build only run when their inputs actually changed, so most
# deploys skip them. If any step fails the script aborts and the site stays in
# maintenance mode on purpose — fix the issue, then re-run.
#
# See docs/ops/deployment.md for first-time setup and troubleshooting.
set -euo pipefail
cd "$(dirname "$0")"
REF="${1:-main}"
echo "==> Deploying ref: ${REF}"
# Remember the current commit so we can see what changed after checkout.
BEFORE="$(git rev-parse HEAD)"
echo "==> Maintenance mode on"
php artisan down --retry=15
git fetch --tags --prune origin
git checkout "${REF}"
# Fast-forward to the remote only when on a branch (a tag leaves a detached HEAD).
if git symbolic-ref -q HEAD >/dev/null; then
git pull --ff-only
fi
AFTER="$(git rev-parse HEAD)"
CHANGED="$(git diff --name-only "${BEFORE}" "${AFTER}" || true)"
# Reinstall PHP deps only if the lockfile moved.
if grep -q '^composer\.lock$' <<<"${CHANGED}"; then
echo "==> composer.lock changed — installing PHP deps"
composer install --no-dev --optimize-autoloader
else
echo "==> composer.lock unchanged — skipping composer install"
fi
# Rebuild the Vue SPA only if frontend sources or the JS lockfile moved.
if grep -qE '^(package(-lock)?\.json|vite\.config\.|resources/(js|css)/)' <<<"${CHANGED}"; then
echo "==> Frontend changed — rebuilding SPA"
npm ci
npm run build
else
echo "==> No frontend changes — skipping npm build"
fi
echo "==> Running migrations"
php artisan migrate --force
echo "==> Rebuilding caches"
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
echo "==> Restarting queue workers"
php artisan queue:restart
echo "==> Maintenance mode off"
php artisan up
echo "==> Deploy complete"
php artisan about

View File

@@ -63,6 +63,21 @@ CSV;
->and(Postcode::find('BT11AA'))->toBeNull(); ->and(Postcode::find('BT11AA'))->toBeNull();
}); });
it('skips postcodes with placeholder coordinates (no grid reference)', function (): void {
$csv = <<<'CSV'
pcd,pcds,doterm,lat,long
"SW1A1AA","SW1A 1AA","",51.501009,-0.141588
"GIR0AA","GIR 0AA","",99.999999,0.000000
CSV;
$path = writeOnspdFixture($csv);
$this->artisan('postcodes:import', ['--file' => $path])->assertSuccessful();
expect(Postcode::count())->toBe(1)
->and(Postcode::find('GIR0AA'))->toBeNull();
});
it('accepts ArcGIS ONSPD exports that use PCD7 instead of PCD', function (): void { it('accepts ArcGIS ONSPD exports that use PCD7 instead of PCD', function (): void {
$csv = <<<'CSV' $csv = <<<'CSV'
OBJECTID,PCD7,PCD8,PCDS,DOTERM,LAT,LONG,x,y OBJECTID,PCD7,PCD8,PCDS,DOTERM,LAT,LONG,x,y