- API Platform + Doctrine ORM + jsor/doctrine-postgis installés - 6 entités Symfony (SargassumObservation, SargassumForecast, DataIngestionJob, CoastalPoint, ImpactScore, UserFeedback) - Migration initiale manuelle avec CREATE EXTENSION postgis, toutes les tables, index GIST et BTREE - doctrine.yaml configuré pour PostgreSQL 16 + types PostGIS - Roadmap Phase 0 complète Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
148 lines
6.9 KiB
PHP
148 lines
6.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace DoctrineMigrations;
|
|
|
|
use Doctrine\DBAL\Schema\Schema;
|
|
use Doctrine\Migrations\AbstractMigration;
|
|
|
|
final class Version20260401000000 extends AbstractMigration
|
|
{
|
|
public function getDescription(): string
|
|
{
|
|
return 'Schéma initial — toutes les entités + extension PostGIS + index spatiaux';
|
|
}
|
|
|
|
public function up(Schema $schema): void
|
|
{
|
|
$this->addSql('CREATE EXTENSION IF NOT EXISTS postgis');
|
|
|
|
// SargassumObservation
|
|
$this->addSql('
|
|
CREATE TABLE sargassum_observation (
|
|
id UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
geometry geometry(MULTIPOLYGON, 4326) NOT NULL,
|
|
bbox geometry(POLYGON, 4326),
|
|
detected_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
|
source VARCHAR(50) NOT NULL,
|
|
tile_id VARCHAR(100) NOT NULL,
|
|
cloud_coverage DOUBLE PRECISION NOT NULL,
|
|
confidence DOUBLE PRECISION NOT NULL,
|
|
afai_mean DOUBLE PRECISION NOT NULL,
|
|
afai_std DOUBLE PRECISION NOT NULL,
|
|
coverage_area DOUBLE PRECISION,
|
|
processing_version VARCHAR(50),
|
|
created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
|
PRIMARY KEY(id)
|
|
)
|
|
');
|
|
$this->addSql('CREATE INDEX CONCURRENTLY idx_observation_detected_at ON sargassum_observation (detected_at)');
|
|
$this->addSql('CREATE INDEX CONCURRENTLY idx_observation_source ON sargassum_observation (source)');
|
|
$this->addSql('CREATE INDEX CONCURRENTLY idx_observation_geometry ON sargassum_observation USING GIST (geometry)');
|
|
$this->addSql('CREATE INDEX CONCURRENTLY idx_observation_bbox ON sargassum_observation USING GIST (bbox)');
|
|
|
|
// SargassumForecast
|
|
$this->addSql('
|
|
CREATE TABLE sargassum_forecast (
|
|
id UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
source_observation_id UUID NOT NULL,
|
|
geometry geometry(MULTIPOLYGON, 4326) NOT NULL,
|
|
valid_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
|
computed_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
|
model_version VARCHAR(50) NOT NULL,
|
|
time_horizon INT NOT NULL,
|
|
confidence DOUBLE PRECISION NOT NULL,
|
|
drift_vector_avg geometry(POINT, 4326),
|
|
created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
|
PRIMARY KEY(id)
|
|
)
|
|
');
|
|
$this->addSql('ALTER TABLE sargassum_forecast ADD CONSTRAINT fk_forecast_observation FOREIGN KEY (source_observation_id) REFERENCES sargassum_observation (id) ON DELETE CASCADE');
|
|
$this->addSql('CREATE INDEX CONCURRENTLY idx_forecast_valid_at ON sargassum_forecast (valid_at)');
|
|
$this->addSql('CREATE INDEX CONCURRENTLY idx_forecast_horizon ON sargassum_forecast (time_horizon)');
|
|
$this->addSql('CREATE INDEX CONCURRENTLY idx_forecast_geometry ON sargassum_forecast USING GIST (geometry)');
|
|
|
|
// DataIngestionJob
|
|
$this->addSql('
|
|
CREATE TABLE data_ingestion_job (
|
|
id UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
source VARCHAR(50) NOT NULL,
|
|
tile_id VARCHAR(100) NOT NULL,
|
|
requested_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
|
processed_at TIMESTAMP(0) WITHOUT TIME ZONE,
|
|
status VARCHAR(20) NOT NULL DEFAULT \'pending\',
|
|
retry_count INT NOT NULL DEFAULT 0,
|
|
error_message TEXT,
|
|
PRIMARY KEY(id)
|
|
)
|
|
');
|
|
$this->addSql('CREATE INDEX CONCURRENTLY idx_job_status ON data_ingestion_job (status)');
|
|
$this->addSql('CREATE INDEX CONCURRENTLY idx_job_requested_at ON data_ingestion_job (requested_at)');
|
|
|
|
// CoastalPoint
|
|
$this->addSql('
|
|
CREATE TABLE coastal_point (
|
|
id UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
name VARCHAR(150) NOT NULL,
|
|
geometry geometry(POINT, 4326) NOT NULL,
|
|
type VARCHAR(30) NOT NULL,
|
|
region VARCHAR(100) NOT NULL,
|
|
PRIMARY KEY(id)
|
|
)
|
|
');
|
|
$this->addSql('CREATE INDEX CONCURRENTLY idx_coastal_type ON coastal_point (type)');
|
|
$this->addSql('CREATE INDEX CONCURRENTLY idx_coastal_region ON coastal_point (region)');
|
|
$this->addSql('CREATE INDEX CONCURRENTLY idx_coastal_geometry ON coastal_point USING GIST (geometry)');
|
|
|
|
// ImpactScore
|
|
$this->addSql('
|
|
CREATE TABLE impact_score (
|
|
id UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
coastal_point_id UUID NOT NULL,
|
|
timestamp TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
|
score INT NOT NULL,
|
|
level VARCHAR(20) NOT NULL,
|
|
distance_to_nearest_sargassum DOUBLE PRECISION,
|
|
density_estimate DOUBLE PRECISION,
|
|
trend VARCHAR(20),
|
|
PRIMARY KEY(id)
|
|
)
|
|
');
|
|
$this->addSql('ALTER TABLE impact_score ADD CONSTRAINT fk_score_coastal_point FOREIGN KEY (coastal_point_id) REFERENCES coastal_point (id) ON DELETE CASCADE');
|
|
$this->addSql('CREATE INDEX CONCURRENTLY idx_score_timestamp ON impact_score (timestamp)');
|
|
$this->addSql('CREATE INDEX CONCURRENTLY idx_score_level ON impact_score (level)');
|
|
|
|
// UserFeedback
|
|
$this->addSql('
|
|
CREATE TABLE user_feedback (
|
|
id UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
coastal_point_id UUID,
|
|
location geometry(POINT, 4326) NOT NULL,
|
|
timestamp TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
|
has_seaweed BOOLEAN NOT NULL,
|
|
density VARCHAR(20),
|
|
source VARCHAR(100),
|
|
PRIMARY KEY(id)
|
|
)
|
|
');
|
|
$this->addSql('ALTER TABLE user_feedback ADD CONSTRAINT fk_feedback_coastal_point FOREIGN KEY (coastal_point_id) REFERENCES coastal_point (id) ON DELETE SET NULL');
|
|
$this->addSql('CREATE INDEX CONCURRENTLY idx_feedback_timestamp ON user_feedback (timestamp)');
|
|
$this->addSql('CREATE INDEX CONCURRENTLY idx_feedback_location ON user_feedback USING GIST (location)');
|
|
}
|
|
|
|
public function down(Schema $schema): void
|
|
{
|
|
$this->addSql('ALTER TABLE sargassum_forecast DROP CONSTRAINT fk_forecast_observation');
|
|
$this->addSql('ALTER TABLE impact_score DROP CONSTRAINT fk_score_coastal_point');
|
|
$this->addSql('ALTER TABLE user_feedback DROP CONSTRAINT fk_feedback_coastal_point');
|
|
|
|
$this->addSql('DROP TABLE IF EXISTS user_feedback');
|
|
$this->addSql('DROP TABLE IF EXISTS impact_score');
|
|
$this->addSql('DROP TABLE IF EXISTS sargassum_forecast');
|
|
$this->addSql('DROP TABLE IF EXISTS sargassum_observation');
|
|
$this->addSql('DROP TABLE IF EXISTS coastal_point');
|
|
$this->addSql('DROP TABLE IF EXISTS data_ingestion_job');
|
|
}
|
|
}
|