From f74a9b52591733dc9e63632f72aaab430f941acc Mon Sep 17 00:00:00 2001 From: JORGE Date: Thu, 3 Oct 2024 11:12:30 -0400 Subject: [PATCH 01/64] [TM-1127] add migration to create table delayed jobs --- ...10_03_150604_create_delayed_jobs_table.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 database/migrations/2024_10_03_150604_create_delayed_jobs_table.php diff --git a/database/migrations/2024_10_03_150604_create_delayed_jobs_table.php b/database/migrations/2024_10_03_150604_create_delayed_jobs_table.php new file mode 100644 index 000000000..43b22fc39 --- /dev/null +++ b/database/migrations/2024_10_03_150604_create_delayed_jobs_table.php @@ -0,0 +1,31 @@ +id(); + $table->uuid('uuid')->unique(); + $table->enum('status', ['pending', 'failed', 'succeeded']); + $table->integer('statusCode')->nullable(); + $table->string('payload'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('delayed_jobs'); + } +}; From bfe6494354a95376f028539eaf5d23696152f954 Mon Sep 17 00:00:00 2001 From: JORGE Date: Thu, 3 Oct 2024 12:29:20 -0400 Subject: [PATCH 02/64] [TM-1127] create job and move function insert to service --- .../TerrafundCreateGeometryController.php | 58 ++------- app/Jobs/InsertGeojsonToDBJob.php | 110 ++++++++++++++++++ app/Models/DelayedJob.php | 61 ++++++++++ app/Services/PolygonService.php | 43 +++++++ ...10_03_150604_create_delayed_jobs_table.php | 3 +- 5 files changed, 225 insertions(+), 50 deletions(-) create mode 100755 app/Jobs/InsertGeojsonToDBJob.php create mode 100644 app/Models/DelayedJob.php diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index aed9f85cc..d1c3215df 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -65,45 +65,6 @@ public function storeGeometry(Request $request) return response()->json(['uuid' => $polygonGeometry->uuid], 200); } - /** - * @throws ValidationException - */ - public function insertGeojsonToDB(string $geojsonFilename, ?string $entity_uuid = null, ?string $entity_type = null, ?string $primary_uuid = null, ?bool $submit_polygon_loaded = false) - { - try { - $tempDir = sys_get_temp_dir(); - $geojsonPath = $tempDir . DIRECTORY_SEPARATOR . $geojsonFilename; - $geojsonData = file_get_contents($geojsonPath); - $service = App::make(PolygonService::class); - if ($entity_type === 'project' || $entity_type === 'project-pitch') { - $entity = $service->getEntity($entity_type, $entity_uuid); - $hasBeenDeleted = GeometryHelper::deletePolygonWithRelated($entity); - if ($entity && $hasBeenDeleted) { - return $service->createProjectPolygon($entity, $geojsonData); - } else { - return ['error' => 'Entity not found']; - } - } else { - $geojson = json_decode($geojsonData, true); - SitePolygonValidator::validate('FEATURE_BOUNDS', $geojson, false); - SitePolygonValidator::validate('GEOMETRY_TYPE', $geojson, false); - - return $service->createGeojsonModels($geojson, ['site_id' => $entity_uuid, 'source' => PolygonService::UPLOADED_SOURCE], $primary_uuid, $submit_polygon_loaded); - - } - - } catch (Exception $e) { - $errorMessage = $e->getMessage(); - $decodedErrorMessage = json_decode($errorMessage, true); - if (json_last_error() === JSON_ERROR_NONE) { - return ['error' => $decodedErrorMessage]; - } else { - Log::info('Error inserting geojson to DB', ['error' => $errorMessage]); - - return ['error' => $errorMessage]; - } - } - } public function validateDataInDB(Request $request) { @@ -205,7 +166,8 @@ function ($attribute, $value, $fail) { return response()->json(['error' => 'Failed to convert KML to GeoJSON', 'message' => $process->getErrorOutput()], 500); } - $uuid = $this->insertGeojsonToDB($geojsonFilename, $entity_uuid, $entity_type); + + $uuid = App::make(PolygonService::class)->insertGeojsonToDB($geojsonFilename, $entity_uuid, $entity_type); if (isset($uuid['error'])) { return response()->json(['error' => 'Geometry not inserted into DB', 'message' => $uuid['error']], 500); } @@ -262,7 +224,7 @@ function ($attribute, $value, $fail) { $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && filter_var($body['submit_polygon_loaded'], FILTER_VALIDATE_BOOLEAN); if (! $polygonLoadedList && ! $submitPolygonsLoaded) { - $uuid = $this->insertGeojsonToDB($geojsonFilename, $site_id, null, $body['primary_uuid'] ?? null); + $uuid = App::make(PolygonService::class)->insertGeojsonToDB($geojsonFilename, $site_id, 'site', $body['primary_uuid'] ?? null); } if ($polygonLoadedList) { @@ -273,7 +235,7 @@ function ($attribute, $value, $fail) { } if ($submitPolygonsLoaded) { - $uuid = $this->insertGeojsonToDB($filename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); + $uuid = App::make(PolygonService::class)->insertGeojsonToDB($filename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); } if (isset($uuid['error'])) { @@ -359,7 +321,7 @@ public function uploadShapefileProject(Request $request) return response()->json(['error' => 'Failed to convert Shapefile to GeoJSON', 'message' => $process->getErrorOutput()], 500); } - $uuid = $this->insertGeojsonToDB($geojsonFilename, $entity_uuid, $entity_type); + $uuid = App::make(PolygonService::class)->insertGeojsonToDB($geojsonFilename, $entity_uuid, $entity_type); if (isset($uuid['error'])) { return response()->json(['error' => 'Geometry not inserted into DB', 'message' => $uuid['error']], 500); } @@ -418,7 +380,7 @@ public function uploadShapefile(Request $request) $polygonLoadedList = isset($body['polygon_loaded']) && filter_var($body['polygon_loaded'], FILTER_VALIDATE_BOOLEAN); $submitPolygonsLoaded = $request->input('submit_polygon_loaded') === true || $request->input('submit_polygon_loaded') === 'true'; if (! $polygonLoadedList && ! $submitPolygonsLoaded) { - $uuid = $this->insertGeojsonToDB($geojsonFilename, $site_id, 'site', $body['primary_uuid'] ?? null); + $uuid = App::make(PolygonService::class)->insertGeojsonToDB($geojsonFilename, $site_id, 'site', $body['primary_uuid'] ?? null); } if ($polygonLoadedList) { @@ -430,7 +392,7 @@ public function uploadShapefile(Request $request) } if ($submitPolygonsLoaded) { - $uuid = $this->insertGeojsonToDB($geojsonFilename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); + $uuid = App::make(PolygonService::class)->insertGeojsonToDB($geojsonFilename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); } if (isset($uuid['error'])) { return response()->json(['error' => 'Geometry not inserted into DB', 'message' => $uuid['error']], 500); @@ -596,7 +558,7 @@ public function uploadGeoJSONFileProject(Request $request) $tempDir = sys_get_temp_dir(); $filename = uniqid('geojson_file_') . '.' . $file->getClientOriginalExtension(); $file->move($tempDir, $filename); - $uuid = $this->insertGeojsonToDB($filename, $entity_uuid, $entity_type); + $uuid = App::make(PolygonService::class)->insertGeojsonToDB($filename, $entity_uuid, $entity_type); if (is_array($uuid) && isset($uuid['error'])) { return response()->json(['error' => 'Failed to insert GeoJSON data into the database', 'message' => $uuid['error']], 500); } @@ -630,7 +592,7 @@ public function uploadGeoJSONFile(Request $request) $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && filter_var($body['submit_polygon_loaded'], FILTER_VALIDATE_BOOLEAN); if (! $polygonLoadedList && ! $submitPolygonsLoaded) { - $uuid = $this->insertGeojsonToDB($filename, $site_id, 'site', $body['primary_uuid'] ?? null); + $uuid = App::make(PolygonService::class)->insertGeojsonToDB($filename, $site_id, 'site', $body['primary_uuid'] ?? null); } if ($polygonLoadedList) { @@ -642,7 +604,7 @@ public function uploadGeoJSONFile(Request $request) } if ($submitPolygonsLoaded) { - $uuid = $this->insertGeojsonToDB($filename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); + $uuid = App::make(PolygonService::class)->insertGeojsonToDB($filename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); } if (is_array($uuid) && isset($uuid['error'])) { diff --git a/app/Jobs/InsertGeojsonToDBJob.php b/app/Jobs/InsertGeojsonToDBJob.php new file mode 100755 index 000000000..16e25eb03 --- /dev/null +++ b/app/Jobs/InsertGeojsonToDBJob.php @@ -0,0 +1,110 @@ +geojsonFilename = $geojsonFilename; + $this->entity_uuid = $entity_uuid; + $this->entity_type = $entity_type; + $this->primary_uuid = $primary_uuid; + $this->submit_polygon_loaded = $submit_polygon_loaded; + $this->job_uuid = Str::uuid(); + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + try { + DelayedJob::create([ + 'uuid' => $this->job_uuid, + 'status' => self::STATUS_PENDING, + 'created_at' => now(), + ]); + $tempDir = sys_get_temp_dir(); + $geojsonPath = $tempDir . DIRECTORY_SEPARATOR . $this->geojsonFilename; + $geojsonData = file_get_contents($geojsonPath); + + $service = App::make(PolygonService::class); + + if ($this->entity_type === 'project' || $this->entity_type === 'project-pitch') { + $entity = $service->getEntity($this->entity_type, $this->entity_uuid); + $hasBeenDeleted = GeometryHelper::deletePolygonWithRelated($entity); + + if ($entity && $hasBeenDeleted) { + $payload = $service->createProjectPolygon($entity, $geojsonData); + } else { + Log::error('Entity not found'); + } + } else { + $geojson = json_decode($geojsonData, true); + + SitePolygonValidator::validate('FEATURE_BOUNDS', $geojson, false); + SitePolygonValidator::validate('GEOMETRY_TYPE', $geojson, false); + + $payload = $service->createGeojsonModels($geojson, ['site_id' => $this->entity_uuid, 'source' => PolygonService::UPLOADED_SOURCE], $this->primary_uuid, $this->submit_polygon_loaded); + } + DelayedJob::where('uuid', $this->job_uuid)->update([ + 'status' => self::STATUS_SUCCEEDED, + 'payload' => json_encode($payload), + 'updated_at' => now(), + ]); + } catch (Exception $e) { + DelayedJob::where('uuid', $this->job_uuid)->update([ + 'status' => self::STATUS_FAILED, + 'payload' => json_encode(['error' => $e->getMessage()]), + 'updated_at' => now(), + ]); + $errorMessage = $e->getMessage(); + Log::error('Error inserting GeoJSON to DB', ['error' => $errorMessage]); + } + } +} diff --git a/app/Models/DelayedJob.php b/app/Models/DelayedJob.php new file mode 100644 index 000000000..d99016646 --- /dev/null +++ b/app/Models/DelayedJob.php @@ -0,0 +1,61 @@ + 'string', + ]; + + /** + * Get the current status of the job. + * + * @return string + */ + public function getStatus(): string + { + return $this->status; + } + + /** + * Set the status of the job. + * + * @param string $status + * @return void + */ + public function setStatus(string $status): void + { + $this->status = $status; + $this->save(); + } + + /** + * Update the status code for the job. + * + * @param int|null $code + * @return void + */ + public function updateStatusCode(?int $code): void + { + $this->statusCode = $code; + $this->save(); + } +} diff --git a/app/Services/PolygonService.php b/app/Services/PolygonService.php index 7acf9a853..8a50786a0 100755 --- a/app/Services/PolygonService.php +++ b/app/Services/PolygonService.php @@ -475,4 +475,47 @@ private function isValidDate($date) return false; } } + /** + * @throws ValidationException + */ + public function insertGeojsonToDB(string $geojsonFilename, ?string $entity_uuid = null, ?string $entity_type = null, ?string $primary_uuid = null, ?bool $submit_polygon_loaded = false) + { + try { + $tempDir = sys_get_temp_dir(); + $geojsonPath = $tempDir . DIRECTORY_SEPARATOR . $geojsonFilename; + $geojsonData = file_get_contents($geojsonPath); + + $service = App::make(PolygonService::class); + + if ($entity_type === 'project' || $entity_type === 'project-pitch') { + $entity = $service->getEntity($entity_type, $entity_uuid); + $hasBeenDeleted = GeometryHelper::deletePolygonWithRelated($entity); + + if ($entity && $hasBeenDeleted) { + return $service->createProjectPolygon($entity, $geojsonData); + } else { + return ['error' => 'Entity not found']; + } + } else { + $geojson = json_decode($geojsonData, true); + + SitePolygonValidator::validate('FEATURE_BOUNDS', $geojson, false); + SitePolygonValidator::validate('GEOMETRY_TYPE', $geojson, false); + + return $service->createGeojsonModels($geojson, ['site_id' => $entity_uuid, 'source' => PolygonService::UPLOADED_SOURCE], $primary_uuid, $submit_polygon_loaded); + + } + + } catch (Exception $e) { + $errorMessage = $e->getMessage(); + $decodedErrorMessage = json_decode($errorMessage, true); + if (json_last_error() === JSON_ERROR_NONE) { + return ['error' => $decodedErrorMessage]; + } else { + Log::info('Error inserting geojson to DB', ['error' => $errorMessage]); + + return ['error' => $errorMessage]; + } + } + } } diff --git a/database/migrations/2024_10_03_150604_create_delayed_jobs_table.php b/database/migrations/2024_10_03_150604_create_delayed_jobs_table.php index 43b22fc39..628be6933 100644 --- a/database/migrations/2024_10_03_150604_create_delayed_jobs_table.php +++ b/database/migrations/2024_10_03_150604_create_delayed_jobs_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ From cdafd2ee2d6b9660051c2641917ba7e0410f181e Mon Sep 17 00:00:00 2001 From: JORGE Date: Thu, 3 Oct 2024 14:24:41 -0400 Subject: [PATCH 03/64] [TM-1127] start saving and runing jobs --- .../TerrafundCreateGeometryController.php | 14 ++-- app/Jobs/InsertGeojsonToDBJob.php | 65 +++++++++---------- app/Services/PolygonService.php | 9 ++- 3 files changed, 41 insertions(+), 47 deletions(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index d1c3215df..3efc5008a 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -4,6 +4,7 @@ use App\Helpers\GeometryHelper; use App\Http\Controllers\Controller; +use App\Jobs\InsertGeojsonToDBJob; use App\Models\V2\PolygonGeometry; use App\Models\V2\Sites\Site; use App\Models\V2\Sites\SitePolygon; @@ -380,7 +381,7 @@ public function uploadShapefile(Request $request) $polygonLoadedList = isset($body['polygon_loaded']) && filter_var($body['polygon_loaded'], FILTER_VALIDATE_BOOLEAN); $submitPolygonsLoaded = $request->input('submit_polygon_loaded') === true || $request->input('submit_polygon_loaded') === 'true'; if (! $polygonLoadedList && ! $submitPolygonsLoaded) { - $uuid = App::make(PolygonService::class)->insertGeojsonToDB($geojsonFilename, $site_id, 'site', $body['primary_uuid'] ?? null); + $job = new InsertGeojsonToDBJob($geojsonFilename, $site_id, 'site', $body['primary_uuid'] ?? null); } if ($polygonLoadedList) { @@ -392,14 +393,11 @@ public function uploadShapefile(Request $request) } if ($submitPolygonsLoaded) { - $uuid = App::make(PolygonService::class)->insertGeojsonToDB($geojsonFilename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); + $job = new InsertGeojsonToDBJob($geojsonFilename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); } - if (isset($uuid['error'])) { - return response()->json(['error' => 'Geometry not inserted into DB', 'message' => $uuid['error']], 500); - } - App::make(SiteService::class)->setSiteToRestorationInProgress($site_id); - - return response()->json(['message' => 'Shape file processed and inserted successfully', 'uuid' => $uuid], 200); + $jobUUID = $job->getJobUuid(); + dispatch($job); + return response()->json(['message' => 'Shape file processed and inserted successfully', 'uuid' => $jobUUID], 200); } else { return response()->json(['error' => 'Failed to open the ZIP file'], 400); } diff --git a/app/Jobs/InsertGeojsonToDBJob.php b/app/Jobs/InsertGeojsonToDBJob.php index 16e25eb03..e17cc46e7 100755 --- a/app/Jobs/InsertGeojsonToDBJob.php +++ b/app/Jobs/InsertGeojsonToDBJob.php @@ -5,6 +5,7 @@ use App\Helpers\GeometryHelper; use App\Models\DelayedJob; use App\Services\PolygonService; +use App\Services\SiteService; use App\Validators\SitePolygonValidator; use Exception; use Illuminate\Bus\Queueable; @@ -61,50 +62,46 @@ public function __construct(string $geojsonFilename, ?string $entity_uuid = null * * @return void */ - public function handle() + public function handle(PolygonService $service) { try { + Log::info('starting the job to sleep zzz....', ['job_uuid' => $this->job_uuid]); DelayedJob::create([ - 'uuid' => $this->job_uuid, - 'status' => self::STATUS_PENDING, - 'created_at' => now(), + 'uuid' => $this->job_uuid, + 'status' => self::STATUS_PENDING, + 'created_at' => now(), ]); - $tempDir = sys_get_temp_dir(); - $geojsonPath = $tempDir . DIRECTORY_SEPARATOR . $this->geojsonFilename; - $geojsonData = file_get_contents($geojsonPath); - - $service = App::make(PolygonService::class); - - if ($this->entity_type === 'project' || $this->entity_type === 'project-pitch') { - $entity = $service->getEntity($this->entity_type, $this->entity_uuid); - $hasBeenDeleted = GeometryHelper::deletePolygonWithRelated($entity); - - if ($entity && $hasBeenDeleted) { - $payload = $service->createProjectPolygon($entity, $geojsonData); - } else { - Log::error('Entity not found'); - } - } else { - $geojson = json_decode($geojsonData, true); - - SitePolygonValidator::validate('FEATURE_BOUNDS', $geojson, false); - SitePolygonValidator::validate('GEOMETRY_TYPE', $geojson, false); - - $payload = $service->createGeojsonModels($geojson, ['site_id' => $this->entity_uuid, 'source' => PolygonService::UPLOADED_SOURCE], $this->primary_uuid, $this->submit_polygon_loaded); + $uuids = $service->insertGeojsonToDB( + $this->geojsonFilename, + $this->entity_uuid, + $this->entity_type, + $this->primary_uuid, + $this->submit_polygon_loaded + ); + if (isset($uuids['error'])) { + throw new Exception($uuids['error']); } + App::make(SiteService::class)->setSiteToRestorationInProgress($this->entity_uuid); DelayedJob::where('uuid', $this->job_uuid)->update([ - 'status' => self::STATUS_SUCCEEDED, - 'payload' => json_encode($payload), - 'updated_at' => now(), + 'status' => self::STATUS_SUCCEEDED, + 'payload' => json_encode($uuids), + 'updated_at' => now(), ]); + } catch (Exception $e) { DelayedJob::where('uuid', $this->job_uuid)->update([ - 'status' => self::STATUS_FAILED, - 'payload' => json_encode(['error' => $e->getMessage()]), - 'updated_at' => now(), + 'status' => self::STATUS_FAILED, + 'payload' => json_encode(['error' => $e->getMessage()]), + 'updated_at' => now(), ]); - $errorMessage = $e->getMessage(); - Log::error('Error inserting GeoJSON to DB', ['error' => $errorMessage]); + + Log::error('Error inserting GeoJSON to DB', ['error' => $e->getMessage()]); } } + public function getJobUuid() + { + return $this->job_uuid; + } + + } diff --git a/app/Services/PolygonService.php b/app/Services/PolygonService.php index 8a50786a0..effc1a34c 100755 --- a/app/Services/PolygonService.php +++ b/app/Services/PolygonService.php @@ -485,14 +485,13 @@ public function insertGeojsonToDB(string $geojsonFilename, ?string $entity_uuid $geojsonPath = $tempDir . DIRECTORY_SEPARATOR . $geojsonFilename; $geojsonData = file_get_contents($geojsonPath); - $service = App::make(PolygonService::class); - if ($entity_type === 'project' || $entity_type === 'project-pitch') { - $entity = $service->getEntity($entity_type, $entity_uuid); + $entity = $this->getEntity($entity_type, $entity_uuid); + $hasBeenDeleted = GeometryHelper::deletePolygonWithRelated($entity); if ($entity && $hasBeenDeleted) { - return $service->createProjectPolygon($entity, $geojsonData); + return $this->createProjectPolygon($entity, $geojsonData); } else { return ['error' => 'Entity not found']; } @@ -502,7 +501,7 @@ public function insertGeojsonToDB(string $geojsonFilename, ?string $entity_uuid SitePolygonValidator::validate('FEATURE_BOUNDS', $geojson, false); SitePolygonValidator::validate('GEOMETRY_TYPE', $geojson, false); - return $service->createGeojsonModels($geojson, ['site_id' => $entity_uuid, 'source' => PolygonService::UPLOADED_SOURCE], $primary_uuid, $submit_polygon_loaded); + return $this->createGeojsonModels($geojson, ['site_id' => $entity_uuid, 'source' => PolygonService::UPLOADED_SOURCE], $primary_uuid, $submit_polygon_loaded); } From b54a022aeac108e8e3ab7171d99177e8553b6362 Mon Sep 17 00:00:00 2001 From: JORGE Date: Fri, 4 Oct 2024 10:48:04 -0400 Subject: [PATCH 04/64] [TM-1127] receive correctly the error message in catch --- app/Jobs/InsertGeojsonToDBJob.php | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/app/Jobs/InsertGeojsonToDBJob.php b/app/Jobs/InsertGeojsonToDBJob.php index e17cc46e7..3f8796b4a 100755 --- a/app/Jobs/InsertGeojsonToDBJob.php +++ b/app/Jobs/InsertGeojsonToDBJob.php @@ -17,6 +17,7 @@ use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; +use Throwable; class InsertGeojsonToDBJob implements ShouldQueue { @@ -54,7 +55,7 @@ public function __construct(string $geojsonFilename, ?string $entity_uuid = null $this->entity_type = $entity_type; $this->primary_uuid = $primary_uuid; $this->submit_polygon_loaded = $submit_polygon_loaded; - $this->job_uuid = Str::uuid(); + $this->job_uuid = Str::uuid()->toString(); } /** @@ -65,7 +66,6 @@ public function __construct(string $geojsonFilename, ?string $entity_uuid = null public function handle(PolygonService $service) { try { - Log::info('starting the job to sleep zzz....', ['job_uuid' => $this->job_uuid]); DelayedJob::create([ 'uuid' => $this->job_uuid, 'status' => self::STATUS_PENDING, @@ -78,8 +78,13 @@ public function handle(PolygonService $service) $this->primary_uuid, $this->submit_polygon_loaded ); - if (isset($uuids['error'])) { - throw new Exception($uuids['error']); + if (isset($uuids['error'])) { + $errorMessage = is_array($uuids['error']) + ? json_encode($uuids['error'], JSON_PRETTY_PRINT) + : strval($uuids['error']); + + throw new \Exception($errorMessage); + } App::make(SiteService::class)->setSiteToRestorationInProgress($this->entity_uuid); DelayedJob::where('uuid', $this->job_uuid)->update([ @@ -91,11 +96,9 @@ public function handle(PolygonService $service) } catch (Exception $e) { DelayedJob::where('uuid', $this->job_uuid)->update([ 'status' => self::STATUS_FAILED, - 'payload' => json_encode(['error' => $e->getMessage()]), + 'payload' => ['error' => $e->getMessage()], 'updated_at' => now(), ]); - - Log::error('Error inserting GeoJSON to DB', ['error' => $e->getMessage()]); } } public function getJobUuid() From 5e2f56375f3418881ab0d230369cf6d66e522b21 Mon Sep 17 00:00:00 2001 From: cesarLima1 Date: Fri, 4 Oct 2024 12:35:18 -0400 Subject: [PATCH 05/64] [TM-1127] create job for validation --- app/Jobs/runSitePolygonsValidationJob.php | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 app/Jobs/runSitePolygonsValidationJob.php diff --git a/app/Jobs/runSitePolygonsValidationJob.php b/app/Jobs/runSitePolygonsValidationJob.php new file mode 100644 index 000000000..8c0800d67 --- /dev/null +++ b/app/Jobs/runSitePolygonsValidationJob.php @@ -0,0 +1,31 @@ + Date: Fri, 4 Oct 2024 12:59:12 -0400 Subject: [PATCH 06/64] [TM-1127] fix names for post endpoinst validation --- .../V2/Terrafund/TerrafundCreateGeometryController.php | 6 +++--- routes/api_v2.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index 3efc5008a..fcad90df4 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -1169,7 +1169,7 @@ public function runValidationPolygon(string $uuid) $this->validateDataInDB($request); } - public function getValidationPolygon(Request $request) + public function sendRunValidationPolygon(Request $request) { $uuid = $request->input('uuid'); @@ -1179,7 +1179,7 @@ public function getValidationPolygon(Request $request) return $criteriaData; } - public function getSiteValidationPolygon(Request $request) + public function runSiteValidationPolygon(Request $request) { try { $uuid = $request->input('uuid'); @@ -1198,7 +1198,7 @@ public function getSiteValidationPolygon(Request $request) } } - public function getPolygonsValidation(Request $request) + public function runPolygonsValidation(Request $request) { try { $uuids = $request->input('uuids'); diff --git a/routes/api_v2.php b/routes/api_v2.php index 9dd1d1370..558270ddd 100644 --- a/routes/api_v2.php +++ b/routes/api_v2.php @@ -658,9 +658,9 @@ Route::get('/validation/estimated-area-project', [TerrafundCreateGeometryController::class, 'validateEstimatedAreaProject']); Route::get('/validation/estimated-area-site', [TerrafundCreateGeometryController::class, 'validateEstimatedAreaSite']); Route::get('/validation/table-data', [TerrafundCreateGeometryController::class, 'validateDataInDB']); - Route::post('/validation/polygon', [TerrafundCreateGeometryController::class, 'getValidationPolygon']); - Route::post('/validation/polygons', [TerrafundCreateGeometryController::class, 'getPolygonsValidation']); - Route::post('/validation/sitePolygons', [TerrafundCreateGeometryController::class, 'getSiteValidationPolygon']); + Route::post('/validation/polygon', [TerrafundCreateGeometryController::class, 'sendRunValidationPolygon']); + Route::post('/validation/polygons', [TerrafundCreateGeometryController::class, 'runPolygonsValidation']); + Route::post('/validation/sitePolygons', [TerrafundCreateGeometryController::class, 'runSiteValidationPolygon']); Route::get('/validation/site', [TerrafundCreateGeometryController::class, 'getCurrentSiteValidation']); Route::post('/clip-polygons/site/{uuid}', [TerrafundClipGeometryController::class, 'clipOverlappingPolygonsBySite']); Route::post('/clip-polygons/polygon/{uuid}', [TerrafundClipGeometryController::class, 'clipOverlappingPolygon']); From b326797b259aadd50f2dbaa71a9f6f6b3d257e31 Mon Sep 17 00:00:00 2001 From: cesarLima1 Date: Mon, 7 Oct 2024 11:04:14 -0400 Subject: [PATCH 07/64] [TM-1127] start up job for polygon validations --- .../TerrafundCreateGeometryController.php | 14 +- app/Jobs/RunSitePolygonsValidationJob.php | 95 ++++++++ app/Jobs/runSitePolygonsValidationJob.php | 31 --- app/Services/PolygonValidationService.php | 204 ++++++++++++++++++ 4 files changed, 308 insertions(+), 36 deletions(-) create mode 100644 app/Jobs/RunSitePolygonsValidationJob.php delete mode 100644 app/Jobs/runSitePolygonsValidationJob.php create mode 100644 app/Services/PolygonValidationService.php diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index fcad90df4..bed7eb06d 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -5,6 +5,7 @@ use App\Helpers\GeometryHelper; use App\Http\Controllers\Controller; use App\Jobs\InsertGeojsonToDBJob; +use App\Jobs\RunSitePolygonsValidationJob; use App\Models\V2\PolygonGeometry; use App\Models\V2\Sites\Site; use App\Models\V2\Sites\SitePolygon; @@ -1184,13 +1185,16 @@ public function runSiteValidationPolygon(Request $request) try { $uuid = $request->input('uuid'); - $sitePolygonsUuids = GeometryHelper::getSitePolygonsUuids($uuid); + // $sitePolygonsUuids = GeometryHelper::getSitePolygonsUuids($uuid); - foreach ($sitePolygonsUuids as $polygonUuid) { - $this->runValidationPolygon($polygonUuid); - } + // foreach ($sitePolygonsUuids as $polygonUuid) { + // $this->runValidationPolygon($polygonUuid); + // } - return response()->json(['message' => 'Validation completed for all site polygons']); + $job = new RunSitePolygonsValidationJob($uuid); + $jobUUID = $job->getJobUuid(); + dispatch($job); + return response()->json(['message' => 'Validation completed for all site polygons', 'uuid' => $jobUUID], 200); } catch (\Exception $e) { Log::error('Error during site validation polygon: ' . $e->getMessage()); diff --git a/app/Jobs/RunSitePolygonsValidationJob.php b/app/Jobs/RunSitePolygonsValidationJob.php new file mode 100644 index 000000000..eb8db4a44 --- /dev/null +++ b/app/Jobs/RunSitePolygonsValidationJob.php @@ -0,0 +1,95 @@ +uuid = $uuid; + $this->job_uuid = Str::uuid()->toString(); + } + + /** + * Execute the job. + * + * @return void + */ + public function handle(PolygonValidationService $validationService) + { + try { + DelayedJob::create([ + 'uuid' => $this->job_uuid, + 'status' => self::STATUS_PENDING, + 'created_at' => now(), + ]); + + $sitePolygonsUuids = GeometryHelper::getSitePolygonsUuids($this->uuid); + $validationResults = []; + + foreach ($sitePolygonsUuids as $polygonUuid) { + $request = new Request(['uuid' => $polygonUuid]); + + $validationResults[$polygonUuid] = [ + 'overlapping' => $validationService->validateOverlapping($request), + 'selfIntersection' => $validationService->checkSelfIntersection($request), + 'coordinateSystem' => $validationService->validateCoordinateSystem($request), + 'polygonSize' => $validationService->validatePolygonSize($request), + 'withinCountry' => $validationService->checkWithinCountry($request), + 'boundarySegments' => $validationService->checkBoundarySegments($request), + 'geometryType' => $validationService->getGeometryType($request), + 'estimatedArea' => $validationService->validateEstimatedArea($request), + 'dataInDB' => $validationService->validateDataInDB($request), + ]; + } + + DelayedJob::where('uuid', $this->job_uuid)->update([ + 'status' => self::STATUS_SUCCEEDED, + 'payload' => 'Validation completed for all site polygons', + 'updated_at' => now(), + ]); + + } catch (Exception $e) { + Log::error('Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); + + DelayedJob::where('uuid', $this->job_uuid)->update([ + 'status' => self::STATUS_FAILED, + 'payload' => json_encode(['error' => $e->getMessage()]), + 'updated_at' => now(), + ]); + } + } + + public function getJobUuid() + { + return $this->job_uuid; + } +} diff --git a/app/Jobs/runSitePolygonsValidationJob.php b/app/Jobs/runSitePolygonsValidationJob.php deleted file mode 100644 index 8c0800d67..000000000 --- a/app/Jobs/runSitePolygonsValidationJob.php +++ /dev/null @@ -1,31 +0,0 @@ -input('uuid'); + return $this->handlePolygonValidation( + $uuid, + NotOverlapping::getIntersectionData($uuid), + PolygonService::OVERLAPPING_CRITERIA_ID + ); + } + + public function checkSelfIntersection(Request $request) + { + $uuid = $request->query('uuid'); + $geometry = PolygonGeometry::where('uuid', $uuid)->first(); + + if (!$geometry) { + return ['error' => 'Geometry not found', 'status' => 404]; + } + + $isSimple = SelfIntersection::uuidValid($uuid); + $message = $isSimple ? 'The geometry is valid' : 'The geometry has self-intersections'; + $insertionSuccess = App::make(PolygonService::class) + ->createCriteriaSite($uuid, PolygonService::SELF_CRITERIA_ID, $isSimple); + + return [ + 'selfintersects' => $message, + 'geometry_id' => $geometry->id, + 'insertion_success' => $insertionSuccess, + 'valid' => $isSimple, + 'status' => 200 + ]; + } + + public function validateCoordinateSystem(Request $request) + { + $uuid = $request->input('uuid'); + + return $this->handlePolygonValidation( + $uuid, + ['valid' => FeatureBounds::uuidValid($uuid)], + PolygonService::COORDINATE_SYSTEM_CRITERIA_ID + ); + } + + public function validatePolygonSize(Request $request) + { + $uuid = $request->query('uuid'); + $geometry = PolygonGeometry::isUuid($uuid)->first(); + + if (!$geometry) { + return ['error' => 'Geometry not found', 'status' => 404]; + } + + $areaSqMeters = PolygonSize::calculateSqMeters($geometry->db_geometry); + $valid = $areaSqMeters <= PolygonSize::SIZE_LIMIT; + $insertionSuccess = App::make(PolygonService::class) + ->createCriteriaSite($uuid, PolygonService::SIZE_CRITERIA_ID, $valid); + + return [ + 'area_hectares' => $areaSqMeters / 10000, // Convert to hectares + 'area_sqmeters' => $areaSqMeters, + 'geometry_id' => $geometry->id, + 'insertion_success' => $insertionSuccess, + 'valid' => $valid, + 'status' => 200 + ]; + } + + public function checkWithinCountry(Request $request) + { + $polygonUuid = $request->input('uuid'); + + return $this->handlePolygonValidation( + $polygonUuid, + WithinCountry::getIntersectionData($polygonUuid), + PolygonService::WITHIN_COUNTRY_CRITERIA_ID + ); + } + + public function checkBoundarySegments(Request $request) + { + $uuid = $request->query('uuid'); + $geometry = PolygonGeometry::isUuid($uuid)->first(); + + if (!$geometry) { + return ['error' => 'Geometry not found', 'status' => 404]; + } + $spikes = Spikes::detectSpikes($geometry->geo_json); + $valid = count($spikes) === 0; + $insertionSuccess = App::make(PolygonService::class) + ->createCriteriaSite($uuid, PolygonService::SPIKE_CRITERIA_ID, $valid); + + return [ + 'spikes' => $spikes, + 'geometry_id' => $uuid, + 'insertion_success' => $insertionSuccess, + 'valid' => $valid, + 'status' => 200 + ]; + } + + public function getGeometryType(Request $request) + { + $uuid = $request->input('uuid'); + + $geometryType = PolygonGeometry::getGeometryType($uuid); + if ($geometryType) { + $valid = $geometryType === GeometryType::VALID_TYPE; + $insertionSuccess = App::make(PolygonService::class) + ->createCriteriaSite($uuid, PolygonService::GEOMETRY_TYPE_CRITERIA_ID, $valid); + + return [ + 'uuid' => $uuid, + 'geometry_type' => $geometryType, + 'valid' => $valid, + 'insertion_success' => $insertionSuccess, + 'status' => 200 + ]; + } else { + return ['error' => 'Geometry not found for the given UUID', 'status' => 404]; + } + } + + public function validateEstimatedArea(Request $request) + { + $uuid = $request->input('uuid'); + + return $this->handlePolygonValidation( + $uuid, + EstimatedArea::getAreaData($uuid), + PolygonService::ESTIMATED_AREA_CRITERIA_ID + ); + } + + public function validateDataInDB(Request $request) + { + $polygonUuid = $request->input('uuid'); + $fieldsToValidate = ['poly_name', 'plantstart', 'plantend', 'practice', 'target_sys', 'distr', 'num_trees']; + + $sitePolygon = SitePolygon::forPolygonGeometry($polygonUuid)->first(); + if (!$sitePolygon) { + return ['valid' => false, 'message' => 'No site polygon found with the specified UUID.', 'status' => 404]; + } + + $validationErrors = []; + $polygonService = App::make(PolygonService::class); + foreach ($fieldsToValidate as $field) { + $value = $sitePolygon->$field; + if ($polygonService->isInvalidField($field, $value)) { + $validationErrors[] = [ + 'field' => $field, + 'error' => $value, + 'exists' => !is_null($value) && $value !== '', + ]; + } + } + + $isValid = empty($validationErrors); + $responseData = ['valid' => $isValid]; + if (!$isValid) { + $responseData['message'] = 'Some attributes of the site polygon are invalid.'; + } + + $polygonService->createCriteriaSite($polygonUuid, PolygonService::DATA_CRITERIA_ID, $isValid, $validationErrors); + + return array_merge($responseData, ['status' => 200]); + } + + protected function handlePolygonValidation($polygonUuid, $response, $criteriaId) + { + if (isset($response['error']) && $response['error'] != null) { + $status = $response['status']; + unset($response['valid']); + unset($response['status']); + + return $response + ['status' => $status]; + } + $extraInfo = $response['extra_info'] ?? null; + $response['insertion_success'] = App::make(PolygonService::class) + ->createCriteriaSite($polygonUuid, $criteriaId, $response['valid'], $extraInfo); + + return $response; + } +} \ No newline at end of file From f0397e605319c88fe7f0fb3fe2544e43ee15c8f5 Mon Sep 17 00:00:00 2001 From: JORGE Date: Mon, 7 Oct 2024 11:26:14 -0400 Subject: [PATCH 08/64] [TM-1127] create job with uuids for validations --- .../TerrafundCreateGeometryController.php | 18 ++++------ app/Jobs/RunSitePolygonsValidationJob.php | 34 ++++++++----------- 2 files changed, 20 insertions(+), 32 deletions(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index bed7eb06d..9db25d783 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -1185,13 +1185,8 @@ public function runSiteValidationPolygon(Request $request) try { $uuid = $request->input('uuid'); - // $sitePolygonsUuids = GeometryHelper::getSitePolygonsUuids($uuid); - - // foreach ($sitePolygonsUuids as $polygonUuid) { - // $this->runValidationPolygon($polygonUuid); - // } - - $job = new RunSitePolygonsValidationJob($uuid); + $sitePolygonsUuids = GeometryHelper::getSitePolygonsUuids($uuid)->toArray(); + $job = new RunSitePolygonsValidationJob($sitePolygonsUuids); $jobUUID = $job->getJobUuid(); dispatch($job); return response()->json(['message' => 'Validation completed for all site polygons', 'uuid' => $jobUUID], 200); @@ -1206,11 +1201,10 @@ public function runPolygonsValidation(Request $request) { try { $uuids = $request->input('uuids'); - foreach ($uuids as $polygonUuid) { - $this->runValidationPolygon($polygonUuid); - } - - return response()->json(['message' => 'Validation completed for these polygons'], 200); + $job = new RunSitePolygonsValidationJob($uuids); + $jobUUID = $job->getJobUuid(); + dispatch($job); + return response()->json(['message' => 'Validation completed for these polygons', 'uuid' => $jobUUID], 200); } catch (\Exception $e) { return response()->json(['error' => 'An error occurred during validation'], 500); } diff --git a/app/Jobs/RunSitePolygonsValidationJob.php b/app/Jobs/RunSitePolygonsValidationJob.php index eb8db4a44..03b4abe0d 100644 --- a/app/Jobs/RunSitePolygonsValidationJob.php +++ b/app/Jobs/RunSitePolygonsValidationJob.php @@ -25,16 +25,17 @@ class RunSitePolygonsValidationJob implements ShouldQueue protected $uuid; protected $job_uuid; - + protected $sitePolygonsUuids; + /** * Create a new job instance. * * @return void */ - public function __construct(string $uuid) + public function __construct(array $sitePolygonsUuids) { - $this->uuid = $uuid; + $this->sitePolygonsUuids = $sitePolygonsUuids; $this->job_uuid = Str::uuid()->toString(); } @@ -51,24 +52,17 @@ public function handle(PolygonValidationService $validationService) 'status' => self::STATUS_PENDING, 'created_at' => now(), ]); - - $sitePolygonsUuids = GeometryHelper::getSitePolygonsUuids($this->uuid); - $validationResults = []; - - foreach ($sitePolygonsUuids as $polygonUuid) { + foreach ($this->sitePolygonsUuids as $polygonUuid) { $request = new Request(['uuid' => $polygonUuid]); - - $validationResults[$polygonUuid] = [ - 'overlapping' => $validationService->validateOverlapping($request), - 'selfIntersection' => $validationService->checkSelfIntersection($request), - 'coordinateSystem' => $validationService->validateCoordinateSystem($request), - 'polygonSize' => $validationService->validatePolygonSize($request), - 'withinCountry' => $validationService->checkWithinCountry($request), - 'boundarySegments' => $validationService->checkBoundarySegments($request), - 'geometryType' => $validationService->getGeometryType($request), - 'estimatedArea' => $validationService->validateEstimatedArea($request), - 'dataInDB' => $validationService->validateDataInDB($request), - ]; + $validationService->validateOverlapping($request); + $validationService->checkSelfIntersection($request); + $validationService->validateCoordinateSystem($request); + $validationService->validatePolygonSize($request); + $validationService->checkWithinCountry($request); + $validationService->checkBoundarySegments($request); + $validationService->getGeometryType($request); + $validationService->validateEstimatedArea($request); + $validationService->validateDataInDB($request); } DelayedJob::where('uuid', $this->job_uuid)->update([ From 4d85d45cb5dfe4b3a3f7b172d0df6c1bd859529a Mon Sep 17 00:00:00 2001 From: Jorge Monroy Date: Tue, 8 Oct 2024 12:04:50 -0400 Subject: [PATCH 09/64] [TM-1327] add fix to project level (#499) Co-authored-by: cesarLima1 --- app/Helpers/GeometryHelper.php | 6 ++++++ .../V2/Terrafund/TerrafundClipGeometryController.php | 9 ++++++++- app/Services/PolygonService.php | 4 +--- app/Validators/Extensions/Polygons/GeometryType.php | 10 +++++----- routes/api_v2.php | 2 +- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/app/Helpers/GeometryHelper.php b/app/Helpers/GeometryHelper.php index 3fa7cdbf0..7f6a8c6ac 100755 --- a/app/Helpers/GeometryHelper.php +++ b/app/Helpers/GeometryHelper.php @@ -357,6 +357,12 @@ public static function getPolygonsGeojson(array $polygonUuids): array ]; } + public static function getProjectPolygonsUuids($projectId) + { + $project = Project::where('id', $projectId)->firstOrFail(); + $projectPolygonUuids = $project->sitePolygons()->pluck('poly_id')->toArray(); + return $projectPolygonUuids; + } public static function getSitePolygonsUuids($uuid) { return SitePolygon::where('site_id', $uuid)->where('is_active', true)->get()->pluck('poly_id'); diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php index a37cc6075..4cb665eee 100644 --- a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php @@ -6,6 +6,7 @@ use App\Helpers\GeometryHelper; use App\Helpers\PolygonGeometryHelper; use App\Models\V2\Sites\CriteriaSite; +use App\Models\V2\Sites\Site; use App\Models\V2\Sites\SitePolygon; use App\Services\PolygonService; use App\Services\PythonService; @@ -18,7 +19,13 @@ class TerrafundClipGeometryController extends TerrafundCreateGeometryController public function clipOverlappingPolygonsBySite(string $uuid) { $polygonUuids = GeometryHelper::getSitePolygonsUuids($uuid)->toArray(); - + return $this->processClippedPolygons($polygonUuids); + } + public function clipOverlappingPolygonsOfProjectBySite(string $uuid) + { + $sitePolygon = Site::isUuid($uuid)->first(); + $projectId = $sitePolygon->project_id ?? null; + $polygonUuids = GeometryHelper::getProjectPolygonsUuids($projectId); return $this->processClippedPolygons($polygonUuids); } diff --git a/app/Services/PolygonService.php b/app/Services/PolygonService.php index effc1a34c..b00910ee0 100755 --- a/app/Services/PolygonService.php +++ b/app/Services/PolygonService.php @@ -329,9 +329,7 @@ protected function insertSitePolygon(string $polygonUuid, array $properties) return null; } catch (\Exception $e) { - if (! $sitePolygon) { - throw new \Exception('SitePolygon not found for site_id: ' . $properties['site_id']); - } + throw new \Exception('SitePolygon not found for site_id: ' . $properties['site_id']); } } diff --git a/app/Validators/Extensions/Polygons/GeometryType.php b/app/Validators/Extensions/Polygons/GeometryType.php index f3361124b..52a2ebe7c 100644 --- a/app/Validators/Extensions/Polygons/GeometryType.php +++ b/app/Validators/Extensions/Polygons/GeometryType.php @@ -5,6 +5,7 @@ use App\Models\V2\PolygonGeometry; use App\Validators\Extensions\Extension; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; class GeometryType extends Extension { @@ -15,7 +16,8 @@ class GeometryType extends Extension 'message' => 'The geometry must by of polygon type', ]; - public const VALID_TYPE = 'POLYGON'; + public const VALID_TYPE_POLYGON = 'POLYGON'; + public const VALID_TYPE_MULTIPOLYGON = 'MULTIPOLYGON'; public static function passes($attribute, $value, $parameters, $validator): bool { @@ -31,8 +33,7 @@ public static function passes($attribute, $value, $parameters, $validator): bool public static function uuidValid($uuid): bool { $geometryType = PolygonGeometry::getGeometryType($uuid); - - return $geometryType === self::VALID_TYPE; + return $geometryType === self::VALID_TYPE_POLYGON || $geometryType === self::VALID_TYPE_MULTIPOLYGON; } public static function geoJsonValid($geojson): bool @@ -41,7 +42,6 @@ public static function geoJsonValid($geojson): bool 'SELECT ST_GeometryType(ST_GeomFromGeoJSON(:geojson)) AS geometry_type', ['geojson' => json_encode($geojson)] ); - - return $result->geometry_type === self::VALID_TYPE; + return $result->geometry_type === self::VALID_TYPE_POLYGON || $result->geometry_type === self::VALID_TYPE_MULTIPOLYGON; } } diff --git a/routes/api_v2.php b/routes/api_v2.php index 558270ddd..e9641f178 100644 --- a/routes/api_v2.php +++ b/routes/api_v2.php @@ -662,7 +662,7 @@ Route::post('/validation/polygons', [TerrafundCreateGeometryController::class, 'runPolygonsValidation']); Route::post('/validation/sitePolygons', [TerrafundCreateGeometryController::class, 'runSiteValidationPolygon']); Route::get('/validation/site', [TerrafundCreateGeometryController::class, 'getCurrentSiteValidation']); - Route::post('/clip-polygons/site/{uuid}', [TerrafundClipGeometryController::class, 'clipOverlappingPolygonsBySite']); + Route::post('/clip-polygons/site/{uuid}', [TerrafundClipGeometryController::class, 'clipOverlappingPolygonsOfProjectBySite']); Route::post('/clip-polygons/polygon/{uuid}', [TerrafundClipGeometryController::class, 'clipOverlappingPolygon']); Route::post('/clip-polygons/polygons', [TerrafundClipGeometryController::class, 'clipOverlappingPolygons']); From fe4b273bbadf58b46daa8e57f1ca60e8118cf259 Mon Sep 17 00:00:00 2001 From: cesarLima1 Date: Tue, 8 Oct 2024 12:34:37 -0400 Subject: [PATCH 10/64] [TM-1127] add geometry types validation --- .../V2/Terrafund/TerrafundCreateGeometryController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index 9db25d783..32a13b18a 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -481,7 +481,7 @@ public function getGeometryType(Request $request) $geometryType = PolygonGeometry::getGeometryType($uuid); if ($geometryType) { - $valid = $geometryType === GeometryType::VALID_TYPE; + $valid = $geometryType === GeometryType::VALID_TYPE_POLYGON || $geometryType === GeometryType::VALID_TYPE_MULTIPOLYGON; $insertionSuccess = App::make(PolygonService::class) ->createCriteriaSite($uuid, PolygonService::GEOMETRY_TYPE_CRITERIA_ID, $valid); From eb6749f8e97168afd1c63105d4b35c62bb064506 Mon Sep 17 00:00:00 2001 From: JORGE Date: Tue, 8 Oct 2024 12:46:50 -0400 Subject: [PATCH 11/64] [TM-1127] astart adding fix worker and add validation types correct --- .../TerrafundClipGeometryController.php | 16 +++++----- app/Jobs/FixPolygonOverlapJob.php | 31 +++++++++++++++++++ app/Jobs/RunSitePolygonsValidationJob.php | 9 ++++++ app/Services/PolygonValidationService.php | 2 +- 4 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 app/Jobs/FixPolygonOverlapJob.php diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php index 4cb665eee..a2dc86ca9 100644 --- a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php @@ -19,14 +19,17 @@ class TerrafundClipGeometryController extends TerrafundCreateGeometryController public function clipOverlappingPolygonsBySite(string $uuid) { $polygonUuids = GeometryHelper::getSitePolygonsUuids($uuid)->toArray(); - return $this->processClippedPolygons($polygonUuids); + $polygonsClipped = $this->processClippedPolygons($polygonUuids); + return response()->json(['updated_polygons' => $polygonsClipped], 200); + } public function clipOverlappingPolygonsOfProjectBySite(string $uuid) { $sitePolygon = Site::isUuid($uuid)->first(); $projectId = $sitePolygon->project_id ?? null; $polygonUuids = GeometryHelper::getProjectPolygonsUuids($projectId); - return $this->processClippedPolygons($polygonUuids); + $polygonsClipped = $this->processClippedPolygons($polygonUuids); + return response()->json(['updated_polygons' => $polygonsClipped], 200); } public function clipOverlappingPolygons(Request $request) @@ -68,9 +71,7 @@ public function clipOverlappingPolygons(Request $request) $uniquePolygonUuids = array_unique($allPolygonUuids); $processedPolygons = []; if (! empty($uniquePolygonUuids)) { - $response = $this->processClippedPolygons($uniquePolygonUuids); - $responseData = $response->getData(true); - $processedPolygons = $responseData['updated_polygons'] ?? []; + $processedPolygons = $this->processClippedPolygons($uniquePolygonUuids); } else { $processedPolygons = null; } @@ -101,7 +102,8 @@ public function clipOverlappingPolygon(string $uuid) array_unshift($polygonUuids, $uuid); - return $this->processClippedPolygons($polygonUuids); + $polygonsClipped = $this->processClippedPolygons($polygonUuids); + return response()->json(['updated_polygons' => $polygonsClipped], 200); } private function processClippedPolygons(array $polygonUuids) @@ -140,6 +142,6 @@ private function processClippedPolygons(array $polygonUuids) $updatedPolygons = PolygonGeometryHelper::getPolygonsProjection($uuids, ['poly_id', 'poly_name']); - return response()->json(['updated_polygons' => $updatedPolygons]); + return $updatedPolygons; } } diff --git a/app/Jobs/FixPolygonOverlapJob.php b/app/Jobs/FixPolygonOverlapJob.php new file mode 100644 index 000000000..0e0825eb4 --- /dev/null +++ b/app/Jobs/FixPolygonOverlapJob.php @@ -0,0 +1,31 @@ +getMessage()); + DelayedJob::where('uuid', $this->job_uuid)->update([ + 'status' => self::STATUS_FAILED, + 'payload' => json_encode(['error' => $e->getMessage()]), + 'updated_at' => now(), + ]); + } catch (Throwable $e) { + Log::error('Throwable Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); + DelayedJob::where('uuid', $this->job_uuid)->update([ 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), diff --git a/app/Services/PolygonValidationService.php b/app/Services/PolygonValidationService.php index 18c09a6b7..f764c87d5 100644 --- a/app/Services/PolygonValidationService.php +++ b/app/Services/PolygonValidationService.php @@ -125,7 +125,7 @@ public function getGeometryType(Request $request) $geometryType = PolygonGeometry::getGeometryType($uuid); if ($geometryType) { - $valid = $geometryType === GeometryType::VALID_TYPE; + $valid = $geometryType === GeometryType::VALID_TYPE_MULTIPOLYGON || $geometryType === GeometryType::VALID_TYPE_POLYGON; $insertionSuccess = App::make(PolygonService::class) ->createCriteriaSite($uuid, PolygonService::GEOMETRY_TYPE_CRITERIA_ID, $valid); From 5bc12b6bff6a42bd3335f99ab1c9483065ca2750 Mon Sep 17 00:00:00 2001 From: JORGE Date: Tue, 8 Oct 2024 14:18:41 -0400 Subject: [PATCH 12/64] [TM-1127] move clipoverlapping --- .../TerrafundClipGeometryController.php | 45 ++----------------- .../TerrafundCreateGeometryController.php | 6 +++ app/Services/PolygonService.php | 40 +++++++++++++++++ app/Services/PolygonValidationService.php | 14 ++++++ 4 files changed, 64 insertions(+), 41 deletions(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php index a2dc86ca9..4c629447e 100644 --- a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php @@ -19,7 +19,7 @@ class TerrafundClipGeometryController extends TerrafundCreateGeometryController public function clipOverlappingPolygonsBySite(string $uuid) { $polygonUuids = GeometryHelper::getSitePolygonsUuids($uuid)->toArray(); - $polygonsClipped = $this->processClippedPolygons($polygonUuids); + $polygonsClipped = App::make(PolygonService::class)->processClippedPolygons($polygonUuids); return response()->json(['updated_polygons' => $polygonsClipped], 200); } @@ -28,7 +28,7 @@ public function clipOverlappingPolygonsOfProjectBySite(string $uuid) $sitePolygon = Site::isUuid($uuid)->first(); $projectId = $sitePolygon->project_id ?? null; $polygonUuids = GeometryHelper::getProjectPolygonsUuids($projectId); - $polygonsClipped = $this->processClippedPolygons($polygonUuids); + $polygonsClipped = App::make(PolygonService::class)->processClippedPolygons($polygonUuids); return response()->json(['updated_polygons' => $polygonsClipped], 200); } @@ -71,7 +71,7 @@ public function clipOverlappingPolygons(Request $request) $uniquePolygonUuids = array_unique($allPolygonUuids); $processedPolygons = []; if (! empty($uniquePolygonUuids)) { - $processedPolygons = $this->processClippedPolygons($uniquePolygonUuids); + $processedPolygons = App::make(PolygonService::class)->processClippedPolygons($polygonUuids); } else { $processedPolygons = null; } @@ -102,46 +102,9 @@ public function clipOverlappingPolygon(string $uuid) array_unshift($polygonUuids, $uuid); - $polygonsClipped = $this->processClippedPolygons($polygonUuids); + $polygonsClipped = App::make(PolygonService::class)->processClippedPolygons($polygonUuids); return response()->json(['updated_polygons' => $polygonsClipped], 200); } - private function processClippedPolygons(array $polygonUuids) - { - $geojson = GeometryHelper::getPolygonsGeojson($polygonUuids); - - $clippedPolygons = App::make(PythonService::class)->clipPolygons($geojson); - $uuids = []; - - if (isset($clippedPolygons['type']) && $clippedPolygons['type'] === 'FeatureCollection' && isset($clippedPolygons['features'])) { - foreach ($clippedPolygons['features'] as $feature) { - if (isset($feature['properties']['poly_id'])) { - $poly_id = $feature['properties']['poly_id']; - $result = CreateVersionPolygonGeometryHelper::createVersionPolygonGeometry($poly_id, json_encode(['geometry' => $feature])); - - if (isset($result->original['uuid'])) { - $uuids[] = $result->original['uuid']; - } - - if (($key = array_search($poly_id, $polygonUuids)) !== false) { - unset($polygonUuids[$key]); - } - } - } - $polygonUuids = array_values($polygonUuids); - $newPolygonUuids = array_merge($uuids, $polygonUuids); - } else { - Log::error('Error clipping polygons', ['clippedPolygons' => $clippedPolygons]); - } - if (! empty($uuids)) { - foreach ($newPolygonUuids as $polygonUuid) { - $this->runValidationPolygon($polygonUuid); - } - } - - $updatedPolygons = PolygonGeometryHelper::getPolygonsProjection($uuids, ['poly_id', 'poly_name']); - - return $updatedPolygons; - } } diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index 32a13b18a..bc90838f2 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -1157,6 +1157,7 @@ private function handlePolygonValidation($polygonUuid, $response, $criteriaId): public function runValidationPolygon(string $uuid) { + try{ $request = new Request(['uuid' => $uuid]); $this->validateOverlapping($request); @@ -1168,6 +1169,11 @@ public function runValidationPolygon(string $uuid) $this->getGeometryType($request); $this->validateEstimatedArea($request); $this->validateDataInDB($request); + } catch(\Exception $e) { + Log::error('Error during validation polygon: ' . $e->getMessage()); + throw $e; + } + } public function sendRunValidationPolygon(Request $request) diff --git a/app/Services/PolygonService.php b/app/Services/PolygonService.php index b00910ee0..96ec99ca0 100755 --- a/app/Services/PolygonService.php +++ b/app/Services/PolygonService.php @@ -2,7 +2,9 @@ namespace App\Services; +use App\Helpers\CreateVersionPolygonGeometryHelper; use App\Helpers\GeometryHelper; +use App\Helpers\PolygonGeometryHelper; use App\Models\V2\PointGeometry; use App\Models\V2\PolygonGeometry; use App\Models\V2\ProjectPitch; @@ -515,4 +517,42 @@ public function insertGeojsonToDB(string $geojsonFilename, ?string $entity_uuid } } } + public function processClippedPolygons(array $polygonUuids) + { + $geojson = GeometryHelper::getPolygonsGeojson($polygonUuids); + + $clippedPolygons = App::make(PythonService::class)->clipPolygons($geojson); + $uuids = []; + + if (isset($clippedPolygons['type']) && $clippedPolygons['type'] === 'FeatureCollection' && isset($clippedPolygons['features'])) { + foreach ($clippedPolygons['features'] as $feature) { + if (isset($feature['properties']['poly_id'])) { + $poly_id = $feature['properties']['poly_id']; + $result = CreateVersionPolygonGeometryHelper::createVersionPolygonGeometry($poly_id, json_encode(['geometry' => $feature])); + + if (isset($result->original['uuid'])) { + $uuids[] = $result->original['uuid']; + } + + if (($key = array_search($poly_id, $polygonUuids)) !== false) { + unset($polygonUuids[$key]); + } + } + } + $polygonUuids = array_values($polygonUuids); + $newPolygonUuids = array_merge($uuids, $polygonUuids); + } else { + Log::error('Error clipping polygons', ['clippedPolygons' => $clippedPolygons]); + } + + if (! empty($uuids)) { + foreach ($newPolygonUuids as $polygonUuid) { + App::make(PolygonValidationService::class)->runValidationPolygon($polygonUuid); + } + } + + $updatedPolygons = PolygonGeometryHelper::getPolygonsProjection($uuids, ['poly_id', 'poly_name']); + + return $updatedPolygons; + } } diff --git a/app/Services/PolygonValidationService.php b/app/Services/PolygonValidationService.php index f764c87d5..9ee9357bc 100644 --- a/app/Services/PolygonValidationService.php +++ b/app/Services/PolygonValidationService.php @@ -201,4 +201,18 @@ protected function handlePolygonValidation($polygonUuid, $response, $criteriaId) return $response; } + public function runValidationPolygon(string $uuid) + { + $request = new Request(['uuid' => $uuid]); + + $this->validateOverlapping($request); + $this->checkSelfIntersection($request); + $this->validateCoordinateSystem($request); + $this->validatePolygonSize($request); + $this->checkWithinCountry($request); + $this->checkBoundarySegments($request); + $this->getGeometryType($request); + $this->validateEstimatedArea($request); + $this->validateDataInDB($request); + } } \ No newline at end of file From dd2e692fafbb43b4b14aa143362020e89a69002c Mon Sep 17 00:00:00 2001 From: JORGE Date: Tue, 8 Oct 2024 14:26:08 -0400 Subject: [PATCH 13/64] [TM-1127] add fix Job --- app/Jobs/FixPolygonOverlapJob.php | 57 ++++++++++++++++++++--- app/Jobs/RunSitePolygonsValidationJob.php | 1 + 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/app/Jobs/FixPolygonOverlapJob.php b/app/Jobs/FixPolygonOverlapJob.php index 0e0825eb4..1412a2770 100644 --- a/app/Jobs/FixPolygonOverlapJob.php +++ b/app/Jobs/FixPolygonOverlapJob.php @@ -1,24 +1,39 @@ polygonUuids = $polygonUuids; + $this->job_uuid = Str::uuid()->toString(); } /** @@ -26,6 +41,36 @@ public function __construct() */ public function handle(): void { - // + try { + DelayedJob::create([ + 'uuid' => $this->job_uuid, + 'status' => self::STATUS_PENDING, + 'created_at' => now(), + ]); + $polygonService = App::make(PolygonService::class); + $updatedPolygons = $polygonService->processClippedPolygons($this->polygonUuids); + DelayedJob::where('uuid', $this->job_uuid)->update([ + 'status' => self::STATUS_SUCCEEDED, + 'payload' => json_encode(['updated_polygons' => $updatedPolygons]), + 'updated_at' => now(), + ]); + + } catch (Exception $e) { + Log::error('Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); + + DelayedJob::where('uuid', $this->job_uuid)->update([ + 'status' => self::STATUS_FAILED, + 'payload' => json_encode(['error' => $e->getMessage()]), + 'updated_at' => now(), + ]); + } catch (Throwable $e) { + Log::error('Throwable Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); + + DelayedJob::where('uuid', $this->job_uuid)->update([ + 'status' => self::STATUS_FAILED, + 'payload' => json_encode(['error' => $e->getMessage()]), + 'updated_at' => now(), + ]); + } } } diff --git a/app/Jobs/RunSitePolygonsValidationJob.php b/app/Jobs/RunSitePolygonsValidationJob.php index 1bbcb002f..587b1d565 100644 --- a/app/Jobs/RunSitePolygonsValidationJob.php +++ b/app/Jobs/RunSitePolygonsValidationJob.php @@ -16,6 +16,7 @@ use Illuminate\Http\Request; use Throwable; + class RunSitePolygonsValidationJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; From 454f40494d8d6cc2f9d05808654a3d86ea5cb208 Mon Sep 17 00:00:00 2001 From: JORGE Date: Tue, 8 Oct 2024 14:34:14 -0400 Subject: [PATCH 14/64] [TM-1127] job_uuid --- .../V2/Terrafund/TerrafundCreateGeometryController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index bc90838f2..416b56ae7 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -398,7 +398,7 @@ public function uploadShapefile(Request $request) } $jobUUID = $job->getJobUuid(); dispatch($job); - return response()->json(['message' => 'Shape file processed and inserted successfully', 'uuid' => $jobUUID], 200); + return response()->json(['message' => 'Shape file processed and inserted successfully', 'job_uuid' => $jobUUID], 200); } else { return response()->json(['error' => 'Failed to open the ZIP file'], 400); } @@ -1195,7 +1195,7 @@ public function runSiteValidationPolygon(Request $request) $job = new RunSitePolygonsValidationJob($sitePolygonsUuids); $jobUUID = $job->getJobUuid(); dispatch($job); - return response()->json(['message' => 'Validation completed for all site polygons', 'uuid' => $jobUUID], 200); + return response()->json(['message' => 'Validation completed for all site polygons', 'job_uuid' => $jobUUID], 200); } catch (\Exception $e) { Log::error('Error during site validation polygon: ' . $e->getMessage()); @@ -1210,7 +1210,7 @@ public function runPolygonsValidation(Request $request) $job = new RunSitePolygonsValidationJob($uuids); $jobUUID = $job->getJobUuid(); dispatch($job); - return response()->json(['message' => 'Validation completed for these polygons', 'uuid' => $jobUUID], 200); + return response()->json(['message' => 'Validation completed for these polygons', 'job_uuid' => $jobUUID], 200); } catch (\Exception $e) { return response()->json(['error' => 'An error occurred during validation'], 500); } From b12f7945e9141c0b70dad2101eab02f38c357ea2 Mon Sep 17 00:00:00 2001 From: cesarLima1 Date: Tue, 8 Oct 2024 14:41:12 -0400 Subject: [PATCH 15/64] [TM-1127] use job for fixing overlaps in polygons --- .../TerrafundClipGeometryController.php | 28 +++++++++---------- app/Jobs/FixPolygonOverlapJob.php | 5 ++++ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php index 4c629447e..19892f32d 100644 --- a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php @@ -10,6 +10,7 @@ use App\Models\V2\Sites\SitePolygon; use App\Services\PolygonService; use App\Services\PythonService; +use FixPolygonOverlapJob; use Illuminate\Http\Request; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Log; @@ -19,8 +20,10 @@ class TerrafundClipGeometryController extends TerrafundCreateGeometryController public function clipOverlappingPolygonsBySite(string $uuid) { $polygonUuids = GeometryHelper::getSitePolygonsUuids($uuid)->toArray(); - $polygonsClipped = App::make(PolygonService::class)->processClippedPolygons($polygonUuids); - return response()->json(['updated_polygons' => $polygonsClipped], 200); + $job = new FixPolygonOverlapJob($polygonUuids); + $jobUUID = $job->getJobUuid(); + dispatch($job); + return response()->json(['job_uuid' => $jobUUID], 200); } public function clipOverlappingPolygonsOfProjectBySite(string $uuid) @@ -28,19 +31,21 @@ public function clipOverlappingPolygonsOfProjectBySite(string $uuid) $sitePolygon = Site::isUuid($uuid)->first(); $projectId = $sitePolygon->project_id ?? null; $polygonUuids = GeometryHelper::getProjectPolygonsUuids($projectId); - $polygonsClipped = App::make(PolygonService::class)->processClippedPolygons($polygonUuids); - return response()->json(['updated_polygons' => $polygonsClipped], 200); + $job = new FixPolygonOverlapJob($polygonUuids); + $jobUUID = $job->getJobUuid(); + dispatch($job); + return response()->json(['job_uuid' => $jobUUID], 200); } public function clipOverlappingPolygons(Request $request) { $uuids = $request->input('uuids'); Log::info('Clipping polygons', ['uuids' => $uuids]); + $jobUUID = null; if (empty($uuids) || ! is_array($uuids)) { return response()->json(['error' => 'Invalid or missing UUIDs'], 400); } $allPolygonUuids = []; - $unprocessedPolygons = []; foreach ($uuids as $uuid) { $polygonOverlappingExtraInfo = CriteriaSite::forCriteria(PolygonService::OVERLAPPING_CRITERIA_ID) ->where('polygon_id', $uuid) @@ -69,17 +74,12 @@ public function clipOverlappingPolygons(Request $request) $allPolygonUuids = array_merge($allPolygonUuids, $polygonUuids); } $uniquePolygonUuids = array_unique($allPolygonUuids); - $processedPolygons = []; if (! empty($uniquePolygonUuids)) { - $processedPolygons = App::make(PolygonService::class)->processClippedPolygons($polygonUuids); - } else { - $processedPolygons = null; + $job = new FixPolygonOverlapJob($polygonUuids); + $jobUUID = $job->getJobUuid(); + dispatch($job); } - - return response()->json([ - 'processed' => $processedPolygons, - 'unprocessed' => $unprocessedPolygons, - ], 200); + return response()->json(['job_uuid' => $jobUUID,], 200); } public function clipOverlappingPolygon(string $uuid) diff --git a/app/Jobs/FixPolygonOverlapJob.php b/app/Jobs/FixPolygonOverlapJob.php index 1412a2770..6c064945c 100644 --- a/app/Jobs/FixPolygonOverlapJob.php +++ b/app/Jobs/FixPolygonOverlapJob.php @@ -73,4 +73,9 @@ public function handle(): void ]); } } + + public function getJobUuid() + { + return $this->job_uuid; + } } From ecf5e636272f4b168590f1d8d1201783c86e5040 Mon Sep 17 00:00:00 2001 From: cesarLima1 Date: Tue, 8 Oct 2024 14:46:09 -0400 Subject: [PATCH 16/64] [TM-1127] add namespace to FixPolygonOverlapJob --- .../V2/Terrafund/TerrafundClipGeometryController.php | 2 +- app/Jobs/FixPolygonOverlapJob.php | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php index 19892f32d..c55072993 100644 --- a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php @@ -10,7 +10,7 @@ use App\Models\V2\Sites\SitePolygon; use App\Services\PolygonService; use App\Services\PythonService; -use FixPolygonOverlapJob; +use App\Jobs\FixPolygonOverlapJob; use Illuminate\Http\Request; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Log; diff --git a/app/Jobs/FixPolygonOverlapJob.php b/app/Jobs/FixPolygonOverlapJob.php index 6c064945c..bef09cd61 100644 --- a/app/Jobs/FixPolygonOverlapJob.php +++ b/app/Jobs/FixPolygonOverlapJob.php @@ -1,5 +1,7 @@ job_uuid; From 8f98e74712ee7ee654afde8fa125c3b5a475ef61 Mon Sep 17 00:00:00 2001 From: cesarLima1 Date: Tue, 8 Oct 2024 14:54:00 -0400 Subject: [PATCH 17/64] [TM-1127] update imports in FixPolygonOverlapJob --- app/Jobs/FixPolygonOverlapJob.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Jobs/FixPolygonOverlapJob.php b/app/Jobs/FixPolygonOverlapJob.php index bef09cd61..10157ab60 100644 --- a/app/Jobs/FixPolygonOverlapJob.php +++ b/app/Jobs/FixPolygonOverlapJob.php @@ -12,6 +12,8 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\App; use Illuminate\Support\Str; +use Throwable; +use Exception; class FixPolygonOverlapJob implements ShouldQueue { From 10891d1d81b495afc154b2e1fe4326d57f498ed1 Mon Sep 17 00:00:00 2001 From: JORGE Date: Tue, 8 Oct 2024 15:39:00 -0400 Subject: [PATCH 18/64] [TM-1127] add middleware to keep user in fix polygon job --- .../CreateVersionPolygonGeometryHelper.php | 8 +---- .../TerrafundClipGeometryController.php | 11 ++++--- .../Middleware/SetAuthenticatedUserForJob.php | 25 ++++++++++++++++ app/Jobs/FixPolygonOverlapJob.php | 29 ++++++++++++++----- app/Services/PolygonService.php | 2 +- 5 files changed, 56 insertions(+), 19 deletions(-) create mode 100644 app/Http/Middleware/SetAuthenticatedUserForJob.php diff --git a/app/Helpers/CreateVersionPolygonGeometryHelper.php b/app/Helpers/CreateVersionPolygonGeometryHelper.php index cc6497eef..95962ba39 100644 --- a/app/Helpers/CreateVersionPolygonGeometryHelper.php +++ b/app/Helpers/CreateVersionPolygonGeometryHelper.php @@ -23,26 +23,21 @@ public static function createVersionPolygonGeometry(string $uuid, $geometry) if ($geometry instanceof Request) { $geometry = $geometry->input('geometry'); } - $polygonGeometry = PolygonGeometry::isUuid($uuid)->first(); - if (! $polygonGeometry) { return response()->json(['message' => 'No polygon geometry found for the given UUID.'], 404); } - $geometry = json_decode($geometry); $geom = DB::raw("ST_GeomFromGeoJSON('" . json_encode($geometry) . "')"); $sitePolygon = SitePolygon::where('poly_id', $polygonGeometry->uuid)->first(); $user = Auth::user(); - $newGeometryVersion = PolygonGeometry::create([ 'geom' => $geom, 'created_by' => $user->id, ]); $newPolygonVersion = $sitePolygon->createCopy($user, $newGeometryVersion->uuid, false); - if ($newPolygonVersion) { PolyHelper::updateEstAreainSitePolygon($newGeometryVersion, $geometry); PolyHelper::updateProjectCentroidFromPolygon($newGeometryVersion); @@ -50,10 +45,9 @@ public static function createVersionPolygonGeometry(string $uuid, $geometry) return response()->json(['message' => 'Site polygon version created successfully.', 'geometry' => $geometry, 'uuid' => $newPolygonVersion->poly_id], 201); } - return response()->json(['message' => 'Failed to create site polygon version.'], 500); } catch (\Exception $e) { - return response()->json(['error' => 'An error occurred: ' . $e->getMessage()], 500); + throw $e; } } } diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php index c55072993..9f4d9ab4d 100644 --- a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php @@ -13,14 +13,16 @@ use App\Jobs\FixPolygonOverlapJob; use Illuminate\Http\Request; use Illuminate\Support\Facades\App; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; class TerrafundClipGeometryController extends TerrafundCreateGeometryController { public function clipOverlappingPolygonsBySite(string $uuid) { + $user = Auth::user(); $polygonUuids = GeometryHelper::getSitePolygonsUuids($uuid)->toArray(); - $job = new FixPolygonOverlapJob($polygonUuids); + $job = new FixPolygonOverlapJob($polygonUuids, $user->id); $jobUUID = $job->getJobUuid(); dispatch($job); return response()->json(['job_uuid' => $jobUUID], 200); @@ -28,10 +30,11 @@ public function clipOverlappingPolygonsBySite(string $uuid) } public function clipOverlappingPolygonsOfProjectBySite(string $uuid) { + $user = Auth::user(); $sitePolygon = Site::isUuid($uuid)->first(); $projectId = $sitePolygon->project_id ?? null; $polygonUuids = GeometryHelper::getProjectPolygonsUuids($projectId); - $job = new FixPolygonOverlapJob($polygonUuids); + $job = new FixPolygonOverlapJob($polygonUuids, $user->id); $jobUUID = $job->getJobUuid(); dispatch($job); return response()->json(['job_uuid' => $jobUUID], 200); @@ -75,7 +78,8 @@ public function clipOverlappingPolygons(Request $request) } $uniquePolygonUuids = array_unique($allPolygonUuids); if (! empty($uniquePolygonUuids)) { - $job = new FixPolygonOverlapJob($polygonUuids); + $user = Auth::user(); + $job = new FixPolygonOverlapJob($polygonUuids, $user->id); $jobUUID = $job->getJobUuid(); dispatch($job); } @@ -101,7 +105,6 @@ public function clipOverlappingPolygon(string $uuid) $polygonUuids = array_filter($polygonUuidsOverlapping); array_unshift($polygonUuids, $uuid); - $polygonsClipped = App::make(PolygonService::class)->processClippedPolygons($polygonUuids); return response()->json(['updated_polygons' => $polygonsClipped], 200); } diff --git a/app/Http/Middleware/SetAuthenticatedUserForJob.php b/app/Http/Middleware/SetAuthenticatedUserForJob.php new file mode 100644 index 000000000..77a509d69 --- /dev/null +++ b/app/Http/Middleware/SetAuthenticatedUserForJob.php @@ -0,0 +1,25 @@ +authUserId)) { + Auth::onceUsingId($job->authUserId); + } + + return $next($job); + } +} diff --git a/app/Jobs/FixPolygonOverlapJob.php b/app/Jobs/FixPolygonOverlapJob.php index 10157ab60..8db3b2faf 100644 --- a/app/Jobs/FixPolygonOverlapJob.php +++ b/app/Jobs/FixPolygonOverlapJob.php @@ -2,6 +2,7 @@ namespace App\Jobs; +use App\Http\Middleware\SetAuthenticatedUserForJob; use App\Models\DelayedJob; use App\Services\PolygonService; use Illuminate\Bus\Queueable; @@ -12,6 +13,7 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\App; use Illuminate\Support\Str; +use Illuminate\Support\Facades\Auth; use Throwable; use Exception; @@ -28,37 +30,50 @@ class FixPolygonOverlapJob implements ShouldQueue protected $polygonService; protected $polygonUuids; protected $job_uuid; + public $authUserId; /** * Create a new job instance. * * @param array $polygonUuids */ - public function __construct(array $polygonUuids) + public function __construct(array $polygonUuids, int $authUserId) { $this->polygonUuids = $polygonUuids; $this->job_uuid = Str::uuid()->toString(); - } + $this->authUserId = $authUserId; + } + /** + * Get the middleware the job should pass through. + * + * @return array + */ + public function middleware() + { + return [new SetAuthenticatedUserForJob]; + } /** * Execute the job. */ public function handle(): void { + try { DelayedJob::create([ 'uuid' => $this->job_uuid, 'status' => self::STATUS_PENDING, 'created_at' => now(), ]); - $polygonService = App::make(PolygonService::class); - $updatedPolygons = $polygonService->processClippedPolygons($this->polygonUuids); + $user = Auth::user(); + if ($user) { + $polygonsClipped = App::make(PolygonService::class)->processClippedPolygons($this->polygonUuids); DelayedJob::where('uuid', $this->job_uuid)->update([ 'status' => self::STATUS_SUCCEEDED, - 'payload' => json_encode(['updated_polygons' => $updatedPolygons]), + 'payload' => json_encode(['updated_polygons' => $polygonsClipped]), 'updated_at' => now(), - ]); - + ]); + } } catch (Exception $e) { Log::error('Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); diff --git a/app/Services/PolygonService.php b/app/Services/PolygonService.php index 96ec99ca0..4c3a1fc2d 100755 --- a/app/Services/PolygonService.php +++ b/app/Services/PolygonService.php @@ -529,7 +529,7 @@ public function processClippedPolygons(array $polygonUuids) if (isset($feature['properties']['poly_id'])) { $poly_id = $feature['properties']['poly_id']; $result = CreateVersionPolygonGeometryHelper::createVersionPolygonGeometry($poly_id, json_encode(['geometry' => $feature])); - + if (isset($result->original['uuid'])) { $uuids[] = $result->original['uuid']; } From a55ffbb2ee3ed910caa89d549a5b2b63ca233375 Mon Sep 17 00:00:00 2001 From: JORGE Date: Tue, 8 Oct 2024 16:07:54 -0400 Subject: [PATCH 19/64] [TM-1127] add job to geojson and KML --- .../TerrafundCreateGeometryController.php | 29 +++++++------------ app/Jobs/InsertGeojsonToDBJob.php | 5 ++-- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index 416b56ae7..9aa6dfc52 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -226,7 +226,7 @@ function ($attribute, $value, $fail) { $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && filter_var($body['submit_polygon_loaded'], FILTER_VALIDATE_BOOLEAN); if (! $polygonLoadedList && ! $submitPolygonsLoaded) { - $uuid = App::make(PolygonService::class)->insertGeojsonToDB($geojsonFilename, $site_id, 'site', $body['primary_uuid'] ?? null); + $job = new InsertGeojsonToDBJob($geojsonFilename, $site_id, 'site', $body['primary_uuid'] ?? null); } if ($polygonLoadedList) { @@ -237,16 +237,12 @@ function ($attribute, $value, $fail) { } if ($submitPolygonsLoaded) { - $uuid = App::make(PolygonService::class)->insertGeojsonToDB($filename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); + $job = new InsertGeojsonToDBJob($filename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); } - if (isset($uuid['error'])) { - return response()->json(['error' => 'Geometry not inserted into DB', 'message' => $uuid['error']], 500); - } - - App::make(SiteService::class)->setSiteToRestorationInProgress($site_id); - - return response()->json(['message' => 'KML file processed and inserted successfully', 'uuid' => $uuid], 200); + $jobUUID = $job->getJobUuid(); + dispatch($job); + return response()->json(['message' => 'KML queued to insert', 'job_uuid' => $jobUUID], 200); } @@ -398,7 +394,7 @@ public function uploadShapefile(Request $request) } $jobUUID = $job->getJobUuid(); dispatch($job); - return response()->json(['message' => 'Shape file processed and inserted successfully', 'job_uuid' => $jobUUID], 200); + return response()->json(['message' => 'Shapefile queued to insert', 'job_uuid' => $jobUUID], 200); } else { return response()->json(['error' => 'Failed to open the ZIP file'], 400); } @@ -591,7 +587,7 @@ public function uploadGeoJSONFile(Request $request) $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && filter_var($body['submit_polygon_loaded'], FILTER_VALIDATE_BOOLEAN); if (! $polygonLoadedList && ! $submitPolygonsLoaded) { - $uuid = App::make(PolygonService::class)->insertGeojsonToDB($filename, $site_id, 'site', $body['primary_uuid'] ?? null); + $job = new InsertGeojsonToDBJob($filename, $site_id, 'site', $body['primary_uuid'] ?? null); } if ($polygonLoadedList) { @@ -603,15 +599,12 @@ public function uploadGeoJSONFile(Request $request) } if ($submitPolygonsLoaded) { - $uuid = App::make(PolygonService::class)->insertGeojsonToDB($filename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); - } - - if (is_array($uuid) && isset($uuid['error'])) { - return response()->json(['error' => 'Failed to insert GeoJSON data into the database', 'message' => $uuid['error']], 500); + $job = new InsertGeojsonToDBJob($filename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); } - App::make(SiteService::class)->setSiteToRestorationInProgress($site_id); - return response()->json(['message' => 'Geojson file processed and inserted successfully', 'uuid' => $uuid], 200); + $jobUUID = $job->getJobUuid(); + dispatch($job); + return response()->json(['message' => 'Geojson queued to insert', 'job_uuid' => $jobUUID], 200); } diff --git a/app/Jobs/InsertGeojsonToDBJob.php b/app/Jobs/InsertGeojsonToDBJob.php index 3f8796b4a..1fe61b06b 100755 --- a/app/Jobs/InsertGeojsonToDBJob.php +++ b/app/Jobs/InsertGeojsonToDBJob.php @@ -78,12 +78,12 @@ public function handle(PolygonService $service) $this->primary_uuid, $this->submit_polygon_loaded ); - if (isset($uuids['error'])) { + if (true) { $errorMessage = is_array($uuids['error']) ? json_encode($uuids['error'], JSON_PRETTY_PRINT) : strval($uuids['error']); - throw new \Exception($errorMessage); + throw new \Exception($errorMessage, 500); } App::make(SiteService::class)->setSiteToRestorationInProgress($this->entity_uuid); @@ -98,6 +98,7 @@ public function handle(PolygonService $service) 'status' => self::STATUS_FAILED, 'payload' => ['error' => $e->getMessage()], 'updated_at' => now(), + 'statusCode' => $e->getCode() ]); } } From 2879e6c022ebe81c99d5bea145d96e1c06421a00 Mon Sep 17 00:00:00 2001 From: JORGE Date: Tue, 8 Oct 2024 16:12:11 -0400 Subject: [PATCH 20/64] [TM-1127] fix name error message in geom type --- app/Validators/Extensions/Polygons/GeometryType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Validators/Extensions/Polygons/GeometryType.php b/app/Validators/Extensions/Polygons/GeometryType.php index 52a2ebe7c..f1418a7b6 100644 --- a/app/Validators/Extensions/Polygons/GeometryType.php +++ b/app/Validators/Extensions/Polygons/GeometryType.php @@ -13,7 +13,7 @@ class GeometryType extends Extension public static $message = [ 'key' => 'GEOMETRY_TYPE', - 'message' => 'The geometry must by of polygon type', + 'message' => 'The geometry must be of polygon or multipolygon type', ]; public const VALID_TYPE_POLYGON = 'POLYGON'; From d4941bbbf5a57d83caa96b2f909f02a4eef33a82 Mon Sep 17 00:00:00 2001 From: JORGE Date: Tue, 8 Oct 2024 16:18:18 -0400 Subject: [PATCH 21/64] [TM-1127] add status codes to import geom to DB --- app/Jobs/InsertGeojsonToDBJob.php | 5 ++++- app/Services/SiteService.php | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/Jobs/InsertGeojsonToDBJob.php b/app/Jobs/InsertGeojsonToDBJob.php index 1fe61b06b..c9475ab5c 100755 --- a/app/Jobs/InsertGeojsonToDBJob.php +++ b/app/Jobs/InsertGeojsonToDBJob.php @@ -12,6 +12,8 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; +use Illuminate\Http\Exceptions\HttpResponseException; +use Illuminate\Http\Response; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\App; @@ -83,7 +85,7 @@ public function handle(PolygonService $service) ? json_encode($uuids['error'], JSON_PRETTY_PRINT) : strval($uuids['error']); - throw new \Exception($errorMessage, 500); + throw new \Exception($errorMessage, Response::HTTP_INTERNAL_SERVER_ERROR); } App::make(SiteService::class)->setSiteToRestorationInProgress($this->entity_uuid); @@ -91,6 +93,7 @@ public function handle(PolygonService $service) 'status' => self::STATUS_SUCCEEDED, 'payload' => json_encode($uuids), 'updated_at' => now(), + 'statusCode' => Response::HTTP_OK ]); } catch (Exception $e) { diff --git a/app/Services/SiteService.php b/app/Services/SiteService.php index 137fafbac..2a9ca49e9 100644 --- a/app/Services/SiteService.php +++ b/app/Services/SiteService.php @@ -3,18 +3,23 @@ namespace App\Services; use App\Models\V2\Sites\Site; +use Illuminate\Http\Response; class SiteService { public static function setSiteToRestorationInProgress($site_uuid) { + try { if (! $site_uuid) { - return; + return; } $site = Site::where('uuid', $site_uuid)->first(); if (is_null($site)) { return; } $site->restorationInProgress(); + } catch(\Exception $e) { + throw new \Exception($e->getMessage(), Response::HTTP_NOT_MODIFIED); + } } } From b6a772f82308d1882e409240eae43baf1de7cd8a Mon Sep 17 00:00:00 2001 From: JORGE Date: Wed, 9 Oct 2024 09:29:38 -0400 Subject: [PATCH 22/64] [TM-1127] add status codes --- app/Jobs/FixPolygonOverlapJob.php | 3 +++ app/Jobs/InsertGeojsonToDBJob.php | 2 +- app/Jobs/RunSitePolygonsValidationJob.php | 11 +++-------- app/Services/PolygonService.php | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/Jobs/FixPolygonOverlapJob.php b/app/Jobs/FixPolygonOverlapJob.php index 8db3b2faf..bac64b883 100644 --- a/app/Jobs/FixPolygonOverlapJob.php +++ b/app/Jobs/FixPolygonOverlapJob.php @@ -16,6 +16,7 @@ use Illuminate\Support\Facades\Auth; use Throwable; use Exception; +use Illuminate\Http\Response; class FixPolygonOverlapJob implements ShouldQueue { @@ -81,6 +82,7 @@ public function handle(): void 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), 'updated_at' => now(), + 'statusCode' => $e->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR ]); } catch (Throwable $e) { Log::error('Throwable Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); @@ -89,6 +91,7 @@ public function handle(): void 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), 'updated_at' => now(), + 'statusCode' => $e->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR ]); } } diff --git a/app/Jobs/InsertGeojsonToDBJob.php b/app/Jobs/InsertGeojsonToDBJob.php index c9475ab5c..51a4a3b29 100755 --- a/app/Jobs/InsertGeojsonToDBJob.php +++ b/app/Jobs/InsertGeojsonToDBJob.php @@ -101,7 +101,7 @@ public function handle(PolygonService $service) 'status' => self::STATUS_FAILED, 'payload' => ['error' => $e->getMessage()], 'updated_at' => now(), - 'statusCode' => $e->getCode() + 'statusCode' => $e->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR ]); } } diff --git a/app/Jobs/RunSitePolygonsValidationJob.php b/app/Jobs/RunSitePolygonsValidationJob.php index 587b1d565..686fad22f 100644 --- a/app/Jobs/RunSitePolygonsValidationJob.php +++ b/app/Jobs/RunSitePolygonsValidationJob.php @@ -14,6 +14,7 @@ use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; use Illuminate\Http\Request; +use Illuminate\Http\Response; use Throwable; @@ -71,6 +72,7 @@ public function handle(PolygonValidationService $validationService) 'status' => self::STATUS_SUCCEEDED, 'payload' => 'Validation completed for all site polygons', 'updated_at' => now(), + 'statusCode' => Response::HTTP_OK, ]); } catch (Exception $e) { @@ -80,14 +82,7 @@ public function handle(PolygonValidationService $validationService) 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), 'updated_at' => now(), - ]); - } catch (Throwable $e) { - Log::error('Throwable Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); - - DelayedJob::where('uuid', $this->job_uuid)->update([ - 'status' => self::STATUS_FAILED, - 'payload' => json_encode(['error' => $e->getMessage()]), - 'updated_at' => now(), + 'statusCode' => $e->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR ]); } } diff --git a/app/Services/PolygonService.php b/app/Services/PolygonService.php index 4c3a1fc2d..24c814510 100755 --- a/app/Services/PolygonService.php +++ b/app/Services/PolygonService.php @@ -542,7 +542,7 @@ public function processClippedPolygons(array $polygonUuids) $polygonUuids = array_values($polygonUuids); $newPolygonUuids = array_merge($uuids, $polygonUuids); } else { - Log::error('Error clipping polygons', ['clippedPolygons' => $clippedPolygons]); + throw new \Exception('Error processing polygons', Response::HTTP_INTERNAL_SERVER_ERROR); } if (! empty($uuids)) { From 2f7b0816a89118b57abcec893636dfed06418599 Mon Sep 17 00:00:00 2001 From: JORGE Date: Wed, 9 Oct 2024 09:34:17 -0400 Subject: [PATCH 23/64] [TM-1127] lint fix --- .../CreateVersionPolygonGeometryHelper.php | 1 + app/Helpers/GeometryHelper.php | 2 + .../TerrafundClipGeometryController.php | 14 ++-- .../TerrafundCreateGeometryController.php | 50 ++++++----- app/Jobs/FixPolygonOverlapJob.php | 83 ++++++++++--------- app/Jobs/InsertGeojsonToDBJob.php | 26 +++--- app/Jobs/RunSitePolygonsValidationJob.php | 19 +++-- app/Services/PolygonService.php | 6 +- app/Services/PolygonValidationService.php | 25 +++--- app/Services/SiteService.php | 22 ++--- .../Extensions/Polygons/GeometryType.php | 3 +- 11 files changed, 132 insertions(+), 119 deletions(-) diff --git a/app/Helpers/CreateVersionPolygonGeometryHelper.php b/app/Helpers/CreateVersionPolygonGeometryHelper.php index 95962ba39..6abdda79a 100644 --- a/app/Helpers/CreateVersionPolygonGeometryHelper.php +++ b/app/Helpers/CreateVersionPolygonGeometryHelper.php @@ -45,6 +45,7 @@ public static function createVersionPolygonGeometry(string $uuid, $geometry) return response()->json(['message' => 'Site polygon version created successfully.', 'geometry' => $geometry, 'uuid' => $newPolygonVersion->poly_id], 201); } + return response()->json(['message' => 'Failed to create site polygon version.'], 500); } catch (\Exception $e) { throw $e; diff --git a/app/Helpers/GeometryHelper.php b/app/Helpers/GeometryHelper.php index 7f6a8c6ac..ba5bc6a46 100755 --- a/app/Helpers/GeometryHelper.php +++ b/app/Helpers/GeometryHelper.php @@ -361,8 +361,10 @@ public static function getProjectPolygonsUuids($projectId) { $project = Project::where('id', $projectId)->firstOrFail(); $projectPolygonUuids = $project->sitePolygons()->pluck('poly_id')->toArray(); + return $projectPolygonUuids; } + public static function getSitePolygonsUuids($uuid) { return SitePolygon::where('site_id', $uuid)->where('is_active', true)->get()->pluck('poly_id'); diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php index 9f4d9ab4d..32180ebeb 100644 --- a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php @@ -2,15 +2,12 @@ namespace App\Http\Controllers\V2\Terrafund; -use App\Helpers\CreateVersionPolygonGeometryHelper; use App\Helpers\GeometryHelper; -use App\Helpers\PolygonGeometryHelper; +use App\Jobs\FixPolygonOverlapJob; use App\Models\V2\Sites\CriteriaSite; use App\Models\V2\Sites\Site; use App\Models\V2\Sites\SitePolygon; use App\Services\PolygonService; -use App\Services\PythonService; -use App\Jobs\FixPolygonOverlapJob; use Illuminate\Http\Request; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Auth; @@ -25,18 +22,21 @@ public function clipOverlappingPolygonsBySite(string $uuid) $job = new FixPolygonOverlapJob($polygonUuids, $user->id); $jobUUID = $job->getJobUuid(); dispatch($job); + return response()->json(['job_uuid' => $jobUUID], 200); } + public function clipOverlappingPolygonsOfProjectBySite(string $uuid) { - $user = Auth::user(); + $user = Auth::user(); $sitePolygon = Site::isUuid($uuid)->first(); $projectId = $sitePolygon->project_id ?? null; $polygonUuids = GeometryHelper::getProjectPolygonsUuids($projectId); $job = new FixPolygonOverlapJob($polygonUuids, $user->id); $jobUUID = $job->getJobUuid(); dispatch($job); + return response()->json(['job_uuid' => $jobUUID], 200); } @@ -83,6 +83,7 @@ public function clipOverlappingPolygons(Request $request) $jobUUID = $job->getJobUuid(); dispatch($job); } + return response()->json(['job_uuid' => $jobUUID,], 200); } @@ -106,8 +107,7 @@ public function clipOverlappingPolygon(string $uuid) array_unshift($polygonUuids, $uuid); $polygonsClipped = App::make(PolygonService::class)->processClippedPolygons($polygonUuids); + return response()->json(['updated_polygons' => $polygonsClipped], 200); } - - } diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index 9aa6dfc52..07858390c 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -32,7 +32,6 @@ use Illuminate\Support\Facades\Response; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; -use Illuminate\Validation\ValidationException; use Symfony\Component\Process\Process; class TerrafundCreateGeometryController extends Controller @@ -67,7 +66,6 @@ public function storeGeometry(Request $request) return response()->json(['uuid' => $polygonGeometry->uuid], 200); } - public function validateDataInDB(Request $request) { $polygonUuid = $request->input('uuid'); @@ -168,7 +166,7 @@ function ($attribute, $value, $fail) { return response()->json(['error' => 'Failed to convert KML to GeoJSON', 'message' => $process->getErrorOutput()], 500); } - + $uuid = App::make(PolygonService::class)->insertGeojsonToDB($geojsonFilename, $entity_uuid, $entity_type); if (isset($uuid['error'])) { return response()->json(['error' => 'Geometry not inserted into DB', 'message' => $uuid['error']], 500); @@ -241,8 +239,9 @@ function ($attribute, $value, $fail) { } $jobUUID = $job->getJobUuid(); - dispatch($job); - return response()->json(['message' => 'KML queued to insert', 'job_uuid' => $jobUUID], 200); + dispatch($job); + + return response()->json(['message' => 'KML queued to insert', 'job_uuid' => $jobUUID], 200); } @@ -378,7 +377,7 @@ public function uploadShapefile(Request $request) $polygonLoadedList = isset($body['polygon_loaded']) && filter_var($body['polygon_loaded'], FILTER_VALIDATE_BOOLEAN); $submitPolygonsLoaded = $request->input('submit_polygon_loaded') === true || $request->input('submit_polygon_loaded') === 'true'; if (! $polygonLoadedList && ! $submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($geojsonFilename, $site_id, 'site', $body['primary_uuid'] ?? null); + $job = new InsertGeojsonToDBJob($geojsonFilename, $site_id, 'site', $body['primary_uuid'] ?? null); } if ($polygonLoadedList) { @@ -394,6 +393,7 @@ public function uploadShapefile(Request $request) } $jobUUID = $job->getJobUuid(); dispatch($job); + return response()->json(['message' => 'Shapefile queued to insert', 'job_uuid' => $jobUUID], 200); } else { return response()->json(['error' => 'Failed to open the ZIP file'], 400); @@ -604,6 +604,7 @@ public function uploadGeoJSONFile(Request $request) $jobUUID = $job->getJobUuid(); dispatch($job); + return response()->json(['message' => 'Geojson queued to insert', 'job_uuid' => $jobUUID], 200); } @@ -1150,23 +1151,24 @@ private function handlePolygonValidation($polygonUuid, $response, $criteriaId): public function runValidationPolygon(string $uuid) { - try{ - $request = new Request(['uuid' => $uuid]); - - $this->validateOverlapping($request); - $this->checkSelfIntersection($request); - $this->validateCoordinateSystem($request); - $this->validatePolygonSize($request); - $this->checkWithinCountry($request); - $this->checkBoundarySegments($request); - $this->getGeometryType($request); - $this->validateEstimatedArea($request); - $this->validateDataInDB($request); - } catch(\Exception $e) { - Log::error('Error during validation polygon: ' . $e->getMessage()); - throw $e; - } - + try { + $request = new Request(['uuid' => $uuid]); + + $this->validateOverlapping($request); + $this->checkSelfIntersection($request); + $this->validateCoordinateSystem($request); + $this->validatePolygonSize($request); + $this->checkWithinCountry($request); + $this->checkBoundarySegments($request); + $this->getGeometryType($request); + $this->validateEstimatedArea($request); + $this->validateDataInDB($request); + } catch(\Exception $e) { + Log::error('Error during validation polygon: ' . $e->getMessage()); + + throw $e; + } + } public function sendRunValidationPolygon(Request $request) @@ -1188,6 +1190,7 @@ public function runSiteValidationPolygon(Request $request) $job = new RunSitePolygonsValidationJob($sitePolygonsUuids); $jobUUID = $job->getJobUuid(); dispatch($job); + return response()->json(['message' => 'Validation completed for all site polygons', 'job_uuid' => $jobUUID], 200); } catch (\Exception $e) { Log::error('Error during site validation polygon: ' . $e->getMessage()); @@ -1203,6 +1206,7 @@ public function runPolygonsValidation(Request $request) $job = new RunSitePolygonsValidationJob($uuids); $jobUUID = $job->getJobUuid(); dispatch($job); + return response()->json(['message' => 'Validation completed for these polygons', 'job_uuid' => $jobUUID], 200); } catch (\Exception $e) { return response()->json(['error' => 'An error occurred during validation'], 500); diff --git a/app/Jobs/FixPolygonOverlapJob.php b/app/Jobs/FixPolygonOverlapJob.php index bac64b883..f372d5137 100644 --- a/app/Jobs/FixPolygonOverlapJob.php +++ b/app/Jobs/FixPolygonOverlapJob.php @@ -5,32 +5,37 @@ use App\Http\Middleware\SetAuthenticatedUserForJob; use App\Models\DelayedJob; use App\Services\PolygonService; +use Exception; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; -use Illuminate\Support\Facades\Log; use Illuminate\Foundation\Bus\Dispatchable; +use Illuminate\Http\Response; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\App; -use Illuminate\Support\Str; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Log; +use Illuminate\Support\Str; use Throwable; -use Exception; -use Illuminate\Http\Response; class FixPolygonOverlapJob implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Dispatchable; + use InteractsWithQueue; + use Queueable; + use SerializesModels; private const STATUS_PENDING = 'pending'; private const STATUS_FAILED = 'failed'; private const STATUS_SUCCEEDED = 'succeeded'; - protected $polygonService; + protected $polygonUuids; + protected $job_uuid; + public $authUserId; /** @@ -45,6 +50,7 @@ public function __construct(array $polygonUuids, int $authUserId) $this->authUserId = $authUserId; } + /** * Get the middleware the job should pass through. * @@ -54,46 +60,47 @@ public function middleware() { return [new SetAuthenticatedUserForJob]; } + /** * Execute the job. */ public function handle(): void { - + try { - DelayedJob::create([ - 'uuid' => $this->job_uuid, - 'status' => self::STATUS_PENDING, - 'created_at' => now(), - ]); - $user = Auth::user(); - if ($user) { - $polygonsClipped = App::make(PolygonService::class)->processClippedPolygons($this->polygonUuids); + DelayedJob::create([ + 'uuid' => $this->job_uuid, + 'status' => self::STATUS_PENDING, + 'created_at' => now(), + ]); + $user = Auth::user(); + if ($user) { + $polygonsClipped = App::make(PolygonService::class)->processClippedPolygons($this->polygonUuids); + DelayedJob::where('uuid', $this->job_uuid)->update([ + 'status' => self::STATUS_SUCCEEDED, + 'payload' => json_encode(['updated_polygons' => $polygonsClipped]), + 'updated_at' => now(), + ]); + } + } catch (Exception $e) { + Log::error('Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); + + DelayedJob::where('uuid', $this->job_uuid)->update([ + 'status' => self::STATUS_FAILED, + 'payload' => json_encode(['error' => $e->getMessage()]), + 'updated_at' => now(), + 'statusCode' => $e->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR, + ]); + } catch (Throwable $e) { + Log::error('Throwable Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); + DelayedJob::where('uuid', $this->job_uuid)->update([ - 'status' => self::STATUS_SUCCEEDED, - 'payload' => json_encode(['updated_polygons' => $polygonsClipped]), - 'updated_at' => now(), + 'status' => self::STATUS_FAILED, + 'payload' => json_encode(['error' => $e->getMessage()]), + 'updated_at' => now(), + 'statusCode' => $e->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR, ]); - } - } catch (Exception $e) { - Log::error('Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); - - DelayedJob::where('uuid', $this->job_uuid)->update([ - 'status' => self::STATUS_FAILED, - 'payload' => json_encode(['error' => $e->getMessage()]), - 'updated_at' => now(), - 'statusCode' => $e->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR - ]); - } catch (Throwable $e) { - Log::error('Throwable Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); - - DelayedJob::where('uuid', $this->job_uuid)->update([ - 'status' => self::STATUS_FAILED, - 'payload' => json_encode(['error' => $e->getMessage()]), - 'updated_at' => now(), - 'statusCode' => $e->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR - ]); - } + } } public function getJobUuid() diff --git a/app/Jobs/InsertGeojsonToDBJob.php b/app/Jobs/InsertGeojsonToDBJob.php index 51a4a3b29..08f495353 100755 --- a/app/Jobs/InsertGeojsonToDBJob.php +++ b/app/Jobs/InsertGeojsonToDBJob.php @@ -2,24 +2,19 @@ namespace App\Jobs; -use App\Helpers\GeometryHelper; use App\Models\DelayedJob; use App\Services\PolygonService; use App\Services\SiteService; -use App\Validators\SitePolygonValidator; use Exception; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Http\Exceptions\HttpResponseException; use Illuminate\Http\Response; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\App; -use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; -use Throwable; class InsertGeojsonToDBJob implements ShouldQueue { @@ -80,12 +75,12 @@ public function handle(PolygonService $service) $this->primary_uuid, $this->submit_polygon_loaded ); - if (true) { - $errorMessage = is_array($uuids['error']) - ? json_encode($uuids['error'], JSON_PRETTY_PRINT) - : strval($uuids['error']); - - throw new \Exception($errorMessage, Response::HTTP_INTERNAL_SERVER_ERROR); + if (true) { + $errorMessage = is_array($uuids['error']) + ? json_encode($uuids['error'], JSON_PRETTY_PRINT) + : strval($uuids['error']); + + throw new \Exception($errorMessage, Response::HTTP_INTERNAL_SERVER_ERROR); } App::make(SiteService::class)->setSiteToRestorationInProgress($this->entity_uuid); @@ -93,22 +88,21 @@ public function handle(PolygonService $service) 'status' => self::STATUS_SUCCEEDED, 'payload' => json_encode($uuids), 'updated_at' => now(), - 'statusCode' => Response::HTTP_OK + 'statusCode' => Response::HTTP_OK, ]); - + } catch (Exception $e) { DelayedJob::where('uuid', $this->job_uuid)->update([ 'status' => self::STATUS_FAILED, 'payload' => ['error' => $e->getMessage()], 'updated_at' => now(), - 'statusCode' => $e->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR + 'statusCode' => $e->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR, ]); } } + public function getJobUuid() { return $this->job_uuid; } - - } diff --git a/app/Jobs/RunSitePolygonsValidationJob.php b/app/Jobs/RunSitePolygonsValidationJob.php index 686fad22f..c9610eaf7 100644 --- a/app/Jobs/RunSitePolygonsValidationJob.php +++ b/app/Jobs/RunSitePolygonsValidationJob.php @@ -2,34 +2,35 @@ namespace App\Jobs; -use App\Helpers\GeometryHelper; use App\Models\DelayedJob; use App\Services\PolygonValidationService; use Exception; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; +use Illuminate\Http\Request; +use Illuminate\Http\Response; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; -use Illuminate\Http\Request; -use Illuminate\Http\Response; -use Throwable; - class RunSitePolygonsValidationJob implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Dispatchable; + use InteractsWithQueue; + use Queueable; + use SerializesModels; private const STATUS_PENDING = 'pending'; private const STATUS_FAILED = 'failed'; private const STATUS_SUCCEEDED = 'succeeded'; protected $uuid; + protected $job_uuid; - protected $sitePolygonsUuids; + protected $sitePolygonsUuids; /** * Create a new job instance. @@ -77,12 +78,12 @@ public function handle(PolygonValidationService $validationService) } catch (Exception $e) { Log::error('Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); - + DelayedJob::where('uuid', $this->job_uuid)->update([ 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), 'updated_at' => now(), - 'statusCode' => $e->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR + 'statusCode' => $e->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR, ]); } } diff --git a/app/Services/PolygonService.php b/app/Services/PolygonService.php index 24c814510..3a7f4a192 100755 --- a/app/Services/PolygonService.php +++ b/app/Services/PolygonService.php @@ -475,6 +475,7 @@ private function isValidDate($date) return false; } } + /** * @throws ValidationException */ @@ -487,7 +488,7 @@ public function insertGeojsonToDB(string $geojsonFilename, ?string $entity_uuid if ($entity_type === 'project' || $entity_type === 'project-pitch') { $entity = $this->getEntity($entity_type, $entity_uuid); - + $hasBeenDeleted = GeometryHelper::deletePolygonWithRelated($entity); if ($entity && $hasBeenDeleted) { @@ -517,6 +518,7 @@ public function insertGeojsonToDB(string $geojsonFilename, ?string $entity_uuid } } } + public function processClippedPolygons(array $polygonUuids) { $geojson = GeometryHelper::getPolygonsGeojson($polygonUuids); @@ -529,7 +531,7 @@ public function processClippedPolygons(array $polygonUuids) if (isset($feature['properties']['poly_id'])) { $poly_id = $feature['properties']['poly_id']; $result = CreateVersionPolygonGeometryHelper::createVersionPolygonGeometry($poly_id, json_encode(['geometry' => $feature])); - + if (isset($result->original['uuid'])) { $uuids[] = $result->original['uuid']; } diff --git a/app/Services/PolygonValidationService.php b/app/Services/PolygonValidationService.php index 9ee9357bc..f41e7c0a6 100644 --- a/app/Services/PolygonValidationService.php +++ b/app/Services/PolygonValidationService.php @@ -4,7 +4,6 @@ use App\Models\V2\PolygonGeometry; use App\Models\V2\Sites\SitePolygon; -use App\Services\PolygonService; use App\Validators\Extensions\Polygons\EstimatedArea; use App\Validators\Extensions\Polygons\FeatureBounds; use App\Validators\Extensions\Polygons\GeometryType; @@ -21,6 +20,7 @@ class PolygonValidationService public function validateOverlapping(Request $request) { $uuid = $request->input('uuid'); + return $this->handlePolygonValidation( $uuid, NotOverlapping::getIntersectionData($uuid), @@ -33,7 +33,7 @@ public function checkSelfIntersection(Request $request) $uuid = $request->query('uuid'); $geometry = PolygonGeometry::where('uuid', $uuid)->first(); - if (!$geometry) { + if (! $geometry) { return ['error' => 'Geometry not found', 'status' => 404]; } @@ -47,7 +47,7 @@ public function checkSelfIntersection(Request $request) 'geometry_id' => $geometry->id, 'insertion_success' => $insertionSuccess, 'valid' => $isSimple, - 'status' => 200 + 'status' => 200, ]; } @@ -67,7 +67,7 @@ public function validatePolygonSize(Request $request) $uuid = $request->query('uuid'); $geometry = PolygonGeometry::isUuid($uuid)->first(); - if (!$geometry) { + if (! $geometry) { return ['error' => 'Geometry not found', 'status' => 404]; } @@ -82,7 +82,7 @@ public function validatePolygonSize(Request $request) 'geometry_id' => $geometry->id, 'insertion_success' => $insertionSuccess, 'valid' => $valid, - 'status' => 200 + 'status' => 200, ]; } @@ -102,7 +102,7 @@ public function checkBoundarySegments(Request $request) $uuid = $request->query('uuid'); $geometry = PolygonGeometry::isUuid($uuid)->first(); - if (!$geometry) { + if (! $geometry) { return ['error' => 'Geometry not found', 'status' => 404]; } $spikes = Spikes::detectSpikes($geometry->geo_json); @@ -115,7 +115,7 @@ public function checkBoundarySegments(Request $request) 'geometry_id' => $uuid, 'insertion_success' => $insertionSuccess, 'valid' => $valid, - 'status' => 200 + 'status' => 200, ]; } @@ -134,7 +134,7 @@ public function getGeometryType(Request $request) 'geometry_type' => $geometryType, 'valid' => $valid, 'insertion_success' => $insertionSuccess, - 'status' => 200 + 'status' => 200, ]; } else { return ['error' => 'Geometry not found for the given UUID', 'status' => 404]; @@ -158,7 +158,7 @@ public function validateDataInDB(Request $request) $fieldsToValidate = ['poly_name', 'plantstart', 'plantend', 'practice', 'target_sys', 'distr', 'num_trees']; $sitePolygon = SitePolygon::forPolygonGeometry($polygonUuid)->first(); - if (!$sitePolygon) { + if (! $sitePolygon) { return ['valid' => false, 'message' => 'No site polygon found with the specified UUID.', 'status' => 404]; } @@ -170,14 +170,14 @@ public function validateDataInDB(Request $request) $validationErrors[] = [ 'field' => $field, 'error' => $value, - 'exists' => !is_null($value) && $value !== '', + 'exists' => ! is_null($value) && $value !== '', ]; } } $isValid = empty($validationErrors); $responseData = ['valid' => $isValid]; - if (!$isValid) { + if (! $isValid) { $responseData['message'] = 'Some attributes of the site polygon are invalid.'; } @@ -201,6 +201,7 @@ protected function handlePolygonValidation($polygonUuid, $response, $criteriaId) return $response; } + public function runValidationPolygon(string $uuid) { $request = new Request(['uuid' => $uuid]); @@ -215,4 +216,4 @@ public function runValidationPolygon(string $uuid) $this->validateEstimatedArea($request); $this->validateDataInDB($request); } -} \ No newline at end of file +} diff --git a/app/Services/SiteService.php b/app/Services/SiteService.php index 2a9ca49e9..d5fbba874 100644 --- a/app/Services/SiteService.php +++ b/app/Services/SiteService.php @@ -9,17 +9,17 @@ class SiteService { public static function setSiteToRestorationInProgress($site_uuid) { - try { - if (! $site_uuid) { - return; + try { + if (! $site_uuid) { + return; + } + $site = Site::where('uuid', $site_uuid)->first(); + if (is_null($site)) { + return; + } + $site->restorationInProgress(); + } catch(\Exception $e) { + throw new \Exception($e->getMessage(), Response::HTTP_NOT_MODIFIED); } - $site = Site::where('uuid', $site_uuid)->first(); - if (is_null($site)) { - return; - } - $site->restorationInProgress(); - } catch(\Exception $e) { - throw new \Exception($e->getMessage(), Response::HTTP_NOT_MODIFIED); - } } } diff --git a/app/Validators/Extensions/Polygons/GeometryType.php b/app/Validators/Extensions/Polygons/GeometryType.php index f1418a7b6..899783bc7 100644 --- a/app/Validators/Extensions/Polygons/GeometryType.php +++ b/app/Validators/Extensions/Polygons/GeometryType.php @@ -5,7 +5,6 @@ use App\Models\V2\PolygonGeometry; use App\Validators\Extensions\Extension; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Log; class GeometryType extends Extension { @@ -33,6 +32,7 @@ public static function passes($attribute, $value, $parameters, $validator): bool public static function uuidValid($uuid): bool { $geometryType = PolygonGeometry::getGeometryType($uuid); + return $geometryType === self::VALID_TYPE_POLYGON || $geometryType === self::VALID_TYPE_MULTIPOLYGON; } @@ -42,6 +42,7 @@ public static function geoJsonValid($geojson): bool 'SELECT ST_GeometryType(ST_GeomFromGeoJSON(:geojson)) AS geometry_type', ['geojson' => json_encode($geojson)] ); + return $result->geometry_type === self::VALID_TYPE_POLYGON || $result->geometry_type === self::VALID_TYPE_MULTIPOLYGON; } } From 3f976102f1cfd41377709644bf1a18de23f1691a Mon Sep 17 00:00:00 2001 From: JORGE Date: Wed, 9 Oct 2024 09:39:46 -0400 Subject: [PATCH 24/64] [TM-1127] remove comments --- app/Jobs/InsertGeojsonToDBJob.php | 1 - app/Models/DelayedJob.php | 3 --- 2 files changed, 4 deletions(-) diff --git a/app/Jobs/InsertGeojsonToDBJob.php b/app/Jobs/InsertGeojsonToDBJob.php index 08f495353..dc2935509 100755 --- a/app/Jobs/InsertGeojsonToDBJob.php +++ b/app/Jobs/InsertGeojsonToDBJob.php @@ -27,7 +27,6 @@ class InsertGeojsonToDBJob implements ShouldQueue private const STATUS_FAILED = 'failed'; private const STATUS_SUCCEEDED = 'succeeded'; - // Define the properties for the parameters protected $geojsonFilename; protected $entity_uuid; diff --git a/app/Models/DelayedJob.php b/app/Models/DelayedJob.php index d99016646..aab4bded5 100644 --- a/app/Models/DelayedJob.php +++ b/app/Models/DelayedJob.php @@ -9,10 +9,8 @@ class DelayedJob extends Model { use HasFactory; - // Define the table name explicitly, if necessary (optional) protected $table = 'delayed_jobs'; - // Use mass assignment protection for the fields that are fillable protected $fillable = [ 'uuid', 'status', @@ -20,7 +18,6 @@ class DelayedJob extends Model 'payload', ]; - // Ensure the UUID is cast to string protected $casts = [ 'uuid' => 'string', ]; From 3f6f9efc54263ec9c8e410443fadc84b9e1ac332 Mon Sep 17 00:00:00 2001 From: Limber Mamani <154026979+LimberHope@users.noreply.github.com> Date: Thu, 17 Oct 2024 10:55:22 -0400 Subject: [PATCH 25/64] [TM-1310] update logic to due_at (#504) * [TM-1310] update logic to due_at * [TM-1310] fix lint * [TM-1310] add hours to due_at --- .../OneOff/UpdatePPCDueDatesCommand.php | 54 ++++++++----------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/app/Console/Commands/OneOff/UpdatePPCDueDatesCommand.php b/app/Console/Commands/OneOff/UpdatePPCDueDatesCommand.php index aeb142d79..b8997a8b6 100644 --- a/app/Console/Commands/OneOff/UpdatePPCDueDatesCommand.php +++ b/app/Console/Commands/OneOff/UpdatePPCDueDatesCommand.php @@ -3,6 +3,7 @@ namespace App\Console\Commands\OneOff; use App\Models\V2\Tasks\Task; +use Carbon\Carbon; use Illuminate\Console\Command; use Illuminate\Support\Facades\Log; @@ -28,48 +29,35 @@ class UpdatePPCDueDatesCommand extends Command public function handle() { Log::info('update ppc due dates'); - $datesToUpdate = [ - '2022-12' => '2023-01-06 04:00:00.000', - '2023-01' => '2023-02-03 04:00:00.000', - '2023-02' => '2023-03-03 04:00:00.000', - '2023-03' => '2023-04-07 04:00:00.000', - // Q2 (April-June) 2024 - '2024-04' => '2024-07-05 04:00:00.000', - '2024-05' => '2024-07-05 04:00:00.000', - '2024-06' => '2024-07-05 04:00:00.000', - // Q3 (July-Sept) 2024 - '2024-07' => '2024-10-04 04:00:00.000', - '2024-08' => '2024-10-04 04:00:00.000', - '2024-09' => '2024-10-04 04:00:00.000', - ]; - foreach ($datesToUpdate as $reportingPeriod => $correctDueDate) { - list($year, $month) = explode('-', $reportingPeriod); - $tasks = Task::where(function ($query) use ($reportingPeriod, $year, $month) { - $query->where('period_key', $reportingPeriod) - ->orWhere('period_key', $year . '-' . intval($month)); - })->whereHas('project', function ($query) { - $query->where('framework_key', 'ppc'); - })->get(); - foreach ($tasks as $task) { - $project = $task->project; + $tasks = Task::whereHas('project', function ($query) { + $query->where('framework_key', 'ppc'); + })->get(); + foreach ($tasks as $task) { + $project = $task->project; + if ($project && $project->framework_key == 'ppc') { + $dueAt = Carbon::parse($task->due_at)->setTimezone('UTC'); - if ($project && $project->framework_key == 'ppc') { - $task->due_at = $correctDueDate; + if ($dueAt->hour >= 0 && $dueAt->hour <= 4) { + $task->due_at = $dueAt->setHour(5); $task->save(); } + } - $reports = collect($task->projectReport->get())->concat($task->siteReports)->concat($task->nurseryReports); + $reports = collect([$task->projectReport])->concat($task->siteReports)->concat($task->nurseryReports)->filter(function ($report) { + return $report !== null; + }); - foreach ($reports as $report) { - if ($report->framework_key !== 'ppc') { - continue; - } - $report->due_at = $correctDueDate; + foreach ($reports as $report) { + if ($report->framework_key !== 'ppc') { + continue; + } + $dueAt = Carbon::parse($report->due_at)->setTimezone('UTC'); + if ($dueAt->hour >= 0 && $dueAt->hour <= 4) { + $report->due_at = $dueAt->setHour(5); $report->save(); } } - } $this->info('All due dates updated successfully.'); From 6fe0b3ef9dea5a24daef7036ebb6dbd2677b8a5f Mon Sep 17 00:00:00 2001 From: Jorge Monroy Date: Mon, 21 Oct 2024 10:38:26 -0400 Subject: [PATCH 26/64] [TM-1301] add validator for dates (#513) --- app/Services/PolygonService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Services/PolygonService.php b/app/Services/PolygonService.php index 7acf9a853..0f36f4035 100755 --- a/app/Services/PolygonService.php +++ b/app/Services/PolygonService.php @@ -349,7 +349,7 @@ protected function insertSitePolygonVersion(string $polygonUuid, string $primary } else { $user = User::find(1); } - $newSitePolygon = $sitePolygon->createCopy($user, $polygonUuid, $submit_polygon_loaded, $properties); + $newSitePolygon = $sitePolygon->createCopy($user, $polygonUuid, $submit_polygon_loaded, $this->validateSitePolygonProperties($polygonUuid, $properties)); if (! $newSitePolygon) { return false; } From 392d5efb619b8d42f25119bf2bae9eb601f73c6a Mon Sep 17 00:00:00 2001 From: Limber Mamani <154026979+LimberHope@users.noreply.github.com> Date: Tue, 22 Oct 2024 09:22:34 -0400 Subject: [PATCH 27/64] [TM-1405] add controller to hectares under restoration (#516) * [TM-1405] add controller to hectares under restoration * rename variable * [TM-1405] add constant to indicator * [TM-1405] add path and definition * [TM-1405] update definitions * fix lint * update definition * [TM-1405] improve code * [TM-1405] improve code --- .../GetHectaresRestoredController.php | 89 ++++++++ ...hboardIndicatorHectaresRestorationData.yml | 60 +++++ ...rdIndicatorHectaresRestorationResponse.yml | 4 + openapi-src/V2/definitions/_index.yml | 6 +- ...shboard-indicator-hectares-restoration.yml | 21 ++ openapi-src/V2/paths/_index.yml | 3 + resources/docs/swagger-v2.yml | 207 ++++++++++++++++++ routes/api_v2.php | 2 + 8 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 app/Http/Controllers/V2/Indicators/GetHectaresRestoredController.php create mode 100644 openapi-src/V2/definitions/DashboardIndicatorHectaresRestorationData.yml create mode 100644 openapi-src/V2/definitions/DashboardIndicatorHectaresRestorationResponse.yml create mode 100644 openapi-src/V2/paths/Dashboard/get-v2-dashboard-indicator-hectares-restoration.yml diff --git a/app/Http/Controllers/V2/Indicators/GetHectaresRestoredController.php b/app/Http/Controllers/V2/Indicators/GetHectaresRestoredController.php new file mode 100644 index 000000000..1727aa360 --- /dev/null +++ b/app/Http/Controllers/V2/Indicators/GetHectaresRestoredController.php @@ -0,0 +1,89 @@ +pluck('uuid')->toArray(); + $HECTAREAS_BY_RESTORATION = '5'; + $HECTAREAS_BY_TARGET_LAND_USE_TYPES = '6'; + + $projectsPolygons = $this->getProjectsPolygons($projectsToQuery); + $polygonsUuids = array_column($projectsPolygons, 'uuid'); + + $restorationStrategiesRepresented = $this->polygonToOutputHectares($HECTAREAS_BY_RESTORATION, $polygonsUuids); + $targetLandUseTypesRepresented = $this->polygonToOutputHectares($HECTAREAS_BY_TARGET_LAND_USE_TYPES, $polygonsUuids); + + if (empty($restorationStrategiesRepresented) && empty($targetLandUseTypesRepresented)) { + return response()->json([ + 'restoration_strategies_represented' => [], + 'target_land_use_types_represented' => [], + 'message' => 'No data available for restoration strategies and target land use types.', + ]); + } + + return response()->json([ + 'restoration_strategies_represented' => $this->calculateGroupedHectares($restorationStrategiesRepresented), + 'target_land_use_types_represented' => $this->calculateGroupedHectares($targetLandUseTypesRepresented), + ]); + } catch (Exception $e) { + return response()->json([ + 'error' => 'An error occurred: ' . $e->getMessage(), + ], 500); + } + } + + public function getProjectsPolygons($projects) + { + return DB::select(' + SELECT sp.uuid + FROM site_polygon sp + INNER JOIN v2_sites s ON sp.site_id = s.uuid + INNER JOIN v2_projects p ON s.project_id = p.id + WHERE p.uuid IN ('. implode(',', array_fill(0, count($projects), '?')) .') + ', $projects); + } + + public function polygonToOutputHectares($indicatorId, $polygonsUuids) + { + return DB::select(' + SELECT * + FROM indicator_output_hectares + WHERE indicator_id = ? + AND polygon_id IN (' . implode(',', array_fill(0, count($polygonsUuids), '?')) . ') + ', array_merge([$indicatorId], $polygonsUuids)); + } + + public function calculateGroupedHectares($polygonsToOutputHectares) + { + $hectaresRestored = []; + + foreach ($polygonsToOutputHectares as $hectare) { + $decodedValue = json_decode($hectare->value, true); + + if ($decodedValue) { + foreach ($decodedValue as $key => $value) { + if (! isset($hectaresRestored[$key])) { + $hectaresRestored[$key] = 0; + } + $hectaresRestored[$key] += $value; + } + } + } + + foreach ($hectaresRestored as $key => $value) { + $hectaresRestored[$key] = round($value, 3); + } + + return $hectaresRestored; + } +} diff --git a/openapi-src/V2/definitions/DashboardIndicatorHectaresRestorationData.yml b/openapi-src/V2/definitions/DashboardIndicatorHectaresRestorationData.yml new file mode 100644 index 000000000..3de32b80c --- /dev/null +++ b/openapi-src/V2/definitions/DashboardIndicatorHectaresRestorationData.yml @@ -0,0 +1,60 @@ +type: object +properties: + restoration_strategies_represented: + type: object + properties: + tree-planting: + type: number + description: Total amount for tree planting projects. + tree-planting,direct-seeding: + type: number + description: Total amount for projects involving both tree planting and direct seeding. + assisted-natural-regeneration: + type: number + description: Total amount for assisted natural regeneration projects. + tree-planting,assisted-natural-regeneration: + type: number + description: Total amount for projects involving both tree planting and assisted natural regeneration. + direct-seeding: + type: number + description: Total amount for direct seeding projects. + control: + type: number + description: Total amount for control projects. + null: + type: number + description: Total amount for projects with no specific restoration category. + target_land_use_types_represented: + type: object + properties: + null: + type: number + description: Total amount for projects without a defined land use type. + natural-forest: + type: number + description: Total amount for projects involving natural forest. + agroforest: + type: number + description: Total amount for agroforest projects. + silvopasture: + type: number + description: Total amount for silvopasture projects. + woodlot-or-plantation: + type: number + description: Total amount for woodlot or plantation projects. + riparian-area-or-wetland: + type: number + description: Total amount for riparian area or wetland projects. + agroforest,riparian-area-or-wetland: + type: number + description: Total amount for projects involving both agroforest and riparian area or wetland. + riparian-area-or-wetland,woodlot-or-plantation: + type: number + description: Total amount for projects involving both riparian area or wetland and woodlot or plantation. + 'Open natural ecosystem or Grasslands': + type: number + description: Total amount for projects involving open natural ecosystem or grasslands. + urban-forest: + type: number + description: Total amount for urban forest projects. + diff --git a/openapi-src/V2/definitions/DashboardIndicatorHectaresRestorationResponse.yml b/openapi-src/V2/definitions/DashboardIndicatorHectaresRestorationResponse.yml new file mode 100644 index 000000000..d985ed41c --- /dev/null +++ b/openapi-src/V2/definitions/DashboardIndicatorHectaresRestorationResponse.yml @@ -0,0 +1,4 @@ +type: object +properties: + data: + $ref: './_index.yml#/DashboardIndicatorHectaresRestorationData' diff --git a/openapi-src/V2/definitions/_index.yml b/openapi-src/V2/definitions/_index.yml index 386e4a43e..3843f73ee 100644 --- a/openapi-src/V2/definitions/_index.yml +++ b/openapi-src/V2/definitions/_index.yml @@ -381,4 +381,8 @@ MyOrganisationLite: UpdateMediaRequest: $ref: './UpdateMediaRequest.yml' FileResource: - $ref: './FileResource.yml' \ No newline at end of file + $ref: './FileResource.yml' +DashboardIndicatorHectaresRestorationResponse: + $ref: './DashboardIndicatorHectaresRestorationResponse.yml' +DashboardIndicatorHectaresRestorationData: + $ref: './DashboardIndicatorHectaresRestorationData.yml' \ No newline at end of file diff --git a/openapi-src/V2/paths/Dashboard/get-v2-dashboard-indicator-hectares-restoration.yml b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-indicator-hectares-restoration.yml new file mode 100644 index 000000000..8e2192396 --- /dev/null +++ b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-indicator-hectares-restoration.yml @@ -0,0 +1,21 @@ +summary: Retrieve hectares by restoration +description: | + This endpoint returns hectares restored using data from indicators 5 (restoration strategies) and 6 (target land use types). +parameters: + - name: search + type: string + in: query + description: 'search term to use on the collection' + - name: filter + type: string + in: query + description: 'multiple filters can be applied. syntax is ?filter[foo]=value1,value2$filter[bar]=value3' +responses: + '200': + description: Successful response + schema: + $ref: '../../definitions/_index.yml#/DashboardIndicatorHectaresRestorationResponse' + '400': + description: Bad request + '500': + description: Internal server error \ No newline at end of file diff --git a/openapi-src/V2/paths/_index.yml b/openapi-src/V2/paths/_index.yml index 33ce6a613..110a1efe4 100644 --- a/openapi-src/V2/paths/_index.yml +++ b/openapi-src/V2/paths/_index.yml @@ -2703,6 +2703,9 @@ /v2/dashboard/top-trees-planted: get: $ref: './Dashboard/get-v2-dashboard-top-trees-planted.yml' +/v2/dashboard/indicator/hectares-restoration: + get: + $ref: './Dashboard/get-v2-dashboard-indicator-hectares-restoration.yml' /v2/project-pipeline: get: $ref: './ProjectPipeline/get-v2-project-pipeline.yml' diff --git a/resources/docs/swagger-v2.yml b/resources/docs/swagger-v2.yml index f061b5463..74bc890bf 100644 --- a/resources/docs/swagger-v2.yml +++ b/resources/docs/swagger-v2.yml @@ -44106,6 +44106,129 @@ definitions: type: boolean is_cover: type: boolean + DashboardIndicatorHectaresRestorationResponse: + type: object + properties: + data: + type: object + properties: + restoration_strategies_represented: + type: object + properties: + tree-planting: + type: number + description: Total amount for tree planting projects. + 'tree-planting,direct-seeding': + type: number + description: Total amount for projects involving both tree planting and direct seeding. + assisted-natural-regeneration: + type: number + description: Total amount for assisted natural regeneration projects. + 'tree-planting,assisted-natural-regeneration': + type: number + description: Total amount for projects involving both tree planting and assisted natural regeneration. + direct-seeding: + type: number + description: Total amount for direct seeding projects. + control: + type: number + description: Total amount for control projects. + 'null': + type: number + description: Total amount for projects with no specific restoration category. + target_land_use_types_represented: + type: object + properties: + 'null': + type: number + description: Total amount for projects without a defined land use type. + natural-forest: + type: number + description: Total amount for projects involving natural forest. + agroforest: + type: number + description: Total amount for agroforest projects. + silvopasture: + type: number + description: Total amount for silvopasture projects. + woodlot-or-plantation: + type: number + description: Total amount for woodlot or plantation projects. + riparian-area-or-wetland: + type: number + description: Total amount for riparian area or wetland projects. + 'agroforest,riparian-area-or-wetland': + type: number + description: Total amount for projects involving both agroforest and riparian area or wetland. + 'riparian-area-or-wetland,woodlot-or-plantation': + type: number + description: Total amount for projects involving both riparian area or wetland and woodlot or plantation. + Open natural ecosystem or Grasslands: + type: number + description: Total amount for projects involving open natural ecosystem or grasslands. + urban-forest: + type: number + description: Total amount for urban forest projects. + DashboardIndicatorHectaresRestorationData: + type: object + properties: + restoration_strategies_represented: + type: object + properties: + tree-planting: + type: number + description: Total amount for tree planting projects. + 'tree-planting,direct-seeding': + type: number + description: Total amount for projects involving both tree planting and direct seeding. + assisted-natural-regeneration: + type: number + description: Total amount for assisted natural regeneration projects. + 'tree-planting,assisted-natural-regeneration': + type: number + description: Total amount for projects involving both tree planting and assisted natural regeneration. + direct-seeding: + type: number + description: Total amount for direct seeding projects. + control: + type: number + description: Total amount for control projects. + 'null': + type: number + description: Total amount for projects with no specific restoration category. + target_land_use_types_represented: + type: object + properties: + 'null': + type: number + description: Total amount for projects without a defined land use type. + natural-forest: + type: number + description: Total amount for projects involving natural forest. + agroforest: + type: number + description: Total amount for agroforest projects. + silvopasture: + type: number + description: Total amount for silvopasture projects. + woodlot-or-plantation: + type: number + description: Total amount for woodlot or plantation projects. + riparian-area-or-wetland: + type: number + description: Total amount for riparian area or wetland projects. + 'agroforest,riparian-area-or-wetland': + type: number + description: Total amount for projects involving both agroforest and riparian area or wetland. + 'riparian-area-or-wetland,woodlot-or-plantation': + type: number + description: Total amount for projects involving both riparian area or wetland and woodlot or plantation. + Open natural ecosystem or Grasslands: + type: number + description: Total amount for projects involving open natural ecosystem or grasslands. + urban-forest: + type: number + description: Total amount for urban forest projects. paths: '/v2/tree-species/{entity}/{UUID}': get: @@ -94581,6 +94704,90 @@ paths: description: Bad request '500': description: Internal server error + /v2/dashboard/indicator/hectares-restoration: + get: + summary: Retrieve hectares by restoration + description: | + This endpoint returns hectares restored using data from indicators 5 (restoration strategies) and 6 (target land use types). + parameters: + - name: search + type: string + in: query + description: search term to use on the collection + - name: filter + type: string + in: query + description: 'multiple filters can be applied. syntax is ?filter[foo]=value1,value2$filter[bar]=value3' + responses: + '200': + description: Successful response + schema: + type: object + properties: + data: + type: object + properties: + restoration_strategies_represented: + type: object + properties: + tree-planting: + type: number + description: Total amount for tree planting projects. + 'tree-planting,direct-seeding': + type: number + description: Total amount for projects involving both tree planting and direct seeding. + assisted-natural-regeneration: + type: number + description: Total amount for assisted natural regeneration projects. + 'tree-planting,assisted-natural-regeneration': + type: number + description: Total amount for projects involving both tree planting and assisted natural regeneration. + direct-seeding: + type: number + description: Total amount for direct seeding projects. + control: + type: number + description: Total amount for control projects. + 'null': + type: number + description: Total amount for projects with no specific restoration category. + target_land_use_types_represented: + type: object + properties: + 'null': + type: number + description: Total amount for projects without a defined land use type. + natural-forest: + type: number + description: Total amount for projects involving natural forest. + agroforest: + type: number + description: Total amount for agroforest projects. + silvopasture: + type: number + description: Total amount for silvopasture projects. + woodlot-or-plantation: + type: number + description: Total amount for woodlot or plantation projects. + riparian-area-or-wetland: + type: number + description: Total amount for riparian area or wetland projects. + 'agroforest,riparian-area-or-wetland': + type: number + description: Total amount for projects involving both agroforest and riparian area or wetland. + 'riparian-area-or-wetland,woodlot-or-plantation': + type: number + description: Total amount for projects involving both riparian area or wetland and woodlot or plantation. + Open natural ecosystem or Grasslands: + type: number + description: Total amount for projects involving open natural ecosystem or grasslands. + urban-forest: + type: number + description: Total amount for urban forest projects. + '400': + description: Bad request + '500': + description: Internal server error /v2/project-pipeline: get: operationId: get-v2-fproject-pipeline diff --git a/routes/api_v2.php b/routes/api_v2.php index 85f2ccd5f..dc203d966 100644 --- a/routes/api_v2.php +++ b/routes/api_v2.php @@ -99,6 +99,7 @@ use App\Http\Controllers\V2\FundingType\StoreFundingTypeController; use App\Http\Controllers\V2\FundingType\UpdateFundingTypeController; use App\Http\Controllers\V2\Geometry\GeometryController; +use App\Http\Controllers\V2\Indicators\GetHectaresRestoredController; use App\Http\Controllers\V2\LeadershipTeam\DeleteLeadershipTeamController; use App\Http\Controllers\V2\LeadershipTeam\StoreLeadershipTeamController; use App\Http\Controllers\V2\LeadershipTeam\UpdateLeadershipTeamController; @@ -738,6 +739,7 @@ function () { Route::get('/view-project/{uuid}', [ViewProjectController::class, 'getIfUserIsAllowedToProject']); Route::get('/view-project-list', [ViewProjectController::class, 'getAllProjectsAllowedToUser']); Route::get('/frameworks', [ViewProjectController::class, 'getFrameworks']); + Route::get('/indicator/hectares-restoration', GetHectaresRestoredController::class); }); Route::prefix('project-pipeline')->group(function () { From 8ba6f2ebd06a3690a322a59e04adfe6df3dd9e3c Mon Sep 17 00:00:00 2001 From: Limber Mamani <154026979+LimberHope@users.noreply.github.com> Date: Tue, 22 Oct 2024 13:34:02 -0400 Subject: [PATCH 28/64] [TM-1378] add programme field to active projects (#518) --- .../Controllers/V2/Dashboard/ActiveProjectsTableController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Http/Controllers/V2/Dashboard/ActiveProjectsTableController.php b/app/Http/Controllers/V2/Dashboard/ActiveProjectsTableController.php index 0fe7c2844..bb35a4866 100644 --- a/app/Http/Controllers/V2/Dashboard/ActiveProjectsTableController.php +++ b/app/Http/Controllers/V2/Dashboard/ActiveProjectsTableController.php @@ -61,6 +61,7 @@ public function getAllProjects($request, $perPage, $page) 'number_of_trees_goal' => $project->trees_grown_goal, 'date_added' => $project->created_at, 'hectares_under_restoration' => round($project->sitePolygons->sum('calc_area')), + 'programme' => $project->framework_key, ]; }); } From a82620432bfacc0278a02a61ca88c25a44ac1d10 Mon Sep 17 00:00:00 2001 From: JORGE Date: Wed, 23 Oct 2024 11:07:08 -0400 Subject: [PATCH 29/64] [TM-1343] add timeout variable in jobs --- app/Jobs/FixPolygonOverlapJob.php | 8 +++++--- app/Jobs/InsertGeojsonToDBJob.php | 9 ++++++--- app/Jobs/RunSitePolygonsValidationJob.php | 4 +++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/Jobs/FixPolygonOverlapJob.php b/app/Jobs/FixPolygonOverlapJob.php index f372d5137..6b35c3a33 100644 --- a/app/Jobs/FixPolygonOverlapJob.php +++ b/app/Jobs/FixPolygonOverlapJob.php @@ -30,6 +30,8 @@ class FixPolygonOverlapJob implements ShouldQueue private const STATUS_FAILED = 'failed'; private const STATUS_SUCCEEDED = 'succeeded'; + public $timeout = 0; + protected $polygonService; protected $polygonUuids; @@ -83,13 +85,13 @@ public function handle(): void ]); } } catch (Exception $e) { - Log::error('Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); + Log::error('Error in Fix Polygon Overlap Job: ' . $e->getMessage()); DelayedJob::where('uuid', $this->job_uuid)->update([ 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), 'updated_at' => now(), - 'statusCode' => $e->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR, + 'statusCode' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); } catch (Throwable $e) { Log::error('Throwable Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); @@ -98,7 +100,7 @@ public function handle(): void 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), 'updated_at' => now(), - 'statusCode' => $e->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR, + 'statusCode' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); } } diff --git a/app/Jobs/InsertGeojsonToDBJob.php b/app/Jobs/InsertGeojsonToDBJob.php index dc2935509..59e82325e 100755 --- a/app/Jobs/InsertGeojsonToDBJob.php +++ b/app/Jobs/InsertGeojsonToDBJob.php @@ -14,6 +14,7 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\App; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; class InsertGeojsonToDBJob implements ShouldQueue @@ -23,6 +24,8 @@ class InsertGeojsonToDBJob implements ShouldQueue use Queueable; use SerializesModels; + public $timeout = 0; + private const STATUS_PENDING = 'pending'; private const STATUS_FAILED = 'failed'; private const STATUS_SUCCEEDED = 'succeeded'; @@ -74,13 +77,12 @@ public function handle(PolygonService $service) $this->primary_uuid, $this->submit_polygon_loaded ); - if (true) { + if (isset($uuids['error'])) { $errorMessage = is_array($uuids['error']) ? json_encode($uuids['error'], JSON_PRETTY_PRINT) : strval($uuids['error']); throw new \Exception($errorMessage, Response::HTTP_INTERNAL_SERVER_ERROR); - } App::make(SiteService::class)->setSiteToRestorationInProgress($this->entity_uuid); DelayedJob::where('uuid', $this->job_uuid)->update([ @@ -91,11 +93,12 @@ public function handle(PolygonService $service) ]); } catch (Exception $e) { + Log::error('Error in InsertGeojsonToDBJob: ' . $e->getMessage()); DelayedJob::where('uuid', $this->job_uuid)->update([ 'status' => self::STATUS_FAILED, 'payload' => ['error' => $e->getMessage()], 'updated_at' => now(), - 'statusCode' => $e->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR, + 'statusCode' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); } } diff --git a/app/Jobs/RunSitePolygonsValidationJob.php b/app/Jobs/RunSitePolygonsValidationJob.php index c9610eaf7..da2bce56c 100644 --- a/app/Jobs/RunSitePolygonsValidationJob.php +++ b/app/Jobs/RunSitePolygonsValidationJob.php @@ -22,6 +22,8 @@ class RunSitePolygonsValidationJob implements ShouldQueue use Queueable; use SerializesModels; + public $timeout = 0; + private const STATUS_PENDING = 'pending'; private const STATUS_FAILED = 'failed'; private const STATUS_SUCCEEDED = 'succeeded'; @@ -83,7 +85,7 @@ public function handle(PolygonValidationService $validationService) 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), 'updated_at' => now(), - 'statusCode' => $e->getCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR, + 'statusCode' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); } } From 358b2e4c78f45f72e6155e44f76de84bb03742d5 Mon Sep 17 00:00:00 2001 From: JORGE Date: Wed, 23 Oct 2024 12:03:19 -0400 Subject: [PATCH 30/64] [TM-1127] add status code for fix overlapping --- .../V2/Terrafund/TerrafundClipGeometryController.php | 7 +++++++ app/Jobs/FixPolygonOverlapJob.php | 1 + 2 files changed, 8 insertions(+) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php index 32180ebeb..63fd7c1d1 100644 --- a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php @@ -15,8 +15,11 @@ class TerrafundClipGeometryController extends TerrafundCreateGeometryController { + private const MAX_EXECUTION_TIME = 340; public function clipOverlappingPolygonsBySite(string $uuid) { + ini_set('max_execution_time', self::MAX_EXECUTION_TIME); + ini_set('memory_limit', '-1'); $user = Auth::user(); $polygonUuids = GeometryHelper::getSitePolygonsUuids($uuid)->toArray(); $job = new FixPolygonOverlapJob($polygonUuids, $user->id); @@ -29,6 +32,8 @@ public function clipOverlappingPolygonsBySite(string $uuid) public function clipOverlappingPolygonsOfProjectBySite(string $uuid) { + ini_set('max_execution_time', self::MAX_EXECUTION_TIME); + ini_set('memory_limit', '-1'); $user = Auth::user(); $sitePolygon = Site::isUuid($uuid)->first(); $projectId = $sitePolygon->project_id ?? null; @@ -42,6 +47,8 @@ public function clipOverlappingPolygonsOfProjectBySite(string $uuid) public function clipOverlappingPolygons(Request $request) { + ini_set('max_execution_time', self::MAX_EXECUTION_TIME); + ini_set('memory_limit', '-1'); $uuids = $request->input('uuids'); Log::info('Clipping polygons', ['uuids' => $uuids]); $jobUUID = null; diff --git a/app/Jobs/FixPolygonOverlapJob.php b/app/Jobs/FixPolygonOverlapJob.php index 6b35c3a33..c0ae25f69 100644 --- a/app/Jobs/FixPolygonOverlapJob.php +++ b/app/Jobs/FixPolygonOverlapJob.php @@ -82,6 +82,7 @@ public function handle(): void 'status' => self::STATUS_SUCCEEDED, 'payload' => json_encode(['updated_polygons' => $polygonsClipped]), 'updated_at' => now(), + 'statusCode' => Response::HTTP_OK, ]); } } catch (Exception $e) { From f7c79e01f51c1bcee84fe48865f4c4b318784876 Mon Sep 17 00:00:00 2001 From: JORGE Date: Thu, 24 Oct 2024 10:03:54 -0400 Subject: [PATCH 31/64] [TM-1127] change values for delayed jobs migration --- .../2024_10_03_150604_create_delayed_jobs_table.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/database/migrations/2024_10_03_150604_create_delayed_jobs_table.php b/database/migrations/2024_10_03_150604_create_delayed_jobs_table.php index 628be6933..18ee17f28 100644 --- a/database/migrations/2024_10_03_150604_create_delayed_jobs_table.php +++ b/database/migrations/2024_10_03_150604_create_delayed_jobs_table.php @@ -13,9 +13,9 @@ public function up(): void Schema::create('delayed_jobs', function (Blueprint $table) { $table->id(); $table->uuid('uuid')->unique(); - $table->enum('status', ['pending', 'failed', 'succeeded']); - $table->integer('statusCode')->nullable(); - $table->string('payload'); + $table->string('status'); + $table->integer('status_code')->nullable(); + $table->json('payload')->nullable(); $table->timestamps(); }); } From 755e666f23ffa7d1b40143eac538437b21c492b6 Mon Sep 17 00:00:00 2001 From: JORGE Date: Thu, 24 Oct 2024 10:36:18 -0400 Subject: [PATCH 32/64] [TM-1127] change values migration and remove useless functions --- app/Jobs/FixPolygonOverlapJob.php | 6 ++-- app/Jobs/InsertGeojsonToDBJob.php | 6 ++-- app/Jobs/RunSitePolygonsValidationJob.php | 4 +-- app/Models/DelayedJob.php | 36 +---------------------- 4 files changed, 8 insertions(+), 44 deletions(-) diff --git a/app/Jobs/FixPolygonOverlapJob.php b/app/Jobs/FixPolygonOverlapJob.php index c0ae25f69..2c3d6dd73 100644 --- a/app/Jobs/FixPolygonOverlapJob.php +++ b/app/Jobs/FixPolygonOverlapJob.php @@ -82,7 +82,7 @@ public function handle(): void 'status' => self::STATUS_SUCCEEDED, 'payload' => json_encode(['updated_polygons' => $polygonsClipped]), 'updated_at' => now(), - 'statusCode' => Response::HTTP_OK, + 'status_code' => Response::HTTP_OK, ]); } } catch (Exception $e) { @@ -92,7 +92,7 @@ public function handle(): void 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), 'updated_at' => now(), - 'statusCode' => Response::HTTP_INTERNAL_SERVER_ERROR, + 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); } catch (Throwable $e) { Log::error('Throwable Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); @@ -101,7 +101,7 @@ public function handle(): void 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), 'updated_at' => now(), - 'statusCode' => Response::HTTP_INTERNAL_SERVER_ERROR, + 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); } } diff --git a/app/Jobs/InsertGeojsonToDBJob.php b/app/Jobs/InsertGeojsonToDBJob.php index 59e82325e..1f2b595e4 100755 --- a/app/Jobs/InsertGeojsonToDBJob.php +++ b/app/Jobs/InsertGeojsonToDBJob.php @@ -88,8 +88,7 @@ public function handle(PolygonService $service) DelayedJob::where('uuid', $this->job_uuid)->update([ 'status' => self::STATUS_SUCCEEDED, 'payload' => json_encode($uuids), - 'updated_at' => now(), - 'statusCode' => Response::HTTP_OK, + 'status_code' => Response::HTTP_OK, ]); } catch (Exception $e) { @@ -97,8 +96,7 @@ public function handle(PolygonService $service) DelayedJob::where('uuid', $this->job_uuid)->update([ 'status' => self::STATUS_FAILED, 'payload' => ['error' => $e->getMessage()], - 'updated_at' => now(), - 'statusCode' => Response::HTTP_INTERNAL_SERVER_ERROR, + 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); } } diff --git a/app/Jobs/RunSitePolygonsValidationJob.php b/app/Jobs/RunSitePolygonsValidationJob.php index da2bce56c..e727e22e0 100644 --- a/app/Jobs/RunSitePolygonsValidationJob.php +++ b/app/Jobs/RunSitePolygonsValidationJob.php @@ -75,7 +75,7 @@ public function handle(PolygonValidationService $validationService) 'status' => self::STATUS_SUCCEEDED, 'payload' => 'Validation completed for all site polygons', 'updated_at' => now(), - 'statusCode' => Response::HTTP_OK, + 'status_code' => Response::HTTP_OK, ]); } catch (Exception $e) { @@ -85,7 +85,7 @@ public function handle(PolygonValidationService $validationService) 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), 'updated_at' => now(), - 'statusCode' => Response::HTTP_INTERNAL_SERVER_ERROR, + 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); } } diff --git a/app/Models/DelayedJob.php b/app/Models/DelayedJob.php index aab4bded5..01e062554 100644 --- a/app/Models/DelayedJob.php +++ b/app/Models/DelayedJob.php @@ -14,45 +14,11 @@ class DelayedJob extends Model protected $fillable = [ 'uuid', 'status', - 'statusCode', + 'status_code', 'payload', ]; protected $casts = [ 'uuid' => 'string', ]; - - /** - * Get the current status of the job. - * - * @return string - */ - public function getStatus(): string - { - return $this->status; - } - - /** - * Set the status of the job. - * - * @param string $status - * @return void - */ - public function setStatus(string $status): void - { - $this->status = $status; - $this->save(); - } - - /** - * Update the status code for the job. - * - * @param int|null $code - * @return void - */ - public function updateStatusCode(?int $code): void - { - $this->statusCode = $code; - $this->save(); - } } From e6c43152ed1b6a5f778e736e0eaf479f4f6de4ce Mon Sep 17 00:00:00 2001 From: JORGE Date: Thu, 24 Oct 2024 11:58:03 -0400 Subject: [PATCH 33/64] [TM-1127] remove unnecesary functinos and add resource --- .../TerrafundClipGeometryController.php | 10 ++++----- .../TerrafundCreateGeometryController.php | 15 +++++++------ app/Http/Resources/DelayedJobResource.php | 22 +++++++++++++++++++ 3 files changed, 34 insertions(+), 13 deletions(-) create mode 100644 app/Http/Resources/DelayedJobResource.php diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php index 63fd7c1d1..12c644f5c 100644 --- a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\V2\Terrafund; use App\Helpers\GeometryHelper; +use App\Http\Resources\DelayedJobResource; use App\Jobs\FixPolygonOverlapJob; use App\Models\V2\Sites\CriteriaSite; use App\Models\V2\Sites\Site; @@ -23,10 +24,9 @@ public function clipOverlappingPolygonsBySite(string $uuid) $user = Auth::user(); $polygonUuids = GeometryHelper::getSitePolygonsUuids($uuid)->toArray(); $job = new FixPolygonOverlapJob($polygonUuids, $user->id); - $jobUUID = $job->getJobUuid(); dispatch($job); - return response()->json(['job_uuid' => $jobUUID], 200); + return new DelayedJobResource($job); } @@ -39,10 +39,9 @@ public function clipOverlappingPolygonsOfProjectBySite(string $uuid) $projectId = $sitePolygon->project_id ?? null; $polygonUuids = GeometryHelper::getProjectPolygonsUuids($projectId); $job = new FixPolygonOverlapJob($polygonUuids, $user->id); - $jobUUID = $job->getJobUuid(); dispatch($job); - return response()->json(['job_uuid' => $jobUUID], 200); + return new DelayedJobResource($job); } public function clipOverlappingPolygons(Request $request) @@ -87,11 +86,10 @@ public function clipOverlappingPolygons(Request $request) if (! empty($uniquePolygonUuids)) { $user = Auth::user(); $job = new FixPolygonOverlapJob($polygonUuids, $user->id); - $jobUUID = $job->getJobUuid(); dispatch($job); } - return response()->json(['job_uuid' => $jobUUID,], 200); + return new DelayedJobResource($job); } public function clipOverlappingPolygon(string $uuid) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index 07858390c..7bc3c56ae 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -4,6 +4,7 @@ use App\Helpers\GeometryHelper; use App\Http\Controllers\Controller; +use App\Http\Resources\DelayedJobResource; use App\Jobs\InsertGeojsonToDBJob; use App\Jobs\RunSitePolygonsValidationJob; use App\Models\V2\PolygonGeometry; @@ -238,10 +239,9 @@ function ($attribute, $value, $fail) { $job = new InsertGeojsonToDBJob($filename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); } - $jobUUID = $job->getJobUuid(); dispatch($job); - return response()->json(['message' => 'KML queued to insert', 'job_uuid' => $jobUUID], 200); + return (new DelayedJobResource($job))->additional(['message' => 'KML queued to insert']); } @@ -394,7 +394,8 @@ public function uploadShapefile(Request $request) $jobUUID = $job->getJobUuid(); dispatch($job); - return response()->json(['message' => 'Shapefile queued to insert', 'job_uuid' => $jobUUID], 200); + return (new DelayedJobResource($job))->additional(['message' => 'Shapefile queued to insert']); + } else { return response()->json(['error' => 'Failed to open the ZIP file'], 400); } @@ -602,10 +603,9 @@ public function uploadGeoJSONFile(Request $request) $job = new InsertGeojsonToDBJob($filename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); } - $jobUUID = $job->getJobUuid(); dispatch($job); - return response()->json(['message' => 'Geojson queued to insert', 'job_uuid' => $jobUUID], 200); + return (new DelayedJobResource($job))->additional(['message' => 'Geojson queued to insert']); } @@ -1191,7 +1191,7 @@ public function runSiteValidationPolygon(Request $request) $jobUUID = $job->getJobUuid(); dispatch($job); - return response()->json(['message' => 'Validation completed for all site polygons', 'job_uuid' => $jobUUID], 200); + return (new DelayedJobResource($job))->additional(['message' => 'Validation completed for all site polygons']); } catch (\Exception $e) { Log::error('Error during site validation polygon: ' . $e->getMessage()); @@ -1207,7 +1207,8 @@ public function runPolygonsValidation(Request $request) $jobUUID = $job->getJobUuid(); dispatch($job); - return response()->json(['message' => 'Validation completed for these polygons', 'job_uuid' => $jobUUID], 200); + return (new DelayedJobResource($job))->additional(['message' => 'Validation completed for these polygons']); + } catch (\Exception $e) { return response()->json(['error' => 'An error occurred during validation'], 500); } diff --git a/app/Http/Resources/DelayedJobResource.php b/app/Http/Resources/DelayedJobResource.php new file mode 100644 index 000000000..46ff5a2b6 --- /dev/null +++ b/app/Http/Resources/DelayedJobResource.php @@ -0,0 +1,22 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'message' => $this->message ?? 'Job dispatched', + 'job_uuid' => $this->getJobUuid() + ]; + } +} From ca46d58e65d2877f08fba3e56dad109666cb4607 Mon Sep 17 00:00:00 2001 From: cesarLima1 <105736261+cesarLima1@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:24:36 -0400 Subject: [PATCH 34/64] [TM-1407] include framework (#519) * [TM-1407] include framework * [TM-1407] include framework --- .../Controllers/V2/Dashboard/ProjectProfileDetailsController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Http/Controllers/V2/Dashboard/ProjectProfileDetailsController.php b/app/Http/Controllers/V2/Dashboard/ProjectProfileDetailsController.php index efa258ea4..3243bad77 100644 --- a/app/Http/Controllers/V2/Dashboard/ProjectProfileDetailsController.php +++ b/app/Http/Controllers/V2/Dashboard/ProjectProfileDetailsController.php @@ -21,6 +21,7 @@ public function __invoke(Request $request, Project $project) 'restorationStrategy' => $project->restoration_strategy, 'targetLandUse' => $project->land_use_types, 'landTenure' => $project->land_tenure_project_area, + 'framework' => $project->framework_key, ]; return response()->json($response); From 7dd42ca3c273db6de92167f1c6143ccdc8b359c1 Mon Sep 17 00:00:00 2001 From: JORGE Date: Fri, 25 Oct 2024 12:27:03 -0400 Subject: [PATCH 35/64] [TM-1127] add redis to insertGeojsonToDB, and create job before running job --- .../TerrafundCreateGeometryController.php | 60 +++++++++------ app/Http/Resources/DelayedJobResource.php | 2 +- app/Jobs/FixPolygonOverlapJob.php | 3 - app/Jobs/InsertGeojsonToDBJob.php | 76 +++++++------------ app/Jobs/RunSitePolygonsValidationJob.php | 2 - app/Models/DelayedJob.php | 2 + app/Services/PolygonService.php | 24 ++++++ 7 files changed, 91 insertions(+), 78 deletions(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index 7bc3c56ae..650ed0029 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -7,6 +7,7 @@ use App\Http\Resources\DelayedJobResource; use App\Jobs\InsertGeojsonToDBJob; use App\Jobs\RunSitePolygonsValidationJob; +use App\Models\DelayedJob; use App\Models\V2\PolygonGeometry; use App\Models\V2\Sites\Site; use App\Models\V2\Sites\SitePolygon; @@ -30,6 +31,7 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Redis; use Illuminate\Support\Facades\Response; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; @@ -220,28 +222,31 @@ function ($attribute, $value, $fail) { return response()->json(['error' => 'Failed to convert KML to GeoJSON', 'message' => $process->getErrorOutput()], 500); } - + $geojsonContent = file_get_contents($geojsonPath); + $redis_key = 'kml_file_' . uniqid(); + Redis::set($redis_key, $geojsonContent, 'EX', 7200); + $polygonLoadedList = isset($body['polygon_loaded']) && filter_var($body['polygon_loaded'], FILTER_VALIDATE_BOOLEAN); $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && filter_var($body['submit_polygon_loaded'], FILTER_VALIDATE_BOOLEAN); - if (! $polygonLoadedList && ! $submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($geojsonFilename, $site_id, 'site', $body['primary_uuid'] ?? null); + $delayedJob = DelayedJob::create(['status' => InsertGeojsonToDBJob::STATUS_PENDING]); + + if (! $polygonLoadedList && !$submitPolygonsLoaded) { + $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); } if ($polygonLoadedList) { - $geojsonContent = file_get_contents($geojsonPath); - $polygonLoaded = $this->GetAllPolygonsLoaded($geojsonContent, $site_id); - - return response()->json($polygonLoaded->original, 200); + $polygonLoaded = $this->GetAllPolygonsLoaded($geojsonContent, $site_id); + return response()->json($polygonLoaded->original, 200); } if ($submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($filename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); + $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); } dispatch($job); - return (new DelayedJobResource($job))->additional(['message' => 'KML queued to insert']); + return (new DelayedJobResource($delayedJob))->additional(['message' => 'KML queued to insert']); } @@ -355,7 +360,6 @@ public function uploadShapefile(Request $request) $tempDir = sys_get_temp_dir(); $directory = $tempDir . DIRECTORY_SEPARATOR . uniqid('shapefile_'); mkdir($directory, 0755, true); - // Extract the contents of the ZIP file $zip = new \ZipArchive(); if ($zip->open($file->getPathname()) === true) { $zip->extractTo($directory); @@ -373,28 +377,29 @@ public function uploadShapefile(Request $request) return response()->json(['error' => 'Failed to convert Shapefile to GeoJSON', 'message' => $process->getErrorOutput()], 500); } + $geojsonContent = file_get_contents($geojsonPath); + $redis_key = 'shapefile_file_' . uniqid(); + Redis::set($redis_key, $geojsonContent, 'EX', 7200); + $delayedJob = DelayedJob::create(['status' => InsertGeojsonToDBJob::STATUS_PENDING]); $polygonLoadedList = isset($body['polygon_loaded']) && filter_var($body['polygon_loaded'], FILTER_VALIDATE_BOOLEAN); $submitPolygonsLoaded = $request->input('submit_polygon_loaded') === true || $request->input('submit_polygon_loaded') === 'true'; - if (! $polygonLoadedList && ! $submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($geojsonFilename, $site_id, 'site', $body['primary_uuid'] ?? null); - } + + if (! $polygonLoadedList && $submitPolygonsLoaded) { + $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); + } if ($polygonLoadedList) { - $filePath = $tempDir . DIRECTORY_SEPARATOR . $geojsonFilename; - $geojsonContent = file_get_contents($filePath); $polygonLoaded = $this->GetAllPolygonsLoaded($geojsonContent, $site_id); - return response()->json($polygonLoaded->original, 200); } if ($submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($geojsonFilename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); + $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); } - $jobUUID = $job->getJobUuid(); dispatch($job); - return (new DelayedJobResource($job))->additional(['message' => 'Shapefile queued to insert']); + return (new DelayedJobResource($delayedJob))->additional(['message' => 'Shapefile queued to insert']); } else { return response()->json(['error' => 'Failed to open the ZIP file'], 400); @@ -583,29 +588,34 @@ public function uploadGeoJSONFile(Request $request) $tempDir = sys_get_temp_dir(); $filename = uniqid('geojson_file_') . '.' . $file->getClientOriginalExtension(); $file->move($tempDir, $filename); + $filePath = $tempDir . DIRECTORY_SEPARATOR . $filename; + $geojson_content = file_get_contents($filePath); + $redis_key = 'geojson_file_' . uniqid(); + Redis::set($redis_key, $geojson_content, 'EX', 7200); $polygonLoadedList = isset($body['polygon_loaded']) && filter_var($body['polygon_loaded'], FILTER_VALIDATE_BOOLEAN); $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && filter_var($body['submit_polygon_loaded'], FILTER_VALIDATE_BOOLEAN); + $delayedJob = DelayedJob::create(['status' => InsertGeojsonToDBJob::STATUS_PENDING]); + + if (! $polygonLoadedList && ! $submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($filename, $site_id, 'site', $body['primary_uuid'] ?? null); + $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); } if ($polygonLoadedList) { - $filePath = $tempDir . DIRECTORY_SEPARATOR . $filename; - $geojsonContent = file_get_contents($filePath); - $polygonLoaded = $this->GetAllPolygonsLoaded($geojsonContent, $site_id); + $polygonLoaded = $this->GetAllPolygonsLoaded($geojson_content, $site_id); return response()->json($polygonLoaded->original, 200); } if ($submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($filename, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); + $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); } dispatch($job); - return (new DelayedJobResource($job))->additional(['message' => 'Geojson queued to insert']); + return (new DelayedJobResource($delayedJob))->additional(['message' => 'Geojson queued to insert']); } diff --git a/app/Http/Resources/DelayedJobResource.php b/app/Http/Resources/DelayedJobResource.php index 46ff5a2b6..6f362001b 100644 --- a/app/Http/Resources/DelayedJobResource.php +++ b/app/Http/Resources/DelayedJobResource.php @@ -16,7 +16,7 @@ public function toArray(Request $request): array { return [ 'message' => $this->message ?? 'Job dispatched', - 'job_uuid' => $this->getJobUuid() + 'job_uuid' => $this->uuid ]; } } diff --git a/app/Jobs/FixPolygonOverlapJob.php b/app/Jobs/FixPolygonOverlapJob.php index 2c3d6dd73..787857d05 100644 --- a/app/Jobs/FixPolygonOverlapJob.php +++ b/app/Jobs/FixPolygonOverlapJob.php @@ -81,7 +81,6 @@ public function handle(): void DelayedJob::where('uuid', $this->job_uuid)->update([ 'status' => self::STATUS_SUCCEEDED, 'payload' => json_encode(['updated_polygons' => $polygonsClipped]), - 'updated_at' => now(), 'status_code' => Response::HTTP_OK, ]); } @@ -91,7 +90,6 @@ public function handle(): void DelayedJob::where('uuid', $this->job_uuid)->update([ 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), - 'updated_at' => now(), 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); } catch (Throwable $e) { @@ -100,7 +98,6 @@ public function handle(): void DelayedJob::where('uuid', $this->job_uuid)->update([ 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), - 'updated_at' => now(), 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); } diff --git a/app/Jobs/InsertGeojsonToDBJob.php b/app/Jobs/InsertGeojsonToDBJob.php index 1f2b595e4..67b564689 100755 --- a/app/Jobs/InsertGeojsonToDBJob.php +++ b/app/Jobs/InsertGeojsonToDBJob.php @@ -15,77 +15,63 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Redis; use Illuminate\Support\Str; - class InsertGeojsonToDBJob implements ShouldQueue { - use Dispatchable; - use InteractsWithQueue; - use Queueable; - use SerializesModels; + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public $timeout = 0; - private const STATUS_PENDING = 'pending'; - private const STATUS_FAILED = 'failed'; - private const STATUS_SUCCEEDED = 'succeeded'; - - protected $geojsonFilename; + public const STATUS_PENDING = 'pending'; + public const STATUS_FAILED = 'failed'; + public const STATUS_SUCCEEDED = 'succeeded'; protected $entity_uuid; - protected $entity_type; - protected $primary_uuid; - protected $submit_polygon_loaded; + protected $redis_key; + protected $delayed_job_id; - protected $job_uuid; - - /** - * Create a new job instance. - * - * @return void - */ - public function __construct(string $geojsonFilename, ?string $entity_uuid = null, ?string $entity_type = null, ?string $primary_uuid = null, ?bool $submit_polygon_loaded = false) + public function __construct(string $redis_key, string $delayed_job_id, ?string $entity_uuid = null, ?string $entity_type = null, ?string $primary_uuid = null, ?bool $submit_polygon_loaded = false) { - $this->geojsonFilename = $geojsonFilename; + $this->redis_key = $redis_key; $this->entity_uuid = $entity_uuid; $this->entity_type = $entity_type; $this->primary_uuid = $primary_uuid; $this->submit_polygon_loaded = $submit_polygon_loaded; - $this->job_uuid = Str::uuid()->toString(); + $this->delayed_job_id = $delayed_job_id; } - /** - * Execute the job. - * - * @return void - */ public function handle(PolygonService $service) { try { - DelayedJob::create([ - 'uuid' => $this->job_uuid, - 'status' => self::STATUS_PENDING, - 'created_at' => now(), - ]); - $uuids = $service->insertGeojsonToDB( - $this->geojsonFilename, + $delayedJob = DelayedJob::findOrFail($this->delayed_job_id); + if(!$delayedJob) { + throw new \Exception('DelayedJob not found', Response::HTTP_NOT_FOUND); + } + $geojsonContent = Redis::get($this->redis_key); + if (!$geojsonContent) { + Log::error('GeoJSON content not found in Redis for key: ' . $this->redis_key); + Redis::del($this->redis_key); + return; + } + $uuids = $service->insertGeojsonToDBFromContent( + $geojsonContent, $this->entity_uuid, $this->entity_type, $this->primary_uuid, $this->submit_polygon_loaded ); - if (isset($uuids['error'])) { - $errorMessage = is_array($uuids['error']) - ? json_encode($uuids['error'], JSON_PRETTY_PRINT) - : strval($uuids['error']); - throw new \Exception($errorMessage, Response::HTTP_INTERNAL_SERVER_ERROR); + if (isset($uuids['error'])) { + throw new \Exception($uuids['error'], Response::HTTP_INTERNAL_SERVER_ERROR); } + App::make(SiteService::class)->setSiteToRestorationInProgress($this->entity_uuid); - DelayedJob::where('uuid', $this->job_uuid)->update([ + Redis::del($this->redis_key); + $delayedJob->update([ 'status' => self::STATUS_SUCCEEDED, 'payload' => json_encode($uuids), 'status_code' => Response::HTTP_OK, @@ -93,16 +79,12 @@ public function handle(PolygonService $service) } catch (Exception $e) { Log::error('Error in InsertGeojsonToDBJob: ' . $e->getMessage()); - DelayedJob::where('uuid', $this->job_uuid)->update([ + DelayedJob::where('id', $this->delayed_job_id)->update([ 'status' => self::STATUS_FAILED, 'payload' => ['error' => $e->getMessage()], 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); + Redis::del($this->redis_key); } } - - public function getJobUuid() - { - return $this->job_uuid; - } } diff --git a/app/Jobs/RunSitePolygonsValidationJob.php b/app/Jobs/RunSitePolygonsValidationJob.php index e727e22e0..2844a91ce 100644 --- a/app/Jobs/RunSitePolygonsValidationJob.php +++ b/app/Jobs/RunSitePolygonsValidationJob.php @@ -74,7 +74,6 @@ public function handle(PolygonValidationService $validationService) DelayedJob::where('uuid', $this->job_uuid)->update([ 'status' => self::STATUS_SUCCEEDED, 'payload' => 'Validation completed for all site polygons', - 'updated_at' => now(), 'status_code' => Response::HTTP_OK, ]); @@ -84,7 +83,6 @@ public function handle(PolygonValidationService $validationService) DelayedJob::where('uuid', $this->job_uuid)->update([ 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), - 'updated_at' => now(), 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); } diff --git a/app/Models/DelayedJob.php b/app/Models/DelayedJob.php index 01e062554..d180dea40 100644 --- a/app/Models/DelayedJob.php +++ b/app/Models/DelayedJob.php @@ -2,12 +2,14 @@ namespace App\Models; +use App\Models\Traits\HasUuid; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class DelayedJob extends Model { use HasFactory; + use HasUuid; protected $table = 'delayed_jobs'; diff --git a/app/Services/PolygonService.php b/app/Services/PolygonService.php index 37d1f9edf..7ee1c65ff 100755 --- a/app/Services/PolygonService.php +++ b/app/Services/PolygonService.php @@ -518,6 +518,30 @@ public function insertGeojsonToDB(string $geojsonFilename, ?string $entity_uuid } } } + /** + * @throws ValidationException + */ + public function insertGeojsonToDBFromContent(string $geojsonData, ?string $entity_uuid = null, ?string $entity_type = null, ?string $primary_uuid = null, ?bool $submit_polygon_loaded = false) + { + try { + $geojson = json_decode($geojsonData, true); + SitePolygonValidator::validate('FEATURE_BOUNDS', $geojson, false); + SitePolygonValidator::validate('GEOMETRY_TYPE', $geojson, false); + Log::info('data in function', ['entity_uuid' => $entity_uuid, 'entity_type' => $entity_type, 'primary_uuid' => $primary_uuid, 'submit_polygon_loaded' => $submit_polygon_loaded]); + return $this->createGeojsonModels($geojson, ['site_id' => $entity_uuid, 'source' => PolygonService::UPLOADED_SOURCE], $primary_uuid, $submit_polygon_loaded); + + } catch (Exception $e) { + $errorMessage = $e->getMessage(); + $decodedErrorMessage = json_decode($errorMessage, true); + if (json_last_error() === JSON_ERROR_NONE) { + return ['error' => $decodedErrorMessage]; + } else { + Log::info('Error inserting geojson to DB', ['error' => $errorMessage]); + + return ['error' => $errorMessage]; + } + } + } public function processClippedPolygons(array $polygonUuids) { From c64aad6dde700b50b8d3f4932e78a37720a36e73 Mon Sep 17 00:00:00 2001 From: JORGE Date: Fri, 25 Oct 2024 14:59:42 -0400 Subject: [PATCH 36/64] [TM-1127] create job before worker --- .../TerrafundClipGeometryController.php | 17 +++++---- .../TerrafundCreateGeometryController.php | 12 +++---- app/Jobs/FixPolygonOverlapJob.php | 35 +++++++------------ app/Jobs/InsertGeojsonToDBJob.php | 3 -- app/Jobs/RunSitePolygonsValidationJob.php | 27 +++++--------- app/Services/PolygonService.php | 1 - 6 files changed, 38 insertions(+), 57 deletions(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php index 12c644f5c..4f2b3ab9c 100644 --- a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php @@ -5,6 +5,7 @@ use App\Helpers\GeometryHelper; use App\Http\Resources\DelayedJobResource; use App\Jobs\FixPolygonOverlapJob; +use App\Models\DelayedJob; use App\Models\V2\Sites\CriteriaSite; use App\Models\V2\Sites\Site; use App\Models\V2\Sites\SitePolygon; @@ -23,10 +24,11 @@ public function clipOverlappingPolygonsBySite(string $uuid) ini_set('memory_limit', '-1'); $user = Auth::user(); $polygonUuids = GeometryHelper::getSitePolygonsUuids($uuid)->toArray(); - $job = new FixPolygonOverlapJob($polygonUuids, $user->id); + $delayedJob = DelayedJob::create(['status' => FixPolygonOverlapJob::STATUS_PENDING]); + $job = new FixPolygonOverlapJob($delayedJob->id, $polygonUuids, $user->id); dispatch($job); - return new DelayedJobResource($job); + return new DelayedJobResource($delayedJob); } @@ -38,10 +40,11 @@ public function clipOverlappingPolygonsOfProjectBySite(string $uuid) $sitePolygon = Site::isUuid($uuid)->first(); $projectId = $sitePolygon->project_id ?? null; $polygonUuids = GeometryHelper::getProjectPolygonsUuids($projectId); - $job = new FixPolygonOverlapJob($polygonUuids, $user->id); + $delayedJob = DelayedJob::create(['status' => FixPolygonOverlapJob::STATUS_PENDING]); + $job = new FixPolygonOverlapJob($delayedJob->id, $polygonUuids, $user->id); dispatch($job); - return new DelayedJobResource($job); + return new DelayedJobResource($delayedJob); } public function clipOverlappingPolygons(Request $request) @@ -50,7 +53,6 @@ public function clipOverlappingPolygons(Request $request) ini_set('memory_limit', '-1'); $uuids = $request->input('uuids'); Log::info('Clipping polygons', ['uuids' => $uuids]); - $jobUUID = null; if (empty($uuids) || ! is_array($uuids)) { return response()->json(['error' => 'Invalid or missing UUIDs'], 400); } @@ -85,11 +87,12 @@ public function clipOverlappingPolygons(Request $request) $uniquePolygonUuids = array_unique($allPolygonUuids); if (! empty($uniquePolygonUuids)) { $user = Auth::user(); - $job = new FixPolygonOverlapJob($polygonUuids, $user->id); + $delayedJob = DelayedJob::create(['status' => FixPolygonOverlapJob::STATUS_PENDING]); + $job = new FixPolygonOverlapJob($delayedJob->id, $polygonUuids, $user->id); dispatch($job); } - return new DelayedJobResource($job); + return new DelayedJobResource($delayedJob); } public function clipOverlappingPolygon(string $uuid) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index 650ed0029..7d6f60f08 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -1197,11 +1197,11 @@ public function runSiteValidationPolygon(Request $request) $uuid = $request->input('uuid'); $sitePolygonsUuids = GeometryHelper::getSitePolygonsUuids($uuid)->toArray(); - $job = new RunSitePolygonsValidationJob($sitePolygonsUuids); - $jobUUID = $job->getJobUuid(); + $delayedJob = DelayedJob::create(['status' => RunSitePolygonsValidationJob::STATUS_PENDING]); + $job = new RunSitePolygonsValidationJob($delayedJob->id, $sitePolygonsUuids); dispatch($job); - return (new DelayedJobResource($job))->additional(['message' => 'Validation completed for all site polygons']); + return (new DelayedJobResource($delayedJob))->additional(['message' => 'Validation completed for all site polygons']); } catch (\Exception $e) { Log::error('Error during site validation polygon: ' . $e->getMessage()); @@ -1213,11 +1213,11 @@ public function runPolygonsValidation(Request $request) { try { $uuids = $request->input('uuids'); - $job = new RunSitePolygonsValidationJob($uuids); - $jobUUID = $job->getJobUuid(); + $delayedJob = DelayedJob::create(['status' => RunSitePolygonsValidationJob::STATUS_PENDING]); + $job = new RunSitePolygonsValidationJob($delayedJob->id, $uuids); dispatch($job); - return (new DelayedJobResource($job))->additional(['message' => 'Validation completed for these polygons']); + return (new DelayedJobResource($delayedJob))->additional(['message' => 'Validation completed for these polygons']); } catch (\Exception $e) { return response()->json(['error' => 'An error occurred during validation'], 500); diff --git a/app/Jobs/FixPolygonOverlapJob.php b/app/Jobs/FixPolygonOverlapJob.php index 787857d05..7e613b788 100644 --- a/app/Jobs/FixPolygonOverlapJob.php +++ b/app/Jobs/FixPolygonOverlapJob.php @@ -26,31 +26,30 @@ class FixPolygonOverlapJob implements ShouldQueue use SerializesModels; - private const STATUS_PENDING = 'pending'; - private const STATUS_FAILED = 'failed'; - private const STATUS_SUCCEEDED = 'succeeded'; + public const STATUS_PENDING = 'pending'; + public const STATUS_FAILED = 'failed'; + public const STATUS_SUCCEEDED = 'succeeded'; public $timeout = 0; protected $polygonService; protected $polygonUuids; - - protected $job_uuid; - + public $authUserId; + protected $delayed_job_id; + /** * Create a new job instance. * * @param array $polygonUuids */ - public function __construct(array $polygonUuids, int $authUserId) + public function __construct(string $delayed_job_id, array $polygonUuids, int $authUserId) { $this->polygonUuids = $polygonUuids; - $this->job_uuid = Str::uuid()->toString(); $this->authUserId = $authUserId; - + $this->delayed_job_id = $delayed_job_id; } /** @@ -70,24 +69,20 @@ public function handle(): void { try { - DelayedJob::create([ - 'uuid' => $this->job_uuid, - 'status' => self::STATUS_PENDING, - 'created_at' => now(), - ]); + $delayedJob = DelayedJob::findOrFail($this->delayed_job_id); $user = Auth::user(); if ($user) { $polygonsClipped = App::make(PolygonService::class)->processClippedPolygons($this->polygonUuids); - DelayedJob::where('uuid', $this->job_uuid)->update([ + $delayedJob->update([ 'status' => self::STATUS_SUCCEEDED, 'payload' => json_encode(['updated_polygons' => $polygonsClipped]), - 'status_code' => Response::HTTP_OK, + 'status_code' => Response::HTTP_OK ]); } } catch (Exception $e) { Log::error('Error in Fix Polygon Overlap Job: ' . $e->getMessage()); - DelayedJob::where('uuid', $this->job_uuid)->update([ + DelayedJob::where('uuid', $this->delayed_job_id)->update([ 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, @@ -95,7 +90,7 @@ public function handle(): void } catch (Throwable $e) { Log::error('Throwable Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); - DelayedJob::where('uuid', $this->job_uuid)->update([ + DelayedJob::where('uuid', $this->delayed_job_id)->update([ 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, @@ -103,8 +98,4 @@ public function handle(): void } } - public function getJobUuid() - { - return $this->job_uuid; - } } diff --git a/app/Jobs/InsertGeojsonToDBJob.php b/app/Jobs/InsertGeojsonToDBJob.php index 67b564689..1fbf1f988 100755 --- a/app/Jobs/InsertGeojsonToDBJob.php +++ b/app/Jobs/InsertGeojsonToDBJob.php @@ -48,9 +48,6 @@ public function handle(PolygonService $service) { try { $delayedJob = DelayedJob::findOrFail($this->delayed_job_id); - if(!$delayedJob) { - throw new \Exception('DelayedJob not found', Response::HTTP_NOT_FOUND); - } $geojsonContent = Redis::get($this->redis_key); if (!$geojsonContent) { Log::error('GeoJSON content not found in Redis for key: ' . $this->redis_key); diff --git a/app/Jobs/RunSitePolygonsValidationJob.php b/app/Jobs/RunSitePolygonsValidationJob.php index 2844a91ce..0a57a2b93 100644 --- a/app/Jobs/RunSitePolygonsValidationJob.php +++ b/app/Jobs/RunSitePolygonsValidationJob.php @@ -24,13 +24,13 @@ class RunSitePolygonsValidationJob implements ShouldQueue public $timeout = 0; - private const STATUS_PENDING = 'pending'; - private const STATUS_FAILED = 'failed'; - private const STATUS_SUCCEEDED = 'succeeded'; + public const STATUS_PENDING = 'pending'; + public const STATUS_FAILED = 'failed'; + public const STATUS_SUCCEEDED = 'succeeded'; protected $uuid; - protected $job_uuid; + protected $delayed_job_id; protected $sitePolygonsUuids; @@ -39,10 +39,10 @@ class RunSitePolygonsValidationJob implements ShouldQueue * * @return void */ - public function __construct(array $sitePolygonsUuids) + public function __construct(string $delayed_job_id, array $sitePolygonsUuids) { $this->sitePolygonsUuids = $sitePolygonsUuids; - $this->job_uuid = Str::uuid()->toString(); + $this->delayed_job_id = $delayed_job_id; } /** @@ -53,11 +53,7 @@ public function __construct(array $sitePolygonsUuids) public function handle(PolygonValidationService $validationService) { try { - DelayedJob::create([ - 'uuid' => $this->job_uuid, - 'status' => self::STATUS_PENDING, - 'created_at' => now(), - ]); + $delayedJob = DelayedJob::findOrFail($this->delayed_job_id); foreach ($this->sitePolygonsUuids as $polygonUuid) { $request = new Request(['uuid' => $polygonUuid]); $validationService->validateOverlapping($request); @@ -71,7 +67,7 @@ public function handle(PolygonValidationService $validationService) $validationService->validateDataInDB($request); } - DelayedJob::where('uuid', $this->job_uuid)->update([ + $delayedJob->update([ 'status' => self::STATUS_SUCCEEDED, 'payload' => 'Validation completed for all site polygons', 'status_code' => Response::HTTP_OK, @@ -80,16 +76,11 @@ public function handle(PolygonValidationService $validationService) } catch (Exception $e) { Log::error('Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); - DelayedJob::where('uuid', $this->job_uuid)->update([ + DelayedJob::where('id', $this->delayed_job_id)->update([ 'status' => self::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); } } - - public function getJobUuid() - { - return $this->job_uuid; - } } diff --git a/app/Services/PolygonService.php b/app/Services/PolygonService.php index 7ee1c65ff..b90e69fdb 100755 --- a/app/Services/PolygonService.php +++ b/app/Services/PolygonService.php @@ -527,7 +527,6 @@ public function insertGeojsonToDBFromContent(string $geojsonData, ?string $entit $geojson = json_decode($geojsonData, true); SitePolygonValidator::validate('FEATURE_BOUNDS', $geojson, false); SitePolygonValidator::validate('GEOMETRY_TYPE', $geojson, false); - Log::info('data in function', ['entity_uuid' => $entity_uuid, 'entity_type' => $entity_type, 'primary_uuid' => $primary_uuid, 'submit_polygon_loaded' => $submit_polygon_loaded]); return $this->createGeojsonModels($geojson, ['site_id' => $entity_uuid, 'source' => PolygonService::UPLOADED_SOURCE], $primary_uuid, $submit_polygon_loaded); } catch (Exception $e) { From 39d674828d58e05aa4cc7c397aa40a4fc2f6e5b0 Mon Sep 17 00:00:00 2001 From: JORGE Date: Fri, 25 Oct 2024 15:23:06 -0400 Subject: [PATCH 37/64] [TM-1127] fix difference in sending --- .../V2/Terrafund/TerrafundCreateGeometryController.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index 7d6f60f08..520521801 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -232,7 +232,7 @@ function ($attribute, $value, $fail) { $delayedJob = DelayedJob::create(['status' => InsertGeojsonToDBJob::STATUS_PENDING]); if (! $polygonLoadedList && !$submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); + $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, null, $body['primary_uuid'] ?? null); } if ($polygonLoadedList) { @@ -386,7 +386,7 @@ public function uploadShapefile(Request $request) $submitPolygonsLoaded = $request->input('submit_polygon_loaded') === true || $request->input('submit_polygon_loaded') === 'true'; if (! $polygonLoadedList && $submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); + $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null); } if ($polygonLoadedList) { @@ -600,7 +600,7 @@ public function uploadGeoJSONFile(Request $request) if (! $polygonLoadedList && ! $submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); + $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null); } if ($polygonLoadedList) { @@ -610,7 +610,7 @@ public function uploadGeoJSONFile(Request $request) } if ($submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); + $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); } dispatch($job); From 14dc3f4902c84584193a7b707b5230e69e1ce67c Mon Sep 17 00:00:00 2001 From: JORGE Date: Fri, 25 Oct 2024 15:31:48 -0400 Subject: [PATCH 38/64] [TM-1127] reorder upload functions to prevent useless creations of jobs --- .../TerrafundCreateGeometryController.php | 96 +++++++++---------- 1 file changed, 45 insertions(+), 51 deletions(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index 520521801..15d7f0942 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -223,30 +223,33 @@ function ($attribute, $value, $fail) { return response()->json(['error' => 'Failed to convert KML to GeoJSON', 'message' => $process->getErrorOutput()], 500); } $geojsonContent = file_get_contents($geojsonPath); - $redis_key = 'kml_file_' . uniqid(); - Redis::set($redis_key, $geojsonContent, 'EX', 7200); + $polygonLoadedList = isset($body['polygon_loaded']) && + filter_var($body['polygon_loaded'], FILTER_VALIDATE_BOOLEAN); + $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && + filter_var($body['submit_polygon_loaded'], FILTER_VALIDATE_BOOLEAN); - $polygonLoadedList = isset($body['polygon_loaded']) && filter_var($body['polygon_loaded'], FILTER_VALIDATE_BOOLEAN); - $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && filter_var($body['submit_polygon_loaded'], FILTER_VALIDATE_BOOLEAN); - - $delayedJob = DelayedJob::create(['status' => InsertGeojsonToDBJob::STATUS_PENDING]); - - if (! $polygonLoadedList && !$submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, null, $body['primary_uuid'] ?? null); - } - if ($polygonLoadedList) { - $polygonLoaded = $this->GetAllPolygonsLoaded($geojsonContent, $site_id); - return response()->json($polygonLoaded->original, 200); - } - - if ($submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); + $polygonLoaded = $this->GetAllPolygonsLoaded($geojsonContent, $site_id); + return response()->json($polygonLoaded->original, 200); } + $redis_key = 'kml_file_' . uniqid(); + Redis::set($redis_key, $geojsonContent, 'EX', 7200); + $delayedJob = DelayedJob::create(['status' => InsertGeojsonToDBJob::STATUS_PENDING]); + + $job = new InsertGeojsonToDBJob( + $redis_key, + $delayedJob->id, + $site_id, + 'site', + $body['primary_uuid'] ?? null, + $submitPolygonsLoaded + ); + dispatch($job); - - return (new DelayedJobResource($delayedJob))->additional(['message' => 'KML queued to insert']); + + return (new DelayedJobResource($delayedJob)) + ->additional(['message' => 'KML queued to insert']); } @@ -378,25 +381,21 @@ public function uploadShapefile(Request $request) return response()->json(['error' => 'Failed to convert Shapefile to GeoJSON', 'message' => $process->getErrorOutput()], 500); } $geojsonContent = file_get_contents($geojsonPath); - $redis_key = 'shapefile_file_' . uniqid(); - Redis::set($redis_key, $geojsonContent, 'EX', 7200); - $delayedJob = DelayedJob::create(['status' => InsertGeojsonToDBJob::STATUS_PENDING]); - - $polygonLoadedList = isset($body['polygon_loaded']) && filter_var($body['polygon_loaded'], FILTER_VALIDATE_BOOLEAN); - $submitPolygonsLoaded = $request->input('submit_polygon_loaded') === true || $request->input('submit_polygon_loaded') === 'true'; - - if (! $polygonLoadedList && $submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null); - } - + $polygonLoadedList = isset($body['polygon_loaded']) && + filter_var($body['polygon_loaded'], FILTER_VALIDATE_BOOLEAN); + $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && + filter_var($body['submit_polygon_loaded'], FILTER_VALIDATE_BOOLEAN); + if ($polygonLoadedList) { $polygonLoaded = $this->GetAllPolygonsLoaded($geojsonContent, $site_id); return response()->json($polygonLoaded->original, 200); } - if ($submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); - } + $redis_key = 'shapefile_file_' . uniqid(); + Redis::set($redis_key, $geojsonContent, 'EX', 7200); + $delayedJob = DelayedJob::create(['status' => InsertGeojsonToDBJob::STATUS_PENDING]); + $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); + dispatch($job); return (new DelayedJobResource($delayedJob))->additional(['message' => 'Shapefile queued to insert']); @@ -590,28 +589,23 @@ public function uploadGeoJSONFile(Request $request) $file->move($tempDir, $filename); $filePath = $tempDir . DIRECTORY_SEPARATOR . $filename; $geojson_content = file_get_contents($filePath); - $redis_key = 'geojson_file_' . uniqid(); - Redis::set($redis_key, $geojson_content, 'EX', 7200); - - $polygonLoadedList = isset($body['polygon_loaded']) && filter_var($body['polygon_loaded'], FILTER_VALIDATE_BOOLEAN); - $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && filter_var($body['submit_polygon_loaded'], FILTER_VALIDATE_BOOLEAN); - - $delayedJob = DelayedJob::create(['status' => InsertGeojsonToDBJob::STATUS_PENDING]); - - - if (! $polygonLoadedList && ! $submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null); - } + $polygonLoadedList = isset($body['polygon_loaded']) && + filter_var($body['polygon_loaded'], FILTER_VALIDATE_BOOLEAN); + $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && + filter_var($body['submit_polygon_loaded'], FILTER_VALIDATE_BOOLEAN); if ($polygonLoadedList) { - $polygonLoaded = $this->GetAllPolygonsLoaded($geojson_content, $site_id); - - return response()->json($polygonLoaded->original, 200); + $polygonLoaded = $this->GetAllPolygonsLoaded($geojson_content, $site_id); + + return response()->json($polygonLoaded->original, 200); } + + + $redis_key = 'geojson_file_' . uniqid(); + Redis::set($redis_key, $geojson_content, 'EX', 7200); + $delayedJob = DelayedJob::create(['status' => InsertGeojsonToDBJob::STATUS_PENDING]); - if ($submitPolygonsLoaded) { - $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); - } + $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $submitPolygonsLoaded); dispatch($job); From 2eab851d8895d7fa46f5b6d11543e3d82854be10 Mon Sep 17 00:00:00 2001 From: JORGE Date: Fri, 25 Oct 2024 15:37:23 -0400 Subject: [PATCH 39/64] [TM-1127] ove statuses values to Model --- .../V2/Terrafund/TerrafundClipGeometryController.php | 6 +++--- .../Terrafund/TerrafundCreateGeometryController.php | 12 ++++++------ app/Jobs/FixPolygonOverlapJob.php | 11 +++-------- app/Jobs/InsertGeojsonToDBJob.php | 9 ++------- app/Jobs/RunSitePolygonsValidationJob.php | 8 ++------ app/Models/DelayedJob.php | 5 +++++ 6 files changed, 21 insertions(+), 30 deletions(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php index 4f2b3ab9c..466c7392f 100644 --- a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php @@ -24,7 +24,7 @@ public function clipOverlappingPolygonsBySite(string $uuid) ini_set('memory_limit', '-1'); $user = Auth::user(); $polygonUuids = GeometryHelper::getSitePolygonsUuids($uuid)->toArray(); - $delayedJob = DelayedJob::create(['status' => FixPolygonOverlapJob::STATUS_PENDING]); + $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); $job = new FixPolygonOverlapJob($delayedJob->id, $polygonUuids, $user->id); dispatch($job); @@ -40,7 +40,7 @@ public function clipOverlappingPolygonsOfProjectBySite(string $uuid) $sitePolygon = Site::isUuid($uuid)->first(); $projectId = $sitePolygon->project_id ?? null; $polygonUuids = GeometryHelper::getProjectPolygonsUuids($projectId); - $delayedJob = DelayedJob::create(['status' => FixPolygonOverlapJob::STATUS_PENDING]); + $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); $job = new FixPolygonOverlapJob($delayedJob->id, $polygonUuids, $user->id); dispatch($job); @@ -87,7 +87,7 @@ public function clipOverlappingPolygons(Request $request) $uniquePolygonUuids = array_unique($allPolygonUuids); if (! empty($uniquePolygonUuids)) { $user = Auth::user(); - $delayedJob = DelayedJob::create(['status' => FixPolygonOverlapJob::STATUS_PENDING]); + $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); $job = new FixPolygonOverlapJob($delayedJob->id, $polygonUuids, $user->id); dispatch($job); } diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index 15d7f0942..45704b1e7 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -235,7 +235,7 @@ function ($attribute, $value, $fail) { $redis_key = 'kml_file_' . uniqid(); Redis::set($redis_key, $geojsonContent, 'EX', 7200); - $delayedJob = DelayedJob::create(['status' => InsertGeojsonToDBJob::STATUS_PENDING]); + $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); $job = new InsertGeojsonToDBJob( $redis_key, @@ -393,8 +393,8 @@ public function uploadShapefile(Request $request) $redis_key = 'shapefile_file_' . uniqid(); Redis::set($redis_key, $geojsonContent, 'EX', 7200); - $delayedJob = DelayedJob::create(['status' => InsertGeojsonToDBJob::STATUS_PENDING]); - $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $body['submit_polygon_loaded']); + $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); + $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $submitPolygonsLoaded); dispatch($job); @@ -603,7 +603,7 @@ public function uploadGeoJSONFile(Request $request) $redis_key = 'geojson_file_' . uniqid(); Redis::set($redis_key, $geojson_content, 'EX', 7200); - $delayedJob = DelayedJob::create(['status' => InsertGeojsonToDBJob::STATUS_PENDING]); + $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $submitPolygonsLoaded); @@ -1191,7 +1191,7 @@ public function runSiteValidationPolygon(Request $request) $uuid = $request->input('uuid'); $sitePolygonsUuids = GeometryHelper::getSitePolygonsUuids($uuid)->toArray(); - $delayedJob = DelayedJob::create(['status' => RunSitePolygonsValidationJob::STATUS_PENDING]); + $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); $job = new RunSitePolygonsValidationJob($delayedJob->id, $sitePolygonsUuids); dispatch($job); @@ -1207,7 +1207,7 @@ public function runPolygonsValidation(Request $request) { try { $uuids = $request->input('uuids'); - $delayedJob = DelayedJob::create(['status' => RunSitePolygonsValidationJob::STATUS_PENDING]); + $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); $job = new RunSitePolygonsValidationJob($delayedJob->id, $uuids); dispatch($job); diff --git a/app/Jobs/FixPolygonOverlapJob.php b/app/Jobs/FixPolygonOverlapJob.php index 7e613b788..1ed7ebebf 100644 --- a/app/Jobs/FixPolygonOverlapJob.php +++ b/app/Jobs/FixPolygonOverlapJob.php @@ -25,11 +25,6 @@ class FixPolygonOverlapJob implements ShouldQueue use Queueable; use SerializesModels; - - public const STATUS_PENDING = 'pending'; - public const STATUS_FAILED = 'failed'; - public const STATUS_SUCCEEDED = 'succeeded'; - public $timeout = 0; protected $polygonService; @@ -74,7 +69,7 @@ public function handle(): void if ($user) { $polygonsClipped = App::make(PolygonService::class)->processClippedPolygons($this->polygonUuids); $delayedJob->update([ - 'status' => self::STATUS_SUCCEEDED, + 'status' => DelayedJob::STATUS_SUCCEEDED, 'payload' => json_encode(['updated_polygons' => $polygonsClipped]), 'status_code' => Response::HTTP_OK ]); @@ -83,7 +78,7 @@ public function handle(): void Log::error('Error in Fix Polygon Overlap Job: ' . $e->getMessage()); DelayedJob::where('uuid', $this->delayed_job_id)->update([ - 'status' => self::STATUS_FAILED, + 'status' => DelayedJob::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); @@ -91,7 +86,7 @@ public function handle(): void Log::error('Throwable Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); DelayedJob::where('uuid', $this->delayed_job_id)->update([ - 'status' => self::STATUS_FAILED, + 'status' => DelayedJob::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); diff --git a/app/Jobs/InsertGeojsonToDBJob.php b/app/Jobs/InsertGeojsonToDBJob.php index 1fbf1f988..13bac2ad7 100755 --- a/app/Jobs/InsertGeojsonToDBJob.php +++ b/app/Jobs/InsertGeojsonToDBJob.php @@ -22,11 +22,6 @@ class InsertGeojsonToDBJob implements ShouldQueue use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public $timeout = 0; - - public const STATUS_PENDING = 'pending'; - public const STATUS_FAILED = 'failed'; - public const STATUS_SUCCEEDED = 'succeeded'; - protected $entity_uuid; protected $entity_type; protected $primary_uuid; @@ -69,7 +64,7 @@ public function handle(PolygonService $service) App::make(SiteService::class)->setSiteToRestorationInProgress($this->entity_uuid); Redis::del($this->redis_key); $delayedJob->update([ - 'status' => self::STATUS_SUCCEEDED, + 'status' => DelayedJob::STATUS_SUCCEEDED, 'payload' => json_encode($uuids), 'status_code' => Response::HTTP_OK, ]); @@ -77,7 +72,7 @@ public function handle(PolygonService $service) } catch (Exception $e) { Log::error('Error in InsertGeojsonToDBJob: ' . $e->getMessage()); DelayedJob::where('id', $this->delayed_job_id)->update([ - 'status' => self::STATUS_FAILED, + 'status' => DelayedJob::STATUS_FAILED, 'payload' => ['error' => $e->getMessage()], 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); diff --git a/app/Jobs/RunSitePolygonsValidationJob.php b/app/Jobs/RunSitePolygonsValidationJob.php index 0a57a2b93..fd019d7f1 100644 --- a/app/Jobs/RunSitePolygonsValidationJob.php +++ b/app/Jobs/RunSitePolygonsValidationJob.php @@ -23,10 +23,6 @@ class RunSitePolygonsValidationJob implements ShouldQueue use SerializesModels; public $timeout = 0; - - public const STATUS_PENDING = 'pending'; - public const STATUS_FAILED = 'failed'; - public const STATUS_SUCCEEDED = 'succeeded'; protected $uuid; @@ -68,7 +64,7 @@ public function handle(PolygonValidationService $validationService) } $delayedJob->update([ - 'status' => self::STATUS_SUCCEEDED, + 'status' => DelayedJob::STATUS_SUCCEEDED, 'payload' => 'Validation completed for all site polygons', 'status_code' => Response::HTTP_OK, ]); @@ -77,7 +73,7 @@ public function handle(PolygonValidationService $validationService) Log::error('Error in RunSitePolygonsValidationJob: ' . $e->getMessage()); DelayedJob::where('id', $this->delayed_job_id)->update([ - 'status' => self::STATUS_FAILED, + 'status' => DelayedJob::STATUS_FAILED, 'payload' => json_encode(['error' => $e->getMessage()]), 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); diff --git a/app/Models/DelayedJob.php b/app/Models/DelayedJob.php index d180dea40..21d1151f7 100644 --- a/app/Models/DelayedJob.php +++ b/app/Models/DelayedJob.php @@ -11,6 +11,11 @@ class DelayedJob extends Model use HasFactory; use HasUuid; + public const STATUS_PENDING = 'pending'; + public const STATUS_FAILED = 'failed'; + public const STATUS_SUCCEEDED = 'succeeded'; + + protected $table = 'delayed_jobs'; protected $fillable = [ From fdf7372ba3a271e7075bcac331a7499d54e0a003 Mon Sep 17 00:00:00 2001 From: JORGE Date: Fri, 25 Oct 2024 15:40:47 -0400 Subject: [PATCH 40/64] [TM-1127] move redis delete to finally --- app/Jobs/InsertGeojsonToDBJob.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Jobs/InsertGeojsonToDBJob.php b/app/Jobs/InsertGeojsonToDBJob.php index 13bac2ad7..da527c9d4 100755 --- a/app/Jobs/InsertGeojsonToDBJob.php +++ b/app/Jobs/InsertGeojsonToDBJob.php @@ -38,17 +38,17 @@ public function __construct(string $redis_key, string $delayed_job_id, ?string $ $this->submit_polygon_loaded = $submit_polygon_loaded; $this->delayed_job_id = $delayed_job_id; } - public function handle(PolygonService $service) { try { $delayedJob = DelayedJob::findOrFail($this->delayed_job_id); $geojsonContent = Redis::get($this->redis_key); + if (!$geojsonContent) { Log::error('GeoJSON content not found in Redis for key: ' . $this->redis_key); - Redis::del($this->redis_key); return; } + $uuids = $service->insertGeojsonToDBFromContent( $geojsonContent, $this->entity_uuid, @@ -62,7 +62,7 @@ public function handle(PolygonService $service) } App::make(SiteService::class)->setSiteToRestorationInProgress($this->entity_uuid); - Redis::del($this->redis_key); + $delayedJob->update([ 'status' => DelayedJob::STATUS_SUCCEEDED, 'payload' => json_encode($uuids), @@ -76,6 +76,7 @@ public function handle(PolygonService $service) 'payload' => ['error' => $e->getMessage()], 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, ]); + } finally { Redis::del($this->redis_key); } } From 203e56fd42675c68351c669b802dd70a63f2ee11 Mon Sep 17 00:00:00 2001 From: JORGE Date: Fri, 25 Oct 2024 15:54:04 -0400 Subject: [PATCH 41/64] [TM-1127] ensure that redis is deleted after fail or success --- app/Jobs/InsertGeojsonToDBJob.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/Jobs/InsertGeojsonToDBJob.php b/app/Jobs/InsertGeojsonToDBJob.php index da527c9d4..5e4dc05ec 100755 --- a/app/Jobs/InsertGeojsonToDBJob.php +++ b/app/Jobs/InsertGeojsonToDBJob.php @@ -40,15 +40,21 @@ public function __construct(string $redis_key, string $delayed_job_id, ?string $ } public function handle(PolygonService $service) { + $delayedJob = DelayedJob::findOrFail($this->delayed_job_id); + try { - $delayedJob = DelayedJob::findOrFail($this->delayed_job_id); $geojsonContent = Redis::get($this->redis_key); if (!$geojsonContent) { Log::error('GeoJSON content not found in Redis for key: ' . $this->redis_key); + $delayedJob->update([ + 'status' => DelayedJob::STATUS_FAILED, + 'payload' => ['error' => 'GeoJSON content not found in Redis'], + 'status_code' => Response::HTTP_NOT_FOUND, + ]); return; } - + $uuids = $service->insertGeojsonToDBFromContent( $geojsonContent, $this->entity_uuid, @@ -56,11 +62,11 @@ public function handle(PolygonService $service) $this->primary_uuid, $this->submit_polygon_loaded ); - + if (isset($uuids['error'])) { throw new \Exception($uuids['error'], Response::HTTP_INTERNAL_SERVER_ERROR); } - + App::make(SiteService::class)->setSiteToRestorationInProgress($this->entity_uuid); $delayedJob->update([ @@ -68,10 +74,10 @@ public function handle(PolygonService $service) 'payload' => json_encode($uuids), 'status_code' => Response::HTTP_OK, ]); - + } catch (Exception $e) { Log::error('Error in InsertGeojsonToDBJob: ' . $e->getMessage()); - DelayedJob::where('id', $this->delayed_job_id)->update([ + $delayedJob->update([ 'status' => DelayedJob::STATUS_FAILED, 'payload' => ['error' => $e->getMessage()], 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, From 4c91aec96cc4623ece92ec18d0d9983ac48ac409 Mon Sep 17 00:00:00 2001 From: JORGE Date: Fri, 25 Oct 2024 16:05:15 -0400 Subject: [PATCH 42/64] [TM-1127] create worker only if job has been created --- .../TerrafundCreateGeometryController.php | 60 ++++++++++++++----- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index 45704b1e7..fbb9df078 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -237,19 +237,24 @@ function ($attribute, $value, $fail) { Redis::set($redis_key, $geojsonContent, 'EX', 7200); $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); - $job = new InsertGeojsonToDBJob( + if($delayedJob) { + $job = new InsertGeojsonToDBJob( $redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $submitPolygonsLoaded - ); + ); - dispatch($job); - - return (new DelayedJobResource($delayedJob)) - ->additional(['message' => 'KML queued to insert']); + dispatch($job); + + return (new DelayedJobResource($delayedJob)) + ->additional(['message' => 'KML queued to insert']); + } else { + return response()->json(['error' => 'Failed to create delayed job'], 500); + } + } @@ -394,11 +399,24 @@ public function uploadShapefile(Request $request) $redis_key = 'shapefile_file_' . uniqid(); Redis::set($redis_key, $geojsonContent, 'EX', 7200); $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); - $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $submitPolygonsLoaded); - - dispatch($job); - return (new DelayedJobResource($delayedJob))->additional(['message' => 'Shapefile queued to insert']); + if($delayedJob) { + $job = new InsertGeojsonToDBJob( + $redis_key, + $delayedJob->id, + $site_id, + 'site', + $body['primary_uuid'] ?? null, + $submitPolygonsLoaded + ); + + dispatch($job); + + return (new DelayedJobResource($delayedJob)) + ->additional(['message' => 'Shapefile queued to insert']); + } else { + return response()->json(['error' => 'Failed to create delayed job'], 500); + } } else { return response()->json(['error' => 'Failed to open the ZIP file'], 400); @@ -605,11 +623,23 @@ public function uploadGeoJSONFile(Request $request) Redis::set($redis_key, $geojson_content, 'EX', 7200); $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); - $job = new InsertGeojsonToDBJob($redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $submitPolygonsLoaded); - - dispatch($job); - - return (new DelayedJobResource($delayedJob))->additional(['message' => 'Geojson queued to insert']); + if($delayedJob) { + $job = new InsertGeojsonToDBJob( + $redis_key, + $delayedJob->id, + $site_id, + 'site', + $body['primary_uuid'] ?? null, + $submitPolygonsLoaded + ); + + dispatch($job); + + return (new DelayedJobResource($delayedJob)) + ->additional(['message' => 'Geojson queued to insert']); + } else { + return response()->json(['error' => 'Failed to create delayed job'], 500); + } } From f61c6a5db1b4915314022dc99bdd25c2ff0e4e3d Mon Sep 17 00:00:00 2001 From: JORGE Date: Fri, 25 Oct 2024 16:39:30 -0400 Subject: [PATCH 43/64] [TM-1127] add definition to delayedjobresponse --- .../V2/definitions/DelayedJobsResponse.yml | 11 ++ openapi-src/V2/definitions/_index.yml | 4 +- ...t-v2-terrafund-validation-sitepolygons.yml | 6 +- ...st-v2-terrafund-clip-polygons-polygons.yml | 2 +- ...t-v2-terrafund-clip-polygons-site-uuid.yml | 4 +- .../post-v2-terrafund-upload-geojson.yml | 7 +- .../post-v2-terrafund-upload-kml.yml | 7 +- .../post-v2-terrafund-upload-shapefile.yml | 7 +- .../post-v2-terrafund-validation-polygons.yml | 6 +- resources/docs/swagger-v2.yml | 107 +++++++++++------- 10 files changed, 85 insertions(+), 76 deletions(-) create mode 100644 openapi-src/V2/definitions/DelayedJobsResponse.yml diff --git a/openapi-src/V2/definitions/DelayedJobsResponse.yml b/openapi-src/V2/definitions/DelayedJobsResponse.yml new file mode 100644 index 000000000..c66d801c2 --- /dev/null +++ b/openapi-src/V2/definitions/DelayedJobsResponse.yml @@ -0,0 +1,11 @@ +title: DelayedJob +type: object +properties: + message: + type: string + example: "Job dispatched" # or "file queued to insert" + description: Indicates that the job has been dispatched and will be processed asynchronously. + job_uuid: + type: string + example: "123e4567-e89b-12d3-a456-426614174000" + description: Unique identifier for the delayed job. diff --git a/openapi-src/V2/definitions/_index.yml b/openapi-src/V2/definitions/_index.yml index 3843f73ee..90ca95e05 100644 --- a/openapi-src/V2/definitions/_index.yml +++ b/openapi-src/V2/definitions/_index.yml @@ -385,4 +385,6 @@ FileResource: DashboardIndicatorHectaresRestorationResponse: $ref: './DashboardIndicatorHectaresRestorationResponse.yml' DashboardIndicatorHectaresRestorationData: - $ref: './DashboardIndicatorHectaresRestorationData.yml' \ No newline at end of file + $ref: './DashboardIndicatorHectaresRestorationData.yml' +DelayedJobsResponse: + $ref: './DelayedJobsResponse.yml' \ No newline at end of file diff --git a/openapi-src/V2/paths/Terrafund/get-v2-terrafund-validation-sitepolygons.yml b/openapi-src/V2/paths/Terrafund/get-v2-terrafund-validation-sitepolygons.yml index 6596857f6..45d51c51b 100644 --- a/openapi-src/V2/paths/Terrafund/get-v2-terrafund-validation-sitepolygons.yml +++ b/openapi-src/V2/paths/Terrafund/get-v2-terrafund-validation-sitepolygons.yml @@ -9,8 +9,4 @@ responses: "200": description: Successful response schema: - type: object - properties: - message: - type: string - description: A message indicating the completion of validation for all site polygons. \ No newline at end of file + $ref: '../../definitions/_index.yml#/DelayedJobsResponse' \ No newline at end of file diff --git a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-clip-polygons-polygons.yml b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-clip-polygons-polygons.yml index 09424689e..1486197fa 100644 --- a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-clip-polygons-polygons.yml +++ b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-clip-polygons-polygons.yml @@ -16,4 +16,4 @@ responses: '200': description: A list of processed and unprocessed polygons schema: - $ref: '../../definitions/_index.yml#/ClippedPolygonsResponse' \ No newline at end of file + $ref: '../../definitions/_index.yml#/DelayedJobsResponse' \ No newline at end of file diff --git a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-clip-polygons-site-uuid.yml b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-clip-polygons-site-uuid.yml index c3d37039d..95c44f4de 100644 --- a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-clip-polygons-site-uuid.yml +++ b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-clip-polygons-site-uuid.yml @@ -8,9 +8,9 @@ parameters: type: string responses: '200': - description: Clipped polygons processed successfully + description: Clipped polygons job started succesfully schema: - $ref: '../../definitions/_index.yml#/ClippedPolygonResponse' + $ref: '../../definitions/_index.yml#/DelayedJobsResponse' '400': description: Bad request '404': diff --git a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-geojson.yml b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-geojson.yml index 6b8db7fe3..61e0346b9 100644 --- a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-geojson.yml +++ b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-geojson.yml @@ -17,12 +17,7 @@ responses: '200': description: GeoJSON file processed and inserted successfully schema: - type: object - properties: - message: - type: string - uuid: - type: string + $ref: '../../definitions/_index.yml#/DelayedJobsResponse' '400': description: Bad request '500': diff --git a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-kml.yml b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-kml.yml index e96254a09..852c40075 100644 --- a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-kml.yml +++ b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-kml.yml @@ -17,12 +17,7 @@ responses: '200': description: KML file processed and inserted successfully schema: - type: object - properties: - message: - type: string - uuid: - type: string + $ref: '../../definitions/_index.yml#/DelayedJobsResponse' '400': description: Bad request '500': diff --git a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-shapefile.yml b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-shapefile.yml index 11bc63870..c6897ff0c 100644 --- a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-shapefile.yml +++ b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-shapefile.yml @@ -17,12 +17,7 @@ responses: '200': description: Shapefile processed and inserted successfully schema: - type: object - properties: - message: - type: string - uuid: - type: string + $ref: '../../definitions/_index.yml#/DelayedJobsResponse' '400': description: Bad request '500': diff --git a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-validation-polygons.yml b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-validation-polygons.yml index c63331051..066340b0a 100644 --- a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-validation-polygons.yml +++ b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-validation-polygons.yml @@ -15,8 +15,4 @@ responses: '200': description: Successful response schema: - type: object - properties: - message: - type: string - description: A message indicating the completion of validation for all site polygons. \ No newline at end of file + $ref: '../../definitions/_index.yml#/DelayedJobsResponse' \ No newline at end of file diff --git a/resources/docs/swagger-v2.yml b/resources/docs/swagger-v2.yml index 74bc890bf..4ba213257 100644 --- a/resources/docs/swagger-v2.yml +++ b/resources/docs/swagger-v2.yml @@ -44229,6 +44229,18 @@ definitions: urban-forest: type: number description: Total amount for urban forest projects. + DelayedJobsResponse: + title: DelayedJob + type: object + properties: + message: + type: string + example: Job dispatched + description: Indicates that the job has been dispatched and will be processed asynchronously. + job_uuid: + type: string + example: 123e4567-e89b-12d3-a456-426614174000 + description: Unique identifier for the delayed job. paths: '/v2/tree-species/{entity}/{UUID}': get: @@ -92151,11 +92163,17 @@ paths: '200': description: Successful response schema: + title: DelayedJob type: object properties: message: type: string - description: A message indicating the completion of validation for all site polygons. + example: Job dispatched + description: Indicates that the job has been dispatched and will be processed asynchronously. + job_uuid: + type: string + example: 123e4567-e89b-12d3-a456-426614174000 + description: Unique identifier for the delayed job. /v2/terrafund/validation/site: get: summary: Get criteria data validation results for all polygons in a site @@ -93264,12 +93282,17 @@ paths: '200': description: GeoJSON file processed and inserted successfully schema: + title: DelayedJob type: object properties: message: type: string - uuid: + example: Job dispatched + description: Indicates that the job has been dispatched and will be processed asynchronously. + job_uuid: type: string + example: 123e4567-e89b-12d3-a456-426614174000 + description: Unique identifier for the delayed job. '400': description: Bad request '500': @@ -93295,12 +93318,17 @@ paths: '200': description: Shapefile processed and inserted successfully schema: + title: DelayedJob type: object properties: message: type: string - uuid: + example: Job dispatched + description: Indicates that the job has been dispatched and will be processed asynchronously. + job_uuid: type: string + example: 123e4567-e89b-12d3-a456-426614174000 + description: Unique identifier for the delayed job. '400': description: Bad request '500': @@ -93326,12 +93354,17 @@ paths: '200': description: KML file processed and inserted successfully schema: + title: DelayedJob type: object properties: message: type: string - uuid: + example: Job dispatched + description: Indicates that the job has been dispatched and will be processed asynchronously. + job_uuid: type: string + example: 123e4567-e89b-12d3-a456-426614174000 + description: Unique identifier for the delayed job. '400': description: Bad request '500': @@ -95792,20 +95825,19 @@ paths: type: string responses: '200': - description: Clipped polygons processed successfully + description: Clipped polygons job started succesfully schema: - title: ClippedPolygonResponse + title: DelayedJob type: object properties: - updated_polygons: - type: array - items: - type: object - properties: - uuid: - type: string - poly_name: - type: string + message: + type: string + example: Job dispatched + description: Indicates that the job has been dispatched and will be processed asynchronously. + job_uuid: + type: string + example: 123e4567-e89b-12d3-a456-426614174000 + description: Unique identifier for the delayed job. '400': description: Bad request '404': @@ -95832,36 +95864,17 @@ paths: '200': description: A list of processed and unprocessed polygons schema: + title: DelayedJob type: object properties: - processed: - type: array - description: A list of processed polygons - items: - type: object - properties: - uuid: - type: string - description: The UUID of the unprocessed polygon - example: uuid1 - poly_name: - type: string - description: The name of the unprocessed polygon - example: Polygon Name - unprocessed: - type: array - description: A list of polygons that couldn't be processed - items: - type: object - properties: - uuid: - type: string - description: The UUID of the unprocessed polygon - example: uuid1 - poly_name: - type: string - description: The name of the unprocessed polygon - example: Polygon Name + message: + type: string + example: Job dispatched + description: Indicates that the job has been dispatched and will be processed asynchronously. + job_uuid: + type: string + example: 123e4567-e89b-12d3-a456-426614174000 + description: Unique identifier for the delayed job. /v2/terrafund/validation/polygons: post: summary: Validate multiple polygons @@ -95881,8 +95894,14 @@ paths: '200': description: Successful response schema: + title: DelayedJob type: object properties: message: type: string - description: A message indicating the completion of validation for all site polygons. + example: Job dispatched + description: Indicates that the job has been dispatched and will be processed asynchronously. + job_uuid: + type: string + example: 123e4567-e89b-12d3-a456-426614174000 + description: Unique identifier for the delayed job. From cea51947ff22ba5195c6cc3c79b966aad4e4a857 Mon Sep 17 00:00:00 2001 From: JORGE Date: Fri, 25 Oct 2024 17:56:09 -0400 Subject: [PATCH 44/64] [TM-1127] remove status on init, set pending as default --- .../TerrafundClipGeometryController.php | 9 +- .../TerrafundCreateGeometryController.php | 100 ++++++++---------- app/Http/Resources/DelayedJobResource.php | 8 +- app/Jobs/FixPolygonOverlapJob.php | 8 +- app/Jobs/InsertGeojsonToDBJob.php | 31 ++++-- app/Jobs/RunSitePolygonsValidationJob.php | 1 - app/Models/DelayedJob.php | 1 - app/Services/PolygonService.php | 6 +- ...10_03_150604_create_delayed_jobs_table.php | 2 +- 9 files changed, 84 insertions(+), 82 deletions(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php index 466c7392f..a4ecb766a 100644 --- a/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundClipGeometryController.php @@ -17,14 +17,15 @@ class TerrafundClipGeometryController extends TerrafundCreateGeometryController { - private const MAX_EXECUTION_TIME = 340; + private const MAX_EXECUTION_TIME = 340; + public function clipOverlappingPolygonsBySite(string $uuid) { ini_set('max_execution_time', self::MAX_EXECUTION_TIME); ini_set('memory_limit', '-1'); $user = Auth::user(); $polygonUuids = GeometryHelper::getSitePolygonsUuids($uuid)->toArray(); - $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); + $delayedJob = DelayedJob::create(); $job = new FixPolygonOverlapJob($delayedJob->id, $polygonUuids, $user->id); dispatch($job); @@ -40,7 +41,7 @@ public function clipOverlappingPolygonsOfProjectBySite(string $uuid) $sitePolygon = Site::isUuid($uuid)->first(); $projectId = $sitePolygon->project_id ?? null; $polygonUuids = GeometryHelper::getProjectPolygonsUuids($projectId); - $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); + $delayedJob = DelayedJob::create(); $job = new FixPolygonOverlapJob($delayedJob->id, $polygonUuids, $user->id); dispatch($job); @@ -87,7 +88,7 @@ public function clipOverlappingPolygons(Request $request) $uniquePolygonUuids = array_unique($allPolygonUuids); if (! empty($uniquePolygonUuids)) { $user = Auth::user(); - $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); + $delayedJob = DelayedJob::create(); $job = new FixPolygonOverlapJob($delayedJob->id, $polygonUuids, $user->id); dispatch($job); } diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index fbb9df078..322de1448 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -223,38 +223,35 @@ function ($attribute, $value, $fail) { return response()->json(['error' => 'Failed to convert KML to GeoJSON', 'message' => $process->getErrorOutput()], 500); } $geojsonContent = file_get_contents($geojsonPath); - $polygonLoadedList = isset($body['polygon_loaded']) && + $polygonLoadedList = isset($body['polygon_loaded']) && filter_var($body['polygon_loaded'], FILTER_VALIDATE_BOOLEAN); - $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && + $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && filter_var($body['submit_polygon_loaded'], FILTER_VALIDATE_BOOLEAN); - + if ($polygonLoadedList) { $polygonLoaded = $this->GetAllPolygonsLoaded($geojsonContent, $site_id); + return response()->json($polygonLoaded->original, 200); } $redis_key = 'kml_file_' . uniqid(); Redis::set($redis_key, $geojsonContent, 'EX', 7200); - $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); - - if($delayedJob) { - $job = new InsertGeojsonToDBJob( + $delayedJob = DelayedJob::create(); + + $job = new InsertGeojsonToDBJob( $redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $submitPolygonsLoaded - ); - - dispatch($job); - - return (new DelayedJobResource($delayedJob)) - ->additional(['message' => 'KML queued to insert']); - } else { - return response()->json(['error' => 'Failed to create delayed job'], 500); - } - + ); + + dispatch($job); + + return (new DelayedJobResource($delayedJob)) + ->additional(['message' => 'KML queued to insert']); + } @@ -386,37 +383,35 @@ public function uploadShapefile(Request $request) return response()->json(['error' => 'Failed to convert Shapefile to GeoJSON', 'message' => $process->getErrorOutput()], 500); } $geojsonContent = file_get_contents($geojsonPath); - $polygonLoadedList = isset($body['polygon_loaded']) && + $polygonLoadedList = isset($body['polygon_loaded']) && filter_var($body['polygon_loaded'], FILTER_VALIDATE_BOOLEAN); - $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && + $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && filter_var($body['submit_polygon_loaded'], FILTER_VALIDATE_BOOLEAN); - + if ($polygonLoadedList) { $polygonLoaded = $this->GetAllPolygonsLoaded($geojsonContent, $site_id); + return response()->json($polygonLoaded->original, 200); } $redis_key = 'shapefile_file_' . uniqid(); Redis::set($redis_key, $geojsonContent, 'EX', 7200); - $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); + $delayedJob = DelayedJob::create(); - if($delayedJob) { - $job = new InsertGeojsonToDBJob( + $job = new InsertGeojsonToDBJob( $redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $submitPolygonsLoaded - ); - - dispatch($job); - - return (new DelayedJobResource($delayedJob)) - ->additional(['message' => 'Shapefile queued to insert']); - } else { - return response()->json(['error' => 'Failed to create delayed job'], 500); - } + ); + + dispatch($job); + + return (new DelayedJobResource($delayedJob)) + ->additional(['message' => 'Shapefile queued to insert']); + } else { return response()->json(['error' => 'Failed to open the ZIP file'], 400); @@ -607,39 +602,36 @@ public function uploadGeoJSONFile(Request $request) $file->move($tempDir, $filename); $filePath = $tempDir . DIRECTORY_SEPARATOR . $filename; $geojson_content = file_get_contents($filePath); - $polygonLoadedList = isset($body['polygon_loaded']) && + $polygonLoadedList = isset($body['polygon_loaded']) && filter_var($body['polygon_loaded'], FILTER_VALIDATE_BOOLEAN); - $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && + $submitPolygonsLoaded = isset($body['submit_polygon_loaded']) && filter_var($body['submit_polygon_loaded'], FILTER_VALIDATE_BOOLEAN); if ($polygonLoadedList) { - $polygonLoaded = $this->GetAllPolygonsLoaded($geojson_content, $site_id); - - return response()->json($polygonLoaded->original, 200); + $polygonLoaded = $this->GetAllPolygonsLoaded($geojson_content, $site_id); + + return response()->json($polygonLoaded->original, 200); } - - + + $redis_key = 'geojson_file_' . uniqid(); Redis::set($redis_key, $geojson_content, 'EX', 7200); - $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); + $delayedJob = DelayedJob::create(); - if($delayedJob) { - $job = new InsertGeojsonToDBJob( + $job = new InsertGeojsonToDBJob( $redis_key, $delayedJob->id, $site_id, 'site', $body['primary_uuid'] ?? null, $submitPolygonsLoaded - ); - - dispatch($job); - - return (new DelayedJobResource($delayedJob)) - ->additional(['message' => 'Geojson queued to insert']); - } else { - return response()->json(['error' => 'Failed to create delayed job'], 500); - } + ); + + dispatch($job); + + return (new DelayedJobResource($delayedJob)) + ->additional(['message' => 'Geojson queued to insert']); + } @@ -1221,7 +1213,7 @@ public function runSiteValidationPolygon(Request $request) $uuid = $request->input('uuid'); $sitePolygonsUuids = GeometryHelper::getSitePolygonsUuids($uuid)->toArray(); - $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); + $delayedJob = DelayedJob::create(); $job = new RunSitePolygonsValidationJob($delayedJob->id, $sitePolygonsUuids); dispatch($job); @@ -1237,12 +1229,12 @@ public function runPolygonsValidation(Request $request) { try { $uuids = $request->input('uuids'); - $delayedJob = DelayedJob::create(['status' => DelayedJob::STATUS_PENDING]); + $delayedJob = DelayedJob::create(); $job = new RunSitePolygonsValidationJob($delayedJob->id, $uuids); dispatch($job); return (new DelayedJobResource($delayedJob))->additional(['message' => 'Validation completed for these polygons']); - + } catch (\Exception $e) { return response()->json(['error' => 'An error occurred during validation'], 500); } diff --git a/app/Http/Resources/DelayedJobResource.php b/app/Http/Resources/DelayedJobResource.php index 6f362001b..b993791a6 100644 --- a/app/Http/Resources/DelayedJobResource.php +++ b/app/Http/Resources/DelayedJobResource.php @@ -14,9 +14,9 @@ class DelayedJobResource extends JsonResource */ public function toArray(Request $request): array { - return [ - 'message' => $this->message ?? 'Job dispatched', - 'job_uuid' => $this->uuid - ]; + return [ + 'message' => $this->message ?? 'Job dispatched', + 'job_uuid' => $this->uuid, + ]; } } diff --git a/app/Jobs/FixPolygonOverlapJob.php b/app/Jobs/FixPolygonOverlapJob.php index 1ed7ebebf..6196c339e 100644 --- a/app/Jobs/FixPolygonOverlapJob.php +++ b/app/Jobs/FixPolygonOverlapJob.php @@ -15,7 +15,6 @@ use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; -use Illuminate\Support\Str; use Throwable; class FixPolygonOverlapJob implements ShouldQueue @@ -26,11 +25,11 @@ class FixPolygonOverlapJob implements ShouldQueue use SerializesModels; public $timeout = 0; - + protected $polygonService; protected $polygonUuids; - + public $authUserId; protected $delayed_job_id; @@ -71,7 +70,7 @@ public function handle(): void $delayedJob->update([ 'status' => DelayedJob::STATUS_SUCCEEDED, 'payload' => json_encode(['updated_polygons' => $polygonsClipped]), - 'status_code' => Response::HTTP_OK + 'status_code' => Response::HTTP_OK, ]); } } catch (Exception $e) { @@ -92,5 +91,4 @@ public function handle(): void ]); } } - } diff --git a/app/Jobs/InsertGeojsonToDBJob.php b/app/Jobs/InsertGeojsonToDBJob.php index 5e4dc05ec..a07752d5b 100755 --- a/app/Jobs/InsertGeojsonToDBJob.php +++ b/app/Jobs/InsertGeojsonToDBJob.php @@ -16,17 +16,26 @@ use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Redis; -use Illuminate\Support\Str; + class InsertGeojsonToDBJob implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Dispatchable; + use InteractsWithQueue; + use Queueable; + use SerializesModels; public $timeout = 0; + protected $entity_uuid; + protected $entity_type; + protected $primary_uuid; + protected $submit_polygon_loaded; + protected $redis_key; + protected $delayed_job_id; public function __construct(string $redis_key, string $delayed_job_id, ?string $entity_uuid = null, ?string $entity_type = null, ?string $primary_uuid = null, ?bool $submit_polygon_loaded = false) @@ -38,23 +47,25 @@ public function __construct(string $redis_key, string $delayed_job_id, ?string $ $this->submit_polygon_loaded = $submit_polygon_loaded; $this->delayed_job_id = $delayed_job_id; } + public function handle(PolygonService $service) { $delayedJob = DelayedJob::findOrFail($this->delayed_job_id); - + try { $geojsonContent = Redis::get($this->redis_key); - - if (!$geojsonContent) { + + if (! $geojsonContent) { Log::error('GeoJSON content not found in Redis for key: ' . $this->redis_key); $delayedJob->update([ 'status' => DelayedJob::STATUS_FAILED, 'payload' => ['error' => 'GeoJSON content not found in Redis'], 'status_code' => Response::HTTP_NOT_FOUND, ]); + return; } - + $uuids = $service->insertGeojsonToDBFromContent( $geojsonContent, $this->entity_uuid, @@ -62,19 +73,19 @@ public function handle(PolygonService $service) $this->primary_uuid, $this->submit_polygon_loaded ); - + if (isset($uuids['error'])) { throw new \Exception($uuids['error'], Response::HTTP_INTERNAL_SERVER_ERROR); } - + App::make(SiteService::class)->setSiteToRestorationInProgress($this->entity_uuid); - + $delayedJob->update([ 'status' => DelayedJob::STATUS_SUCCEEDED, 'payload' => json_encode($uuids), 'status_code' => Response::HTTP_OK, ]); - + } catch (Exception $e) { Log::error('Error in InsertGeojsonToDBJob: ' . $e->getMessage()); $delayedJob->update([ diff --git a/app/Jobs/RunSitePolygonsValidationJob.php b/app/Jobs/RunSitePolygonsValidationJob.php index fd019d7f1..88065fe11 100644 --- a/app/Jobs/RunSitePolygonsValidationJob.php +++ b/app/Jobs/RunSitePolygonsValidationJob.php @@ -13,7 +13,6 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Log; -use Illuminate\Support\Str; class RunSitePolygonsValidationJob implements ShouldQueue { diff --git a/app/Models/DelayedJob.php b/app/Models/DelayedJob.php index 21d1151f7..4ae833449 100644 --- a/app/Models/DelayedJob.php +++ b/app/Models/DelayedJob.php @@ -15,7 +15,6 @@ class DelayedJob extends Model public const STATUS_FAILED = 'failed'; public const STATUS_SUCCEEDED = 'succeeded'; - protected $table = 'delayed_jobs'; protected $fillable = [ diff --git a/app/Services/PolygonService.php b/app/Services/PolygonService.php index b90e69fdb..a8b71eff9 100755 --- a/app/Services/PolygonService.php +++ b/app/Services/PolygonService.php @@ -518,8 +518,9 @@ public function insertGeojsonToDB(string $geojsonFilename, ?string $entity_uuid } } } - /** - * @throws ValidationException + + /** + * @throws ValidationException */ public function insertGeojsonToDBFromContent(string $geojsonData, ?string $entity_uuid = null, ?string $entity_type = null, ?string $primary_uuid = null, ?bool $submit_polygon_loaded = false) { @@ -527,6 +528,7 @@ public function insertGeojsonToDBFromContent(string $geojsonData, ?string $entit $geojson = json_decode($geojsonData, true); SitePolygonValidator::validate('FEATURE_BOUNDS', $geojson, false); SitePolygonValidator::validate('GEOMETRY_TYPE', $geojson, false); + return $this->createGeojsonModels($geojson, ['site_id' => $entity_uuid, 'source' => PolygonService::UPLOADED_SOURCE], $primary_uuid, $submit_polygon_loaded); } catch (Exception $e) { diff --git a/database/migrations/2024_10_03_150604_create_delayed_jobs_table.php b/database/migrations/2024_10_03_150604_create_delayed_jobs_table.php index 18ee17f28..2631d42ef 100644 --- a/database/migrations/2024_10_03_150604_create_delayed_jobs_table.php +++ b/database/migrations/2024_10_03_150604_create_delayed_jobs_table.php @@ -13,7 +13,7 @@ public function up(): void Schema::create('delayed_jobs', function (Blueprint $table) { $table->id(); $table->uuid('uuid')->unique(); - $table->string('status'); + $table->string('status')->default('pending'); $table->integer('status_code')->nullable(); $table->json('payload')->nullable(); $table->timestamps(); From 7fb92691f9766f7d9c364121310e91bedc7c077d Mon Sep 17 00:00:00 2001 From: JORGE Date: Fri, 25 Oct 2024 17:57:06 -0400 Subject: [PATCH 45/64] [TM-1127] remove try catch --- .../CreateVersionPolygonGeometryHelper.php | 60 +++++++++---------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/app/Helpers/CreateVersionPolygonGeometryHelper.php b/app/Helpers/CreateVersionPolygonGeometryHelper.php index 6abdda79a..818394752 100644 --- a/app/Helpers/CreateVersionPolygonGeometryHelper.php +++ b/app/Helpers/CreateVersionPolygonGeometryHelper.php @@ -17,38 +17,34 @@ class CreateVersionPolygonGeometryHelper */ public static function createVersionPolygonGeometry(string $uuid, $geometry) { - try { - Log::info("Creating geometry version for polygon with UUID: $uuid"); - - if ($geometry instanceof Request) { - $geometry = $geometry->input('geometry'); - } - $polygonGeometry = PolygonGeometry::isUuid($uuid)->first(); - if (! $polygonGeometry) { - return response()->json(['message' => 'No polygon geometry found for the given UUID.'], 404); - } - $geometry = json_decode($geometry); - $geom = DB::raw("ST_GeomFromGeoJSON('" . json_encode($geometry) . "')"); - - $sitePolygon = SitePolygon::where('poly_id', $polygonGeometry->uuid)->first(); - - $user = Auth::user(); - $newGeometryVersion = PolygonGeometry::create([ - 'geom' => $geom, - 'created_by' => $user->id, - ]); - $newPolygonVersion = $sitePolygon->createCopy($user, $newGeometryVersion->uuid, false); - if ($newPolygonVersion) { - PolyHelper::updateEstAreainSitePolygon($newGeometryVersion, $geometry); - PolyHelper::updateProjectCentroidFromPolygon($newGeometryVersion); - $newPolygonVersion->changeStatusOnEdit(); - - return response()->json(['message' => 'Site polygon version created successfully.', 'geometry' => $geometry, 'uuid' => $newPolygonVersion->poly_id], 201); - } - - return response()->json(['message' => 'Failed to create site polygon version.'], 500); - } catch (\Exception $e) { - throw $e; + Log::info("Creating geometry version for polygon with UUID: $uuid"); + + if ($geometry instanceof Request) { + $geometry = $geometry->input('geometry'); + } + $polygonGeometry = PolygonGeometry::isUuid($uuid)->first(); + if (! $polygonGeometry) { + return response()->json(['message' => 'No polygon geometry found for the given UUID.'], 404); } + $geometry = json_decode($geometry); + $geom = DB::raw("ST_GeomFromGeoJSON('" . json_encode($geometry) . "')"); + + $sitePolygon = SitePolygon::where('poly_id', $polygonGeometry->uuid)->first(); + + $user = Auth::user(); + $newGeometryVersion = PolygonGeometry::create([ + 'geom' => $geom, + 'created_by' => $user->id, + ]); + $newPolygonVersion = $sitePolygon->createCopy($user, $newGeometryVersion->uuid, false); + if ($newPolygonVersion) { + PolyHelper::updateEstAreainSitePolygon($newGeometryVersion, $geometry); + PolyHelper::updateProjectCentroidFromPolygon($newGeometryVersion); + $newPolygonVersion->changeStatusOnEdit(); + + return response()->json(['message' => 'Site polygon version created successfully.', 'geometry' => $geometry, 'uuid' => $newPolygonVersion->poly_id], 201); + } + + return response()->json(['message' => 'Failed to create site polygon version.'], 500); } } From 2c9d1011ad13801b1f5e296d898f558771954076 Mon Sep 17 00:00:00 2001 From: Nathan Curtis Date: Mon, 28 Oct 2024 08:18:52 -0700 Subject: [PATCH 46/64] Roll back documentation changes; this system is going to work more like middleware. This reverts commit f61c6a5db1b4915314022dc99bdd25c2ff0e4e3d. --- .../V2/definitions/DelayedJobsResponse.yml | 11 -- openapi-src/V2/definitions/_index.yml | 4 +- ...t-v2-terrafund-validation-sitepolygons.yml | 6 +- ...st-v2-terrafund-clip-polygons-polygons.yml | 2 +- ...t-v2-terrafund-clip-polygons-site-uuid.yml | 4 +- .../post-v2-terrafund-upload-geojson.yml | 7 +- .../post-v2-terrafund-upload-kml.yml | 7 +- .../post-v2-terrafund-upload-shapefile.yml | 7 +- .../post-v2-terrafund-validation-polygons.yml | 6 +- resources/docs/swagger-v2.yml | 107 +++++++----------- 10 files changed, 76 insertions(+), 85 deletions(-) delete mode 100644 openapi-src/V2/definitions/DelayedJobsResponse.yml diff --git a/openapi-src/V2/definitions/DelayedJobsResponse.yml b/openapi-src/V2/definitions/DelayedJobsResponse.yml deleted file mode 100644 index c66d801c2..000000000 --- a/openapi-src/V2/definitions/DelayedJobsResponse.yml +++ /dev/null @@ -1,11 +0,0 @@ -title: DelayedJob -type: object -properties: - message: - type: string - example: "Job dispatched" # or "file queued to insert" - description: Indicates that the job has been dispatched and will be processed asynchronously. - job_uuid: - type: string - example: "123e4567-e89b-12d3-a456-426614174000" - description: Unique identifier for the delayed job. diff --git a/openapi-src/V2/definitions/_index.yml b/openapi-src/V2/definitions/_index.yml index 90ca95e05..3843f73ee 100644 --- a/openapi-src/V2/definitions/_index.yml +++ b/openapi-src/V2/definitions/_index.yml @@ -385,6 +385,4 @@ FileResource: DashboardIndicatorHectaresRestorationResponse: $ref: './DashboardIndicatorHectaresRestorationResponse.yml' DashboardIndicatorHectaresRestorationData: - $ref: './DashboardIndicatorHectaresRestorationData.yml' -DelayedJobsResponse: - $ref: './DelayedJobsResponse.yml' \ No newline at end of file + $ref: './DashboardIndicatorHectaresRestorationData.yml' \ No newline at end of file diff --git a/openapi-src/V2/paths/Terrafund/get-v2-terrafund-validation-sitepolygons.yml b/openapi-src/V2/paths/Terrafund/get-v2-terrafund-validation-sitepolygons.yml index 45d51c51b..6596857f6 100644 --- a/openapi-src/V2/paths/Terrafund/get-v2-terrafund-validation-sitepolygons.yml +++ b/openapi-src/V2/paths/Terrafund/get-v2-terrafund-validation-sitepolygons.yml @@ -9,4 +9,8 @@ responses: "200": description: Successful response schema: - $ref: '../../definitions/_index.yml#/DelayedJobsResponse' \ No newline at end of file + type: object + properties: + message: + type: string + description: A message indicating the completion of validation for all site polygons. \ No newline at end of file diff --git a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-clip-polygons-polygons.yml b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-clip-polygons-polygons.yml index 1486197fa..09424689e 100644 --- a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-clip-polygons-polygons.yml +++ b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-clip-polygons-polygons.yml @@ -16,4 +16,4 @@ responses: '200': description: A list of processed and unprocessed polygons schema: - $ref: '../../definitions/_index.yml#/DelayedJobsResponse' \ No newline at end of file + $ref: '../../definitions/_index.yml#/ClippedPolygonsResponse' \ No newline at end of file diff --git a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-clip-polygons-site-uuid.yml b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-clip-polygons-site-uuid.yml index 95c44f4de..c3d37039d 100644 --- a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-clip-polygons-site-uuid.yml +++ b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-clip-polygons-site-uuid.yml @@ -8,9 +8,9 @@ parameters: type: string responses: '200': - description: Clipped polygons job started succesfully + description: Clipped polygons processed successfully schema: - $ref: '../../definitions/_index.yml#/DelayedJobsResponse' + $ref: '../../definitions/_index.yml#/ClippedPolygonResponse' '400': description: Bad request '404': diff --git a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-geojson.yml b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-geojson.yml index 61e0346b9..6b8db7fe3 100644 --- a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-geojson.yml +++ b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-geojson.yml @@ -17,7 +17,12 @@ responses: '200': description: GeoJSON file processed and inserted successfully schema: - $ref: '../../definitions/_index.yml#/DelayedJobsResponse' + type: object + properties: + message: + type: string + uuid: + type: string '400': description: Bad request '500': diff --git a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-kml.yml b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-kml.yml index 852c40075..e96254a09 100644 --- a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-kml.yml +++ b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-kml.yml @@ -17,7 +17,12 @@ responses: '200': description: KML file processed and inserted successfully schema: - $ref: '../../definitions/_index.yml#/DelayedJobsResponse' + type: object + properties: + message: + type: string + uuid: + type: string '400': description: Bad request '500': diff --git a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-shapefile.yml b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-shapefile.yml index c6897ff0c..11bc63870 100644 --- a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-shapefile.yml +++ b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-upload-shapefile.yml @@ -17,7 +17,12 @@ responses: '200': description: Shapefile processed and inserted successfully schema: - $ref: '../../definitions/_index.yml#/DelayedJobsResponse' + type: object + properties: + message: + type: string + uuid: + type: string '400': description: Bad request '500': diff --git a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-validation-polygons.yml b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-validation-polygons.yml index 066340b0a..c63331051 100644 --- a/openapi-src/V2/paths/Terrafund/post-v2-terrafund-validation-polygons.yml +++ b/openapi-src/V2/paths/Terrafund/post-v2-terrafund-validation-polygons.yml @@ -15,4 +15,8 @@ responses: '200': description: Successful response schema: - $ref: '../../definitions/_index.yml#/DelayedJobsResponse' \ No newline at end of file + type: object + properties: + message: + type: string + description: A message indicating the completion of validation for all site polygons. \ No newline at end of file diff --git a/resources/docs/swagger-v2.yml b/resources/docs/swagger-v2.yml index 4ba213257..74bc890bf 100644 --- a/resources/docs/swagger-v2.yml +++ b/resources/docs/swagger-v2.yml @@ -44229,18 +44229,6 @@ definitions: urban-forest: type: number description: Total amount for urban forest projects. - DelayedJobsResponse: - title: DelayedJob - type: object - properties: - message: - type: string - example: Job dispatched - description: Indicates that the job has been dispatched and will be processed asynchronously. - job_uuid: - type: string - example: 123e4567-e89b-12d3-a456-426614174000 - description: Unique identifier for the delayed job. paths: '/v2/tree-species/{entity}/{UUID}': get: @@ -92163,17 +92151,11 @@ paths: '200': description: Successful response schema: - title: DelayedJob type: object properties: message: type: string - example: Job dispatched - description: Indicates that the job has been dispatched and will be processed asynchronously. - job_uuid: - type: string - example: 123e4567-e89b-12d3-a456-426614174000 - description: Unique identifier for the delayed job. + description: A message indicating the completion of validation for all site polygons. /v2/terrafund/validation/site: get: summary: Get criteria data validation results for all polygons in a site @@ -93282,17 +93264,12 @@ paths: '200': description: GeoJSON file processed and inserted successfully schema: - title: DelayedJob type: object properties: message: type: string - example: Job dispatched - description: Indicates that the job has been dispatched and will be processed asynchronously. - job_uuid: + uuid: type: string - example: 123e4567-e89b-12d3-a456-426614174000 - description: Unique identifier for the delayed job. '400': description: Bad request '500': @@ -93318,17 +93295,12 @@ paths: '200': description: Shapefile processed and inserted successfully schema: - title: DelayedJob type: object properties: message: type: string - example: Job dispatched - description: Indicates that the job has been dispatched and will be processed asynchronously. - job_uuid: + uuid: type: string - example: 123e4567-e89b-12d3-a456-426614174000 - description: Unique identifier for the delayed job. '400': description: Bad request '500': @@ -93354,17 +93326,12 @@ paths: '200': description: KML file processed and inserted successfully schema: - title: DelayedJob type: object properties: message: type: string - example: Job dispatched - description: Indicates that the job has been dispatched and will be processed asynchronously. - job_uuid: + uuid: type: string - example: 123e4567-e89b-12d3-a456-426614174000 - description: Unique identifier for the delayed job. '400': description: Bad request '500': @@ -95825,19 +95792,20 @@ paths: type: string responses: '200': - description: Clipped polygons job started succesfully + description: Clipped polygons processed successfully schema: - title: DelayedJob + title: ClippedPolygonResponse type: object properties: - message: - type: string - example: Job dispatched - description: Indicates that the job has been dispatched and will be processed asynchronously. - job_uuid: - type: string - example: 123e4567-e89b-12d3-a456-426614174000 - description: Unique identifier for the delayed job. + updated_polygons: + type: array + items: + type: object + properties: + uuid: + type: string + poly_name: + type: string '400': description: Bad request '404': @@ -95864,17 +95832,36 @@ paths: '200': description: A list of processed and unprocessed polygons schema: - title: DelayedJob type: object properties: - message: - type: string - example: Job dispatched - description: Indicates that the job has been dispatched and will be processed asynchronously. - job_uuid: - type: string - example: 123e4567-e89b-12d3-a456-426614174000 - description: Unique identifier for the delayed job. + processed: + type: array + description: A list of processed polygons + items: + type: object + properties: + uuid: + type: string + description: The UUID of the unprocessed polygon + example: uuid1 + poly_name: + type: string + description: The name of the unprocessed polygon + example: Polygon Name + unprocessed: + type: array + description: A list of polygons that couldn't be processed + items: + type: object + properties: + uuid: + type: string + description: The UUID of the unprocessed polygon + example: uuid1 + poly_name: + type: string + description: The name of the unprocessed polygon + example: Polygon Name /v2/terrafund/validation/polygons: post: summary: Validate multiple polygons @@ -95894,14 +95881,8 @@ paths: '200': description: Successful response schema: - title: DelayedJob type: object properties: message: type: string - example: Job dispatched - description: Indicates that the job has been dispatched and will be processed asynchronously. - job_uuid: - type: string - example: 123e4567-e89b-12d3-a456-426614174000 - description: Unique identifier for the delayed job. + description: A message indicating the completion of validation for all site polygons. From e668c4b139f50b45709b0621a403b174edfaccc3 Mon Sep 17 00:00:00 2001 From: Limber Mamani <154026979+LimberHope@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:34:20 -0400 Subject: [PATCH 47/64] [TM-1479] changes columns to table indicators (#520) --- ..._to_indicator_slug_to_indicator_tables.php | 80 ++++++++++++++++++ ...000_add_timestamps_to_indicator_tables.php | 82 +++++++++++++++++++ ...queness_constraint_to_indicator_tables.php | 67 +++++++++++++++ 3 files changed, 229 insertions(+) create mode 100644 database/migrations/2024_10_28_143000_change_indicator_id_to_indicator_slug_to_indicator_tables.php create mode 100644 database/migrations/2024_10_28_150000_add_timestamps_to_indicator_tables.php create mode 100644 database/migrations/2024_10_28_153000_add_uniqueness_constraint_to_indicator_tables.php diff --git a/database/migrations/2024_10_28_143000_change_indicator_id_to_indicator_slug_to_indicator_tables.php b/database/migrations/2024_10_28_143000_change_indicator_id_to_indicator_slug_to_indicator_tables.php new file mode 100644 index 000000000..5b9bde366 --- /dev/null +++ b/database/migrations/2024_10_28_143000_change_indicator_id_to_indicator_slug_to_indicator_tables.php @@ -0,0 +1,80 @@ +string('indicator_slug')->nullable(); + $table->dropColumn('indicator_id'); + }); + + Schema::table('indicator_output_hectares', function (Blueprint $table) { + $table->string('indicator_slug')->nullable(); + $table->dropColumn('indicator_id'); + }); + + Schema::table('indicator_output_tree_count', function (Blueprint $table) { + $table->string('indicator_slug')->nullable(); + $table->dropColumn('indicator_id'); + }); + + Schema::table('indicator_output_tree_cover', function (Blueprint $table) { + $table->string('indicator_slug')->nullable(); + $table->dropColumn('indicator_id'); + }); + + Schema::table('indicator_output_field_monitoring', function (Blueprint $table) { + $table->string('indicator_slug')->nullable(); + $table->dropColumn('indicator_id'); + }); + + Schema::table('indicator_output_msu_carbon', function (Blueprint $table) { + $table->string('indicator_slug')->nullable(); + $table->dropColumn('indicator_id'); + }); + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('indicator_output_tree_cover_loss', function (Blueprint $table) { + $table->dropColumn('indicator_slug'); + $table->enum('indicator_id', [2, 3])->nullable(); + }); + + Schema::table('indicator_output_hectares', function (Blueprint $table) { + $table->dropColumn('indicator_slug'); + $table->enum('indicator_id', [4, 5, 6])->nullable(); + }); + + Schema::table('indicator_output_tree_count', function (Blueprint $table) { + $table->dropColumn('indicator_slug'); + $table->enum('indicator_id', [7, 8])->nullable(); + }); + + Schema::table('indicator_output_tree_cover', function (Blueprint $table) { + $table->dropColumn('indicator_slug'); + $table->enum('indicator_id', [1])->nullable(); + }); + + Schema::table('indicator_output_field_monitoring', function (Blueprint $table) { + $table->dropColumn('indicator_slug'); + $table->enum('indicator_id', [9])->nullable(); + }); + + Schema::table('indicator_output_msu_carbon', function (Blueprint $table) { + $table->dropColumn('indicator_slug'); + $table->enum('indicator_id', [10])->nullable(); + }); + } +}; diff --git a/database/migrations/2024_10_28_150000_add_timestamps_to_indicator_tables.php b/database/migrations/2024_10_28_150000_add_timestamps_to_indicator_tables.php new file mode 100644 index 000000000..fa00f61dd --- /dev/null +++ b/database/migrations/2024_10_28_150000_add_timestamps_to_indicator_tables.php @@ -0,0 +1,82 @@ +timestamps(); + $table->dropColumn('date_created'); + }); + + Schema::table('indicator_output_tree_cover_loss', function (Blueprint $table) { + $table->timestamps(); + $table->dropColumn('date_created'); + }); + + Schema::table('indicator_output_hectares', function (Blueprint $table) { + $table->timestamps(); + $table->dropColumn('date_created'); + }); + + Schema::table('indicator_output_tree_count', function (Blueprint $table) { + $table->timestamps(); + $table->dropColumn('date_created'); + }); + + Schema::table('indicator_output_tree_cover', function (Blueprint $table) { + $table->timestamps(); + $table->dropColumn('date_created'); + }); + + Schema::table('indicator_output_field_monitoring', function (Blueprint $table) { + $table->timestamps(); + $table->dropColumn('date_created'); + }); + + Schema::table('indicator_output_msu_carbon', function (Blueprint $table) { + $table->timestamps(); + $table->dropColumn('date_created'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('code_indicator', function (Blueprint $table) { + $table->date('date_created')->nullable(); + }); + + Schema::table('indicator_output_tree_cover_loss', function (Blueprint $table) { + $table->date('date_created')->nullable(); + }); + + Schema::table('indicator_output_hectares', function (Blueprint $table) { + $table->date('date_created')->nullable(); + }); + + Schema::table('indicator_output_tree_count', function (Blueprint $table) { + $table->date('date_created')->nullable(); + }); + + Schema::table('indicator_output_tree_cover', function (Blueprint $table) { + $table->date('date_created')->nullable(); + }); + + Schema::table('indicator_output_field_monitoring', function (Blueprint $table) { + $table->date('date_created')->nullable(); + }); + + Schema::table('indicator_output_msu_carbon', function (Blueprint $table) { + $table->date('date_created')->nullable(); + }); + } +}; diff --git a/database/migrations/2024_10_28_153000_add_uniqueness_constraint_to_indicator_tables.php b/database/migrations/2024_10_28_153000_add_uniqueness_constraint_to_indicator_tables.php new file mode 100644 index 000000000..843f55a7e --- /dev/null +++ b/database/migrations/2024_10_28_153000_add_uniqueness_constraint_to_indicator_tables.php @@ -0,0 +1,67 @@ +unique(['polygon_id', 'indicator_slug', 'year_of_analysis'], 'unique_polygon_indicator_year'); + }); + + Schema::table('indicator_output_hectares', function (Blueprint $table) { + $table->unique(['polygon_id', 'indicator_slug', 'year_of_analysis'], 'unique_polygon_indicator_year'); + }); + + Schema::table('indicator_output_tree_count', function (Blueprint $table) { + $table->unique(['polygon_id', 'indicator_slug', 'year_of_analysis'], 'unique_polygon_indicator_year'); + }); + + Schema::table('indicator_output_tree_cover', function (Blueprint $table) { + $table->unique(['polygon_id', 'indicator_slug', 'year_of_analysis'], 'unique_polygon_indicator_year'); + }); + + Schema::table('indicator_output_field_monitoring', function (Blueprint $table) { + $table->unique(['polygon_id', 'indicator_slug', 'year_of_analysis'], 'unique_polygon_indicator_year'); + }); + + Schema::table('indicator_output_msu_carbon', function (Blueprint $table) { + $table->unique(['polygon_id', 'indicator_slug', 'year_of_analysis'], 'unique_polygon_indicator_year'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('indicator_output_tree_cover_loss', function (Blueprint $table) { + $table->dropUnique('unique_polygon_indicator_year'); + }); + + Schema::table('indicator_output_hectares', function (Blueprint $table) { + $table->dropUnique('unique_polygon_indicator_year'); + }); + + Schema::table('indicator_output_tree_count', function (Blueprint $table) { + $table->dropUnique('unique_polygon_indicator_year'); + }); + + Schema::table('indicator_output_tree_cover', function (Blueprint $table) { + $table->dropUnique('unique_polygon_indicator_year'); + }); + + Schema::table('indicator_output_field_monitoring', function (Blueprint $table) { + $table->dropUnique('unique_polygon_indicator_year'); + }); + + Schema::table('indicator_output_msu_carbon', function (Blueprint $table) { + $table->dropUnique('unique_polygon_indicator_year'); + }); + } +}; From 4718949672ff6890bee5cfdc7296d5193d3eaee2 Mon Sep 17 00:00:00 2001 From: Jorge Monroy Date: Mon, 28 Oct 2024 15:59:39 -0400 Subject: [PATCH 48/64] [TM-1366] insert landscape data, create seeder and migration (#521) * [TM-1366] insert landscape data, create seeder and migration * [TM-1366] add fix lint --- ..._landscape_column_in_v2_projects_table.php | 28 +++++ database/seeders/InsertLandscapeData.php | 61 ++++++++++ resources/seeds/uuid-by-landscape.csv | 109 ++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 database/migrations/2024_10_28_192049_modify_landscape_column_in_v2_projects_table.php create mode 100644 database/seeders/InsertLandscapeData.php create mode 100644 resources/seeds/uuid-by-landscape.csv diff --git a/database/migrations/2024_10_28_192049_modify_landscape_column_in_v2_projects_table.php b/database/migrations/2024_10_28_192049_modify_landscape_column_in_v2_projects_table.php new file mode 100644 index 000000000..11c10a4ad --- /dev/null +++ b/database/migrations/2024_10_28_192049_modify_landscape_column_in_v2_projects_table.php @@ -0,0 +1,28 @@ +dropColumn('landscape'); + }); + } + + Schema::table('v2_projects', function (Blueprint $table) { + $table->string('landscape')->nullable(); + }); + } + + public function down(): void + { + Schema::table('v2_projects', function (Blueprint $table) { + $table->dropColumn('landscape'); + }); + } +} diff --git a/database/seeders/InsertLandscapeData.php b/database/seeders/InsertLandscapeData.php new file mode 100644 index 000000000..9e07ce4db --- /dev/null +++ b/database/seeders/InsertLandscapeData.php @@ -0,0 +1,61 @@ +command->error("CSV file not found at {$filePath}"); + + return; + } + + $data = array_map('str_getcsv', file($filePath)); + $header = array_shift($data); // Remove headers + + $output = new ConsoleOutput(); + $createProgress = new ProgressBar($output, count($data)); + $updateProgress = new ProgressBar($output, count($data)); + + $createdCount = 0; + $updatedCount = 0; + + $createProgress->setFormat('Created: %current% [%bar%] %percent:3s%%'); + $updateProgress->setFormat('Updated: %current% [%bar%] %percent:3s%%'); + + foreach ($data as $row) { + $project = Project::updateOrCreate( + ['uuid' => $row[0]], + ['landscape' => $row[1]] + ); + + if ($project->wasRecentlyCreated) { + $createdCount++; + $createProgress->advance(); + } else { + $updatedCount++; + $updateProgress->advance(); + } + } + + $createProgress->finish(); + $output->writeln(''); + $updateProgress->finish(); + + $output->writeln("\nTotal Created: $createdCount"); + $output->writeln("Total Updated: $updatedCount"); + } +} diff --git a/resources/seeds/uuid-by-landscape.csv b/resources/seeds/uuid-by-landscape.csv new file mode 100644 index 000000000..723fef745 --- /dev/null +++ b/resources/seeds/uuid-by-landscape.csv @@ -0,0 +1,109 @@ +TM Project ID,Target Landscape +0f4bdbba-adf9-4554-ba29-fb28d3b9c1fd,Ghana Cocoa Belt +1df7b898-1751-4924-b293-43a0cbcb505d,Lake Kivu & Rusizi River Basin +1e207d56-d0ed-4b4f-b1d9-94aa8aafb71f,Lake Kivu & Rusizi River Basin +01e1872d-bb9c-4cce-948c-8756598e029b,Greater Rift Valley of Kenya +3a2099dd-9a85-4091-b0c7-5edd11acbcd6,Lake Kivu & Rusizi River Basin +4a5aac1e-4ae0-4a54-9a0c-a2f332aabd9f,Lake Kivu & Rusizi River Basin +04a8e791-a41c-42c8-8fa5-580b839d7703,Lake Kivu & Rusizi River Basin +04af9a53-455b-4972-bf23-baa3019919be,Greater Rift Valley of Kenya +5ab53192-39c8-4284-95d5-6f5544f8e94f,Greater Rift Valley of Kenya +05c05359-98f9-4bb3-a7fa-236051940aa6,Greater Rift Valley of Kenya +6ac2c02b-fa83-448c-813c-eaab14efecf1,Greater Rift Valley of Kenya +6c82ba11-3069-4f8a-991f-a1a29f82e192,Lake Kivu & Rusizi River Basin +6daf9f10-dcb8-4b6d-8955-19e6df7ec48a,Lake Kivu & Rusizi River Basin +6eb233d3-63bc-41dd-be2b-7d4f7c01a95a,Lake Kivu & Rusizi River Basin +7a4f2aa6-e916-45bd-ba27-c3fa3d4ea938,Lake Kivu & Rusizi River Basin +8bb81813-c1f3-4973-9caf-319d506a153b,Ghana Cocoa Belt +8c4c6cf9-5b3e-4f36-9091-24b3b1a80403,Greater Rift Valley of Kenya +8f35f005-0876-4b87-b466-41aa86d6798f,Lake Kivu & Rusizi River Basin +9bdf80ad-4c2d-4292-a21b-95b819f9c4be,Greater Rift Valley of Kenya +9c22bab7-9170-4502-bfd5-f7ea361764ce,Lake Kivu & Rusizi River Basin +9c93de5d-12cd-4e7e-b99c-49cb6f890e58,Greater Rift Valley of Kenya +9db2495f-bee1-4323-97ac-c86a3a4e0283,Ghana Cocoa Belt +12aefea5-2ec9-4181-93c3-2c739f7c8d64,Ghana Cocoa Belt +15a3e7a4-5ebd-45d0-a072-d78d2b5f7ac6,Greater Rift Valley of Kenya +24e290f1-5b13-4ca8-acce-43dcc8e5bb43,Ghana Cocoa Belt +25ebba5b-fa08-41de-b770-ae4cb675436d,Lake Kivu & Rusizi River Basin +026f20bc-4032-46b3-a8e9-61f524c6c17a,Lake Kivu & Rusizi River Basin +31e1f9bc-de91-486d-927f-db6d14d03d52,Lake Kivu & Rusizi River Basin +34bfb500-c388-47f6-9843-1b4dcf7ac70f,Lake Kivu & Rusizi River Basin +44c352fb-0581-4b97-92d7-6cbe459e13d0,Greater Rift Valley of Kenya +55c9c722-3aa8-45b8-bd97-36805c3e389f,Greater Rift Valley of Kenya +77fc2c03-5605-45b3-b417-f69d93157215,Greater Rift Valley of Kenya +82dd3a84-2562-4a6f-85d9-f83790daaaba,Greater Rift Valley of Kenya +88f66be9-0df0-41e1-b3ff-20426b558787,Lake Kivu & Rusizi River Basin +90d13b5d-efb6-40b5-8f25-777f69e18d5e,Greater Rift Valley of Kenya +94be995a-7465-444b-96af-15462ab805df,Lake Kivu & Rusizi River Basin +121c1d61-6f1c-4d93-a6ab-87649cd81fc4,Lake Kivu & Rusizi River Basin +146b6912-62a1-4b58-b027-466dc3295731,Greater Rift Valley of Kenya +179b90da-19b2-4103-9eb5-54e47378f100,Lake Kivu & Rusizi River Basin +213c1946-d7f8-4c03-95c3-5774dab48254,Ghana Cocoa Belt +243f93d2-0d4b-4dac-8b23-997e6528dc8e,Ghana Cocoa Belt +265b9b72-95ac-42dd-ab49-cc335bb90e40,Lake Kivu & Rusizi River Basin +385e647c-0b37-42e2-a7ee-b8c8fb454de1,Greater Rift Valley of Kenya +394e4de8-7b45-4834-a467-241ae4a16ece,Ghana Cocoa Belt +529e1bae-2187-473f-a2a3-17e577720aba,Ghana Cocoa Belt +562fa859-5124-49a5-947f-e1ddb7680e07,Greater Rift Valley of Kenya +568dc331-b945-41fd-ab57-80a98be57941,Greater Rift Valley of Kenya +576c1769-ed59-406d-ae6f-d70b136d028c,Greater Rift Valley of Kenya +697bf076-1e8c-4782-9245-a0da519759ea,Ghana Cocoa Belt +744d9613-02a4-4484-be15-0d6a401f0086,Greater Rift Valley of Kenya +921e8a4e-db12-4231-b474-69030cd6513d,Lake Kivu & Rusizi River Basin +943bb150-f1b7-4ad2-bb9e-60a559df2ebd,Ghana Cocoa Belt +996f3017-cc38-44c4-832b-eccde0903881,Lake Kivu & Rusizi River Basin +02031c35-79db-44b2-8a37-a536d38fea34,Lake Kivu & Rusizi River Basin +3332a66e-625a-4f37-8891-80c9b57e2e07,Greater Rift Valley of Kenya +20245af8-de10-46d8-8895-1bfa022932ce,Lake Kivu & Rusizi River Basin +30812e57-54e1-4bde-af22-8856baee51d9,Ghana Cocoa Belt +38315af3-3373-4478-8fca-532f38fc21f7,Lake Kivu & Rusizi River Basin +47118e50-d4d1-4ba0-8094-59cfa441dbb0,Ghana Cocoa Belt +76374ef9-c5df-4d62-a28b-92b100b95581,Greater Rift Valley of Kenya +151735a0-0855-4bf0-9c76-55a91fd169f4,Greater Rift Valley of Kenya +166192d1-d194-416d-abb7-132877462359,Lake Kivu & Rusizi River Basin +476273a3-d0b3-4f89-be10-9703f038d0c0,Ghana Cocoa Belt +617601e0-9839-49fd-b48e-6c07404e7140,Ghana Cocoa Belt +7414340a-986e-4d5f-a614-249f91c3b9d0,Ghana Cocoa Belt +94429259-36eb-4349-9390-6f45e899d0a2,Lake Kivu & Rusizi River Basin +a5e428be-71a2-44f8-8a47-08141b0b259c,Greater Rift Valley of Kenya +a43d00e0-4a5e-44ed-a2b4-f6ef0a0f729c,Greater Rift Valley of Kenya +a727d737-370f-4ac0-889c-88b7b190e4ff,Ghana Cocoa Belt +abdb9d09-7c55-4e26-8961-1aa26e991bbc,Ghana Cocoa Belt +ac5da8e9-46e9-428e-a07f-1e0baf2ff3ed,Greater Rift Valley of Kenya +ac097899-437a-42c5-9b67-b70dd91c231e,Ghana Cocoa Belt +ae90b323-03df-4fb1-bd91-6f05f5236922,Greater Rift Valley of Kenya +b6b9762b-093c-474a-9fbc-3b36feb44a94,Ghana Cocoa Belt +bbd1852f-1c58-4a05-8aa9-8d41b5fe4bbc,Lake Kivu & Rusizi River Basin +bf838a8a-ab07-44c1-8f63-3d929782414a,Greater Rift Valley of Kenya +c30a5f50-2dbf-43d5-8f3d-d5eb7ce2d445,Lake Kivu & Rusizi River Basin +c76fb8a0-d9ff-4856-b20e-7acd6c29dd72,Ghana Cocoa Belt +c325f8e1-a806-468a-8d24-d2b62e3ea888,Greater Rift Valley of Kenya +c7248e59-5a41-4b76-a1a2-d2db8ffc9247,Greater Rift Valley of Kenya +c462918b-47f7-4ed5-99e0-7fec6e342036,Greater Rift Valley of Kenya +c528071b-053c-4291-96b9-688c2ebe12aa,Greater Rift Valley of Kenya +cb8c8dad-43a4-42fa-a262-b9244d23cca8,Greater Rift Valley of Kenya +cd46fa33-a5c1-40b4-a9ca-4793b6248157,Lake Kivu & Rusizi River Basin +cd688e07-0fc0-4c1f-9bef-5ce8063ed674,Greater Rift Valley of Kenya +cdbf9bf1-d088-4664-a98f-8c684b36e5d3,Greater Rift Valley of Kenya +cf16b937-a02b-4691-b816-28669ec348f2,Greater Rift Valley of Kenya +d2c7ba9c-ec6d-47eb-9071-9cd1bbcb8b80,Greater Rift Valley of Kenya +d4c495f1-67d4-49c7-96bc-e38e3b35f263,Lake Kivu & Rusizi River Basin +d537cd2c-dfb2-4a36-a8ef-1f2e7bc705b3,Greater Rift Valley of Kenya +d795ccb8-d9e1-4075-aa2d-c2ee772d72cb,Lake Kivu & Rusizi River Basin +d3899fa2-d478-4b9a-ae86-8c58f5db8910,Lake Kivu & Rusizi River Basin +d8379e7d-8e59-4aba-b9a6-c47d4ff52494,Lake Kivu & Rusizi River Basin +d6481438-9603-4c68-b152-6586ed825b0a,Greater Rift Valley of Kenya +de890c8e-1707-4b69-aa21-7fc551ef7a6a,Ghana Cocoa Belt +e3f222fd-3c0e-4f5a-a4b0-9702f5973f6f,Lake Kivu & Rusizi River Basin +e3f2727f-1d76-4f70-a3c7-a6fcc181918a,Lake Kivu & Rusizi River Basin +e4a9f60c-e555-43ff-b428-5b13f6501d66,Greater Rift Valley of Kenya +e4fe2fa4-6869-4c1e-9347-ba9b135306f5,Greater Rift Valley of Kenya +e32012a6-4bd7-4dfb-87bd-51ea008bab0d,Lake Kivu & Rusizi River Basin +e280730f-181f-48f5-a037-288c47a6db0a,Greater Rift Valley of Kenya +eb56f448-703a-4683-b3fd-54d18b649202,Greater Rift Valley of Kenya +ec683761-c69c-45ee-bcfa-844f6845fd53,Lake Kivu & Rusizi River Basin +f04c60d8-9cdf-49e0-a100-086cd95ce7e1,Lake Kivu & Rusizi River Basin +f143ce09-b00e-4179-83f7-706b149bf544,Lake Kivu & Rusizi River Basin +f494ae8f-e1dd-486f-968f-da177c192562,Lake Kivu & Rusizi River Basin +f35188cb-b753-44f1-bb6f-dd5dd33da141,Greater Rift Valley of Kenya +fd1c50d3-1bd9-454a-bd81-8493b9ab2e84,Ghana Cocoa Belt From 5f3204eaab57841688f85ef23332b2837591a6f0 Mon Sep 17 00:00:00 2001 From: Limber Mamani <154026979+LimberHope@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:19:38 -0400 Subject: [PATCH 49/64] [TM-1479] finalize scripts and clarify questions (#522) * [TM-1479] changes columns to table indicators * changes indicator_id to indicator_slug in controller --- .../Controllers/V2/Indicators/GetHectaresRestoredController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/V2/Indicators/GetHectaresRestoredController.php b/app/Http/Controllers/V2/Indicators/GetHectaresRestoredController.php index 1727aa360..e4c6a93d0 100644 --- a/app/Http/Controllers/V2/Indicators/GetHectaresRestoredController.php +++ b/app/Http/Controllers/V2/Indicators/GetHectaresRestoredController.php @@ -58,7 +58,7 @@ public function polygonToOutputHectares($indicatorId, $polygonsUuids) return DB::select(' SELECT * FROM indicator_output_hectares - WHERE indicator_id = ? + WHERE indicator_slug = ? AND polygon_id IN (' . implode(',', array_fill(0, count($polygonsUuids), '?')) . ') ', array_merge([$indicatorId], $polygonsUuids)); } From a02c8f05524fb89980a0ad7052aaffc5d2302e01 Mon Sep 17 00:00:00 2001 From: Jorge Monroy Date: Mon, 28 Oct 2024 17:46:57 -0400 Subject: [PATCH 50/64] [TM-1409] add multipolygon validation (#523) --- app/Validators/Extensions/Polygons/GeometryType.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/Validators/Extensions/Polygons/GeometryType.php b/app/Validators/Extensions/Polygons/GeometryType.php index f3361124b..899783bc7 100644 --- a/app/Validators/Extensions/Polygons/GeometryType.php +++ b/app/Validators/Extensions/Polygons/GeometryType.php @@ -12,10 +12,11 @@ class GeometryType extends Extension public static $message = [ 'key' => 'GEOMETRY_TYPE', - 'message' => 'The geometry must by of polygon type', + 'message' => 'The geometry must be of polygon or multipolygon type', ]; - public const VALID_TYPE = 'POLYGON'; + public const VALID_TYPE_POLYGON = 'POLYGON'; + public const VALID_TYPE_MULTIPOLYGON = 'MULTIPOLYGON'; public static function passes($attribute, $value, $parameters, $validator): bool { @@ -32,7 +33,7 @@ public static function uuidValid($uuid): bool { $geometryType = PolygonGeometry::getGeometryType($uuid); - return $geometryType === self::VALID_TYPE; + return $geometryType === self::VALID_TYPE_POLYGON || $geometryType === self::VALID_TYPE_MULTIPOLYGON; } public static function geoJsonValid($geojson): bool @@ -42,6 +43,6 @@ public static function geoJsonValid($geojson): bool ['geojson' => json_encode($geojson)] ); - return $result->geometry_type === self::VALID_TYPE; + return $result->geometry_type === self::VALID_TYPE_POLYGON || $result->geometry_type === self::VALID_TYPE_MULTIPOLYGON; } } From a64a4a4546999469ba78367d1fb93ba549c37110 Mon Sep 17 00:00:00 2001 From: cesarLima1 <105736261+cesarLima1@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:54:50 -0400 Subject: [PATCH 51/64] [TM-1404] avoid using duplicated iso countries (#524) --- .../seeders/WorldCountriesGeneralizedTableSeeder.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/database/seeders/WorldCountriesGeneralizedTableSeeder.php b/database/seeders/WorldCountriesGeneralizedTableSeeder.php index 3ceeb7270..c55225e94 100644 --- a/database/seeders/WorldCountriesGeneralizedTableSeeder.php +++ b/database/seeders/WorldCountriesGeneralizedTableSeeder.php @@ -137,9 +137,9 @@ public function run() 'OGR_FID' => 267, 'geometry' => '', 'country' => 'Azores', - 'iso' => 'PT', + 'iso' => '', 'countryaff' => 'Portugal', - 'aff_iso' => 'PT', + 'aff_iso' => '', ], [ 'OGR_FID' => 268, @@ -353,9 +353,9 @@ public function run() 'OGR_FID' => 294, 'geometry' => '', 'country' => 'Canarias', - 'iso' => 'ES', + 'iso' => '', 'countryaff' => 'Spain', - 'aff_iso' => 'ES', + 'aff_iso' => '', ], [ 'OGR_FID' => 295, @@ -1074,9 +1074,9 @@ public function run() 'OGR_FID' => 384, 'geometry' => '', 'country' => 'Madeira', - 'iso' => 'PT', + 'iso' => '', 'countryaff' => 'Portugal', - 'aff_iso' => 'PT', + 'aff_iso' => '', ], [ 'OGR_FID' => 385, From 818e28b95282738d48c4b0b44eeecd0fe3d57d50 Mon Sep 17 00:00:00 2001 From: Jorge Monroy Date: Mon, 28 Oct 2024 18:12:07 -0400 Subject: [PATCH 52/64] [TM-1409] add valid types (#525) --- .../V2/Terrafund/TerrafundCreateGeometryController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index aed9f85cc..b7441aef0 100755 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -520,7 +520,7 @@ public function getGeometryType(Request $request) $geometryType = PolygonGeometry::getGeometryType($uuid); if ($geometryType) { - $valid = $geometryType === GeometryType::VALID_TYPE; + $valid = $geometryType === GeometryType::VALID_TYPE_POLYGON || $geometryType === GeometryType::VALID_TYPE_MULTIPOLYGON; $insertionSuccess = App::make(PolygonService::class) ->createCriteriaSite($uuid, PolygonService::GEOMETRY_TYPE_CRITERIA_ID, $valid); From 06021ee23d5373ae0ce3c4e11e5dc956787029d2 Mon Sep 17 00:00:00 2001 From: JORGE Date: Tue, 29 Oct 2024 11:27:18 -0400 Subject: [PATCH 53/64] [TM-1127] restore value to test --- app/Services/PolygonService.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/Services/PolygonService.php b/app/Services/PolygonService.php index a8b71eff9..0d76c34da 100755 --- a/app/Services/PolygonService.php +++ b/app/Services/PolygonService.php @@ -330,8 +330,10 @@ protected function insertSitePolygon(string $polygonUuid, array $properties) $geometryHelper->updateProjectCentroid($project->uuid); return null; - } catch (\Exception $e) { + } catch (\Exception $e) { + if (! $sitePolygon) { throw new \Exception('SitePolygon not found for site_id: ' . $properties['site_id']); + } } } From 7e5b7b43018ad13dfd1ce810de8688fc01291c66 Mon Sep 17 00:00:00 2001 From: JORGE Date: Tue, 29 Oct 2024 11:42:56 -0400 Subject: [PATCH 54/64] [TM-1127] return error in catch --- app/Services/PolygonService.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/Services/PolygonService.php b/app/Services/PolygonService.php index 0d76c34da..c1745814c 100755 --- a/app/Services/PolygonService.php +++ b/app/Services/PolygonService.php @@ -331,9 +331,7 @@ protected function insertSitePolygon(string $polygonUuid, array $properties) return null; } catch (\Exception $e) { - if (! $sitePolygon) { - throw new \Exception('SitePolygon not found for site_id: ' . $properties['site_id']); - } + return response()->json(['error' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); } } From 46cad7c66fa3e5c218e2a37b7cd3f80d73f1b527 Mon Sep 17 00:00:00 2001 From: JORGE Date: Tue, 29 Oct 2024 11:49:23 -0400 Subject: [PATCH 55/64] [TM-1127] lint fix --- app/Services/PolygonService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Services/PolygonService.php b/app/Services/PolygonService.php index c1745814c..ac80dd04d 100755 --- a/app/Services/PolygonService.php +++ b/app/Services/PolygonService.php @@ -330,8 +330,8 @@ protected function insertSitePolygon(string $polygonUuid, array $properties) $geometryHelper->updateProjectCentroid($project->uuid); return null; - } catch (\Exception $e) { - return response()->json(['error' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); + } catch (\Exception $e) { + return response()->json(['error' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); } } From 7949aa3ace644c21bfb15b6784bd138f21710dc2 Mon Sep 17 00:00:00 2001 From: Nathan Curtis Date: Tue, 29 Oct 2024 12:01:46 -0700 Subject: [PATCH 56/64] [TM-1411] Fix code for finding an existing demographic. --- app/Models/V2/Workdays/Workday.php | 10 +++++----- tests/Unit/Models/V2/Workdays/WorkdayTest.php | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/Models/V2/Workdays/Workday.php b/app/Models/V2/Workdays/Workday.php index da36f0fc8..c7dd00a48 100644 --- a/app/Models/V2/Workdays/Workday.php +++ b/app/Models/V2/Workdays/Workday.php @@ -151,11 +151,11 @@ public static function syncRelation(EntityModel $entity, string $property, $data $demographics = $workday->demographics; $represented = collect(); foreach ($syncData as $row) { - $demographic = $demographics->firstWhere([ - 'type' => data_get($row, 'type'), - 'subtype' => data_get($row, 'subtype'), - 'name' => data_get($row, 'name'), - ]); + $demographic = $demographics->first(fn ($dbRow) => + data_get($dbRow, 'type') == data_get($row, 'type') && + data_get($dbRow, 'subtype') == data_get($row, 'subtype') && + data_get($dbRow, 'name') == data_get($row, 'name') + ); if ($demographic == null) { $workday->demographics()->create($row); diff --git a/tests/Unit/Models/V2/Workdays/WorkdayTest.php b/tests/Unit/Models/V2/Workdays/WorkdayTest.php index 32736efee..3f266f157 100644 --- a/tests/Unit/Models/V2/Workdays/WorkdayTest.php +++ b/tests/Unit/Models/V2/Workdays/WorkdayTest.php @@ -68,9 +68,11 @@ public function test_sync_relation() ]; $siteReport = SiteReport::factory()->create(); Workday::syncRelation($siteReport, 'workdaysVolunteerPlanting', $data, false); + Workday::syncRelation($siteReport, 'workdaysVolunteerPlanting', $data, false); $workday = $siteReport->workdaysVolunteerPlanting()->first(); $this->assertEquals(3, $workday->demographics()->count()); + $this->assertEquals(3, $workday->demographics()->withTrashed()->count()); $this->assertEquals(40, $workday->demographics()->isAge('youth')->first()->amount); $this->assertEquals(40, $workday->demographics()->isGender('non-binary')->first()->amount); $this->assertEquals(40, $workday->demographics()->isEthnicity('other')->first()->amount); From 146c11124033dc626c45b8e9e1c8a5b41c96011b Mon Sep 17 00:00:00 2001 From: cesarLima1 <105736261+cesarLima1@users.noreply.github.com> Date: Tue, 29 Oct 2024 15:11:33 -0400 Subject: [PATCH 57/64] [TM-1407] add organization in response for top trees planted controller (#527) --- .../V2/Dashboard/TopProjectsAndTopTreeSpeciesController.php | 1 + openapi-src/V2/definitions/DashboardTopPlantedTree.yml | 2 ++ resources/docs/swagger-v2.yml | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/app/Http/Controllers/V2/Dashboard/TopProjectsAndTopTreeSpeciesController.php b/app/Http/Controllers/V2/Dashboard/TopProjectsAndTopTreeSpeciesController.php index 826e67b99..22ad90124 100644 --- a/app/Http/Controllers/V2/Dashboard/TopProjectsAndTopTreeSpeciesController.php +++ b/app/Http/Controllers/V2/Dashboard/TopProjectsAndTopTreeSpeciesController.php @@ -33,6 +33,7 @@ public function getTopProjects($projects) $totalSpeciesAmountForSiteReport = $project->trees_planted_count; $topProjects[] = [ + 'organization' => $project->organisation->name, 'project' => $project->name, 'uuid' => $project->uuid, 'trees_planted' => $totalSpeciesAmountForSiteReport, diff --git a/openapi-src/V2/definitions/DashboardTopPlantedTree.yml b/openapi-src/V2/definitions/DashboardTopPlantedTree.yml index 2f6e51418..dd54b3d3c 100644 --- a/openapi-src/V2/definitions/DashboardTopPlantedTree.yml +++ b/openapi-src/V2/definitions/DashboardTopPlantedTree.yml @@ -1,5 +1,7 @@ type: object properties: + organization: + type: string project: type: string uuid: diff --git a/resources/docs/swagger-v2.yml b/resources/docs/swagger-v2.yml index 74bc890bf..e572131b0 100644 --- a/resources/docs/swagger-v2.yml +++ b/resources/docs/swagger-v2.yml @@ -43473,6 +43473,8 @@ definitions: items: type: object properties: + organization: + type: string project: type: string uuid: @@ -43491,6 +43493,8 @@ definitions: DashboardTopPlantedTree: type: object properties: + organization: + type: string project: type: string uuid: @@ -94685,6 +94689,8 @@ paths: items: type: object properties: + organization: + type: string project: type: string uuid: From 07365029740d105b5c4743407f0b73e2764d198d Mon Sep 17 00:00:00 2001 From: Nathan Curtis Date: Tue, 29 Oct 2024 12:34:21 -0700 Subject: [PATCH 58/64] [TM-1411] Combat duplicate demographics caused by race conditions. --- app/Models/V2/Workdays/Workday.php | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/app/Models/V2/Workdays/Workday.php b/app/Models/V2/Workdays/Workday.php index c7dd00a48..108dc4531 100644 --- a/app/Models/V2/Workdays/Workday.php +++ b/app/Models/V2/Workdays/Workday.php @@ -148,28 +148,27 @@ public static function syncRelation(EntityModel $entity, string $property, $data return $syncData; }, []) : []; - $demographics = $workday->demographics; $represented = collect(); foreach ($syncData as $row) { - $demographic = $demographics->first(fn ($dbRow) => - data_get($dbRow, 'type') == data_get($row, 'type') && - data_get($dbRow, 'subtype') == data_get($row, 'subtype') && - data_get($dbRow, 'name') == data_get($row, 'name') - ); + // It's not great to fetch these individually, but this does help combat the possibility of a race condition + // from two HTTP threads (potentially on different servers) processing an update to this workday at the + // same time. + $demographic = $workday->demographics()->where([ + 'type' => data_get($row, 'type'), + 'subtype' => data_get($row, 'subtype'), + 'name' => data_get($row, 'name'), + ])->first(); if ($demographic == null) { - $workday->demographics()->create($row); + $represented->push($workday->demographics()->create($row)->id); } else { $represented->push($demographic->id); $demographic->update(['amount' => data_get($row, 'amount')]); } } + // Remove any existing demographic that wasn't in the submitted set. - foreach ($demographics as $demographic) { - if (! $represented->contains($demographic->id)) { - $demographic->delete(); - } - } + $workday->demographics()->whereNotIn('id', $represented)->delete(); } public function workdayable() From ff4a7648edc68114f412f431f058691dc24568b2 Mon Sep 17 00:00:00 2001 From: Nathan Curtis Date: Tue, 29 Oct 2024 12:55:58 -0700 Subject: [PATCH 59/64] [TM-1411] Make fix script resilient to soft deleted records. --- app/Console/Commands/OneOff/FixDuplicateDemographics.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Console/Commands/OneOff/FixDuplicateDemographics.php b/app/Console/Commands/OneOff/FixDuplicateDemographics.php index 4dbf3e1ed..c2218e85d 100644 --- a/app/Console/Commands/OneOff/FixDuplicateDemographics.php +++ b/app/Console/Commands/OneOff/FixDuplicateDemographics.php @@ -54,7 +54,7 @@ public function handle() foreach ($workdays as $workdayId => $stats) { foreach ($stats as $stat) { - $workday = Workday::find($workdayId); + $workday = Workday::withTrashed()->find($workdayId); $rows = collect($stat['rows']); $max = $rows->max('amount'); @@ -79,7 +79,7 @@ public function handle() 'workday_id' => $workdayId, 'workdayable_type' => $workday->workdayable_type, 'workdayable_id' => $workday->workdayable_id, - 'workdayable_uuid' => $workday->workdayable->uuid, + 'workdayable_uuid' => $workday->workdayable()->withTrashed()->first()->uuid, 'collection' => $workday->collection, 'type' => $stat['type'], 'subtype' => $stat['subtype'], From 149f6c634d4bd7b6154240235a23f55492eed0c2 Mon Sep 17 00:00:00 2001 From: Jose Carlos Laura Ramirez Date: Tue, 29 Oct 2024 15:57:08 -0400 Subject: [PATCH 60/64] [TM-1353] Add in logic for the generation of HBF and TerraFund Enterprise Reporting Tasks (#526) * [TM-1353] add OneOff command to seed hbf and enterprises reports * remove imports * update hours for to account for tz displacement * update name * add one-off command for backdated reports * fix typo --- ...rateBackDatedReportsEnterprisesCommand.php | 110 ++++++++++++++++++ ...SeedScheduledJobsHbfEnterprisesCommand.php | 59 ++++++++++ 2 files changed, 169 insertions(+) create mode 100644 app/Console/Commands/OneOff/GenerateBackDatedReportsEnterprisesCommand.php create mode 100644 app/Console/Commands/OneOff/SeedScheduledJobsHbfEnterprisesCommand.php diff --git a/app/Console/Commands/OneOff/GenerateBackDatedReportsEnterprisesCommand.php b/app/Console/Commands/OneOff/GenerateBackDatedReportsEnterprisesCommand.php new file mode 100644 index 000000000..edab07f9d --- /dev/null +++ b/app/Console/Commands/OneOff/GenerateBackDatedReportsEnterprisesCommand.php @@ -0,0 +1,110 @@ +startOfMonth()->setDay(30)->setHours(5); + $period_key = $due_at->year . '-' . $due_at->month; + $framework_key = 'enterprises'; + Project::where('framework_key', $framework_key) + ->chunkById(100, function ($projects) use ($framework_key, $period_key, $due_at) { + foreach ($projects as $project) { + $this->createTask($project, $framework_key, $period_key, $due_at); + } + }); + } + + public function createTask($project, $framework_key, $period_key, $due_at) + { + $task = Task::create([ + 'organisation_id' => $project->organisation_id, + 'project_id' => $project->id, + 'status' => TaskStatusStateMachine::DUE, + 'period_key' => $period_key, + 'due_at' => $due_at, + ]); + + $projectReport = $task->projectReport()->create([ + 'framework_key' => $framework_key, + 'project_id' => $project->id, + 'status' => ReportStatusStateMachine::DUE, + 'due_at' => $due_at, + ]); + + $hasSite = false; + foreach ($project->sites as $site) { + $hasSite = true; + $task->siteReports()->create([ + 'framework_key' => $framework_key, + 'site_id' => $site->id, + 'status' => ReportStatusStateMachine::DUE, + 'due_at' => $due_at, + ]); + } + + $hasNursery = false; + foreach ($project->nurseries as $nursery) { + $hasNursery = true; + $task->nurseryReports()->create([ + 'framework_key' => $framework_key, + 'nursery_id' => $nursery->id, + 'status' => ReportStatusStateMachine::DUE, + 'due_at' => $due_at, + ]); + } + + $labels = ['Project']; + if ($hasSite) { + $labels[] = 'site'; + } + if ($hasNursery) { + $labels[] = 'nursery'; + } + $message = printf( + '%s %s available', + implode(', ', $labels), + count($labels) > 1 ? 'reports' : 'report' + ); + + Action::create([ + 'status' => Action::STATUS_PENDING, + 'targetable_type' => ProjectReport::class, + 'targetable_id' => $projectReport->id, + 'type' => Action::TYPE_NOTIFICATION, + 'title' => 'Project report', + 'sub_title' => '', + 'text' => $message, + 'project_id' => $project->id, + 'organisation_id' => $project->organisation_id, + ]); + } +} diff --git a/app/Console/Commands/OneOff/SeedScheduledJobsHbfEnterprisesCommand.php b/app/Console/Commands/OneOff/SeedScheduledJobsHbfEnterprisesCommand.php new file mode 100644 index 000000000..fb4d312bb --- /dev/null +++ b/app/Console/Commands/OneOff/SeedScheduledJobsHbfEnterprisesCommand.php @@ -0,0 +1,59 @@ +startOfMonth()->setDay(1), + 'hbf', + Carbon::createFromFormat('m', 12)->startOfMonth()->setDay(1)->setHours(5), + ); + // November-April, May 1, June 1 + TaskDueJob::createTaskDue( + Carbon::createFromFormat('m', 5)->startOfMonth()->setDay(1), + 'hbf', + Carbon::createFromFormat('m', 6)->startOfMonth()->setDay(3)->setHours(5), + ); + + // Enterprises reports + // January-June, July 1, July 30 + TaskDueJob::createTaskDue( + Carbon::createFromFormat('m', 7)->startOfMonth()->setDay(1), + 'enterprises', + Carbon::createFromFormat('m', 7)->startOfMonth()->setDay(30)->setHours(5), + ); + // July-December, January 1, January 30 + TaskDueJob::createTaskDue( + Carbon::createFromFormat('m', 1)->startOfMonth()->setDay(1), + 'enterprises', + Carbon::createFromFormat('m', 1)->startOfMonth()->setDay(30)->setHours(5), + ); + + } +} From 45bd877a5b7fb9102ebd572a024ee13da27ceb42 Mon Sep 17 00:00:00 2001 From: Limber Mamani <154026979+LimberHope@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:10:08 -0400 Subject: [PATCH 61/64] [TM-1379] finalize scripts and clarify questions (#530) * [TM-1479] changes columns to table indicators * changes indicator_id to indicator_slug in controller * [TM-1379] change indicator_slug values --- .../V2/Indicators/GetHectaresRestoredController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/V2/Indicators/GetHectaresRestoredController.php b/app/Http/Controllers/V2/Indicators/GetHectaresRestoredController.php index e4c6a93d0..0408bbe01 100644 --- a/app/Http/Controllers/V2/Indicators/GetHectaresRestoredController.php +++ b/app/Http/Controllers/V2/Indicators/GetHectaresRestoredController.php @@ -14,8 +14,8 @@ public function __invoke(Request $request) { try { $projectsToQuery = TerrafundDashboardQueryHelper::buildQueryFromRequest($request)->pluck('uuid')->toArray(); - $HECTAREAS_BY_RESTORATION = '5'; - $HECTAREAS_BY_TARGET_LAND_USE_TYPES = '6'; + $HECTAREAS_BY_RESTORATION = 'restorationByStrategy'; + $HECTAREAS_BY_TARGET_LAND_USE_TYPES = 'restorationByLandUse'; $projectsPolygons = $this->getProjectsPolygons($projectsToQuery); $polygonsUuids = array_column($projectsPolygons, 'uuid'); From 6af2187f1dc2b10c2725b1aed71c44e9c0bae21c Mon Sep 17 00:00:00 2001 From: JORGE Date: Tue, 29 Oct 2024 17:14:00 -0400 Subject: [PATCH 62/64] [TM-1127] store correct string message as json --- app/Jobs/RunSitePolygonsValidationJob.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Jobs/RunSitePolygonsValidationJob.php b/app/Jobs/RunSitePolygonsValidationJob.php index 88065fe11..96fe5e6f5 100644 --- a/app/Jobs/RunSitePolygonsValidationJob.php +++ b/app/Jobs/RunSitePolygonsValidationJob.php @@ -64,7 +64,7 @@ public function handle(PolygonValidationService $validationService) $delayedJob->update([ 'status' => DelayedJob::STATUS_SUCCEEDED, - 'payload' => 'Validation completed for all site polygons', + 'payload' => ['message' => 'Validation completed for all site polygons'], 'status_code' => Response::HTTP_OK, ]); From 32739d56e5cacb8c7d18666d5902165ec5e6621d Mon Sep 17 00:00:00 2001 From: cesarLima1 <105736261+cesarLima1@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:24:24 -0400 Subject: [PATCH 63/64] [TM-1397] start up command to delete dummy buffered polygons of a site (#532) --- .../Commands/DeleteDummyBufferedPolygons.php | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 app/Console/Commands/DeleteDummyBufferedPolygons.php diff --git a/app/Console/Commands/DeleteDummyBufferedPolygons.php b/app/Console/Commands/DeleteDummyBufferedPolygons.php new file mode 100644 index 000000000..0ec62c325 --- /dev/null +++ b/app/Console/Commands/DeleteDummyBufferedPolygons.php @@ -0,0 +1,61 @@ +argument('siteId'); + + DB::beginTransaction(); + + try { + + $sitePolygons = SitePolygon::where('site_id', $siteId)->get(); + + foreach ($sitePolygons as $sitePolygon) { + $polygonGeometry = PolygonGeometry::where('uuid', $sitePolygon->poly_id)->first(); + if ($polygonGeometry) { + $polygonGeometry->deleteWithRelated(); + } + } + + DB::commit(); + + $this->info("Dummy buffered polygons for site ID $siteId have been successfully deleted."); + + } catch (\Exception $e) { + DB::rollBack(); + $this->error('Failed to delete dummy buffered polygons: ' . $e->getMessage()); + + return Command::FAILURE; + } + + return Command::SUCCESS; + } +} From d122125754a34152fc235e06d56c4930d30d88ae Mon Sep 17 00:00:00 2001 From: Jose Carlos Laura Ramirez Date: Fri, 1 Nov 2024 08:58:01 -0400 Subject: [PATCH 64/64] [TM-1434] add endpoint for downloading reports as s3 presigned urls (#538) --- ...tePreSignedURLDownloadReportController.php | 21 +++++++++++++ ...2-admin-entity-presigned-url-framework.yml | 29 +++++++++++++++++ openapi-src/V2/paths/_index.yml | 3 ++ resources/docs/swagger-v2.yml | 31 +++++++++++++++++++ routes/api_v2.php | 2 ++ 5 files changed, 86 insertions(+) create mode 100644 app/Http/Controllers/V2/Exports/GeneratePreSignedURLDownloadReportController.php create mode 100644 openapi-src/V2/paths/Exports/get-v2-admin-entity-presigned-url-framework.yml diff --git a/app/Http/Controllers/V2/Exports/GeneratePreSignedURLDownloadReportController.php b/app/Http/Controllers/V2/Exports/GeneratePreSignedURLDownloadReportController.php new file mode 100644 index 000000000..78d7c6119 --- /dev/null +++ b/app/Http/Controllers/V2/Exports/GeneratePreSignedURLDownloadReportController.php @@ -0,0 +1,21 @@ +addMinutes(60); + + $presignedUrl = Storage::disk('s3')->temporaryUrl($fileKey, $expiration); + + return response()->json(['url' => $presignedUrl]); + } +} diff --git a/openapi-src/V2/paths/Exports/get-v2-admin-entity-presigned-url-framework.yml b/openapi-src/V2/paths/Exports/get-v2-admin-entity-presigned-url-framework.yml new file mode 100644 index 000000000..e070efe95 --- /dev/null +++ b/openapi-src/V2/paths/Exports/get-v2-admin-entity-presigned-url-framework.yml @@ -0,0 +1,29 @@ +operationId: get-v2-admin-entity-presigned-url-framework.yml +summary: Export entities data +tags: + - V2 Projects + - V2 Sites + - V2 Nurseries + - V2 Project Reports + - V2 Site Reports + - V2 Nursery Reports + - Exports +parameters: + - type: string + name: ENTITY + in: path + required: true + description: allowed values projects/sites/nurseries/project-reports/site-reports/nursery-reports + - type: string + name: FRAMEWORK + in: path + required: true + description: allowed values terrafund/ppc +responses: + '200': + description: OK + schema: + type: object + properties: + url: + type: string diff --git a/openapi-src/V2/paths/_index.yml b/openapi-src/V2/paths/_index.yml index 110a1efe4..e5d57886a 100644 --- a/openapi-src/V2/paths/_index.yml +++ b/openapi-src/V2/paths/_index.yml @@ -2530,6 +2530,9 @@ /v2/admin/{ENTITY}/export/{FRAMEWORK}: get: $ref: './Exports/get-v2-admin-entity-export-framework.yml' +/v2/admin/{ENTITY}/presigned-url/{FRAMEWORK}: + get: + $ref: './Exports/get-v2-admin-entity-presigned-url-framework.yml' /v2/projects/{UUID}/{ENTITY}/export: get: $ref: './Exports/get-v2-projects-uuid-entity-export.yml' diff --git a/resources/docs/swagger-v2.yml b/resources/docs/swagger-v2.yml index e572131b0..d6af0f57b 100644 --- a/resources/docs/swagger-v2.yml +++ b/resources/docs/swagger-v2.yml @@ -92018,6 +92018,37 @@ paths: description: OK schema: type: file + '/v2/admin/{ENTITY}/presigned-url/{FRAMEWORK}': + get: + operationId: get-v2-admin-entity-presigned-url-framework.yml + summary: Export entities data + tags: + - V2 Projects + - V2 Sites + - V2 Nurseries + - V2 Project Reports + - V2 Site Reports + - V2 Nursery Reports + - Exports + parameters: + - type: string + name: ENTITY + in: path + required: true + description: allowed values projects/sites/nurseries/project-reports/site-reports/nursery-reports + - type: string + name: FRAMEWORK + in: path + required: true + description: allowed values terrafund/ppc + responses: + '200': + description: OK + schema: + type: object + properties: + url: + type: string '/v2/projects/{UUID}/{ENTITY}/export': get: operationId: get-v2-projects-uuid-entity-export.yml diff --git a/routes/api_v2.php b/routes/api_v2.php index ec104451f..7b8fe846b 100644 --- a/routes/api_v2.php +++ b/routes/api_v2.php @@ -51,6 +51,7 @@ use App\Http\Controllers\V2\Exports\ExportImageController; use App\Http\Controllers\V2\Exports\ExportProjectEntityAsProjectDeveloperController; use App\Http\Controllers\V2\Exports\ExportReportEntityAsProjectDeveloperController; +use App\Http\Controllers\V2\Exports\GeneratePreSignedURLDownloadReportController; use App\Http\Controllers\V2\Files\FilePropertiesController; use App\Http\Controllers\V2\Files\Gallery\ViewNurseryGalleryController; use App\Http\Controllers\V2\Files\Gallery\ViewNurseryReportGalleryController; @@ -329,6 +330,7 @@ }); Route::get('/{entity}/export/{framework}', ExportAllMonitoredEntitiesController::class); + Route::get('/{entity}/presigned-url/{framework}', GeneratePreSignedURLDownloadReportController::class); ModelInterfaceBindingMiddleware::with(EntityModel::class, function () { Route::put('/{entity}/{status}', AdminStatusEntityController::class);