From 4caec5a3a18413156a6fc63474245324a66736de Mon Sep 17 00:00:00 2001 From: cesarLima1 Date: Tue, 28 May 2024 12:27:44 -0400 Subject: [PATCH 01/18] [TM-658] export dashboard --- .../ActiveProjectsTableController.php | 117 ++++++++++++++++++ .../Dashboard/ProjectListExportController.php | 63 ++++++++++ .../Dashboard/ActiveProjectsTableResource.php | 19 +++ 3 files changed, 199 insertions(+) create mode 100644 app/Http/Controllers/V2/Dashboard/ActiveProjectsTableController.php create mode 100644 app/Http/Controllers/V2/Dashboard/ProjectListExportController.php create mode 100644 app/Http/Resources/V2/Dashboard/ActiveProjectsTableResource.php diff --git a/app/Http/Controllers/V2/Dashboard/ActiveProjectsTableController.php b/app/Http/Controllers/V2/Dashboard/ActiveProjectsTableController.php new file mode 100644 index 000000000..71436c2f6 --- /dev/null +++ b/app/Http/Controllers/V2/Dashboard/ActiveProjectsTableController.php @@ -0,0 +1,117 @@ +input('per_page', PHP_INT_MAX); + $page = $request->input('page', 1); + + $projects = $this->getAllProjects($request, $perPage, $page); + $count = $this->getQuery($request)->count(); + + return new ActiveProjectsTableResource([ + 'current_page' => $page, + 'data' => $projects, + 'per_page' => $perPage, + 'last_page' => ceil($count / $perPage), + 'total' => $count, + ]); + } + + public function getQuery($request) + { + return TerrafundDashboardQueryHelper::buildQueryFromRequest($request) + ->with('organisation') + ->withCount(['sites', 'nurseries']); + } + + public function getAllProjects($request, $perPage, $page) + { + $query = $this->getQuery($request) + ->skip(($page - 1) * $perPage) + ->take($perPage); + + $projects = $query->get(); + + return $projects->map(function ($project) { + return [ + 'uuid' => $project->uuid, + 'name' => $project->name, + 'organisation' => $project->organisation->name, + 'trees_under_restoration' => $this->treesUnderRestoration($project), + 'jobs_created' => $this->jobsCreated($project), + 'volunteers' => $this->volunteers($project), + 'beneficiaries' => $this->beneficiaries($project), + 'survival_rate' => $project->survival_rate, + 'number_of_sites' => $project->sites_count, + 'number_of_nurseries' => $project->nurseries_count, + 'project_country' => $this->projectCountry($project->country), + 'country_slug' => $project->country, + 'number_of_trees_goal' => $project->trees_grown_goal, + 'date_added' => $project->created_at, + ]; + }); + } + + public function treesUnderRestoration($project) + { + return $project->trees_planted_count; + } + + public function jobsCreated($project) + { + $projectReport = $project->reports() + ->selectRaw('SUM(ft_total) as total_ft, SUM(pt_total) as total_pt') + ->groupBy('project_id') + ->first(); + + if ($projectReport) { + return $projectReport->total_ft + $projectReport->total_pt; + } else { + return 0; + } + } + + public function volunteers($project) + { + $totalVolunteers = $project->reports()->selectRaw('SUM(volunteer_total) as total')->first(); + + return $totalVolunteers ? intval($totalVolunteers->total) : 0; + } + + public function beneficiaries($project) + { + $totalBeneficiaries = $project->reports()->selectRaw('SUM(beneficiaries) as total')->first(); + + return $totalBeneficiaries ? intval($totalBeneficiaries->total) : 0; + } + + public function projectCountry($slug) + { + $countryId = FormOptionList::where('key', 'countries')->value('id'); + + return FormOptionListOption::where('form_option_list_id', $countryId) + ->where('slug', $slug) + ->value('label'); + } + + public function paginate($items, $perPage = 10, $page = null, $options = []) + { + $page = $page ?: (LengthAwarePaginator::resolveCurrentPage() ?: 1); + $items = $items instanceof Collection ? $items : Collection::make($items); + + return new LengthAwarePaginator($items->forPage($page, $perPage), $items->count(), $perPage, $page, $options); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/V2/Dashboard/ProjectListExportController.php b/app/Http/Controllers/V2/Dashboard/ProjectListExportController.php new file mode 100644 index 000000000..9d6d1611e --- /dev/null +++ b/app/Http/Controllers/V2/Dashboard/ProjectListExportController.php @@ -0,0 +1,63 @@ +exportCsv($request); + } + + public function exportCsv($request) + { + $activeProjectsController = new ActiveProjectsTableController(); + $perPage = $request->input('per_page', PHP_INT_MAX); + $page = $request->input('page', 1); + + $projects = $activeProjectsController->getAllProjects($request, $perPage, $page); + + $headers = [ + 'uuid' => 'UUID', + 'name' => 'Project Name', + 'organisation' => 'Organisation', + 'project_country' => 'Country', + 'number_of_trees_goal' => 'No. of Trees Goal', + 'trees_under_restoration' => 'No. of Trees Restored', + 'jobs_created' => 'No. of Jobs Created', + 'date_added' => 'Date Added', + 'number_of_sites' => 'No. of Sites', + 'number_of_nurseries' => 'No. of Nurseries', + ]; + + $filteredProjects = []; + foreach ($projects as $project) { + $filteredProject = []; + foreach ($headers as $key => $label) { + $filteredProject[$key] = $project[$key] ?? ''; + } + $filteredProjects[] = $filteredProject; + } + + $csv = Writer::createFromString(''); + + $csv->insertOne(array_values($headers)); + + foreach ($filteredProjects as $filteredProject) { + $csv->insertOne(array_values($filteredProject)); + } + + $csvContent = $csv->toString(); + + $fileName = 'activeProject.csv'; + + return response($csvContent, 200, [ + 'Content-Type' => 'text/csv', + 'Content-Disposition' => 'attachment; filename="' . $fileName . '"', + ]); + } +} diff --git a/app/Http/Resources/V2/Dashboard/ActiveProjectsTableResource.php b/app/Http/Resources/V2/Dashboard/ActiveProjectsTableResource.php new file mode 100644 index 000000000..5fd0250ac --- /dev/null +++ b/app/Http/Resources/V2/Dashboard/ActiveProjectsTableResource.php @@ -0,0 +1,19 @@ +resource; + } +} From 81495775d8716eb3ef9c5bf53cc4937ed78ce68c Mon Sep 17 00:00:00 2001 From: cesarLima1 Date: Tue, 28 May 2024 12:29:05 -0400 Subject: [PATCH 02/18] [TM-626] start up restoration strategies controller --- .../ViewRestorationStrategyController.php | 132 ++++++++++++++++++ .../ViewRestorationStrategyResource.php | 23 +++ 2 files changed, 155 insertions(+) create mode 100644 app/Http/Controllers/V2/Dashboard/ViewRestorationStrategyController.php create mode 100644 app/Http/Resources/V2/Dashboard/ViewRestorationStrategyResource.php diff --git a/app/Http/Controllers/V2/Dashboard/ViewRestorationStrategyController.php b/app/Http/Controllers/V2/Dashboard/ViewRestorationStrategyController.php new file mode 100644 index 000000000..40aac1fca --- /dev/null +++ b/app/Http/Controllers/V2/Dashboard/ViewRestorationStrategyController.php @@ -0,0 +1,132 @@ +buildProjectQuery($request); + + $projectIds = $query->pluck('v2_projects.id')->toArray(); + + $restorationStrategy = $this->getRestorationStrategy($projectIds); + + $landUseType = $this->getLandUseType($projectIds); + + $landTenures = $this->getLandTenures($projectIds); + + $result = [ + 'restorationStrategies' => $this->getResultArray($restorationStrategy, 'strategy'), + 'landUseTypes' => $this->getResultArray($landUseType, 'land_use'), + 'landTenures' => $this->getResultArray($landTenures, 'land_tenure'), + ]; + + return new JsonResponse(ViewRestorationStrategyResource::make($result)); + } + + private function buildProjectQuery(Request $request) + { + + $query = TerrafundDashboardQueryHelper::buildQueryFromRequest($request); + + return $query; + } + + private function getRestorationStrategy(array $projectIds) + { + $strategies = ['direct-seeding', 'tree-planting', 'assisted-natural-regeneration']; + + $conditions = implode(' OR ', array_map(function ($strategy) { + return "JSON_UNQUOTE(JSON_EXTRACT(restoration_strategy, CONCAT('\$[', numbers.n, ']'))) = '$strategy'"; + }, $strategies)); + + $numbers = implode(' UNION ALL ', array_map(function ($n) { + return "SELECT $n AS n"; + }, range(0, 3))); + + return DB::table(DB::raw("(SELECT DISTINCT + project_id, + JSON_UNQUOTE(JSON_EXTRACT(restoration_strategy, CONCAT('\$[', numbers.n, ']'))) AS strategy + FROM + v2_sites + CROSS JOIN + ($numbers) numbers + WHERE + project_id IN (" . implode(',', $projectIds) . ") + AND ($conditions) + ) AS subquery")) + ->groupBy('strategy') + ->select('strategy', DB::raw('COUNT(*) as count_per_project')) + ->get(); + } + + private function getLandUseType(array $projectIds) + { + $landUseTypes = ['agroforest', 'open-natural-ecosystem', 'mangrove', 'natural-forest', 'peatland', 'riparian-area-or-wetland', 'silvopasture', 'urban-forest', 'woodlot-or-plantation']; + + $conditions = implode(' OR ', array_map(function ($type) { + return "JSON_UNQUOTE(JSON_EXTRACT(v2_sites.land_use_types, CONCAT('\$[', numbers.n, ']'))) = '$type'"; + }, $landUseTypes)); + + $numbers = implode(' UNION ALL ', array_map(function ($n) { + return "SELECT $n AS n"; + }, range(0, 4))); + + return Site::select('land_use', DB::raw('COUNT(DISTINCT v2_sites.project_id) as count_per_project')) + ->join(DB::raw("(SELECT project_id, + JSON_UNQUOTE(JSON_EXTRACT(land_use_types, CONCAT('\$[', numbers.n, ']'))) AS land_use + FROM v2_sites + CROSS JOIN + ($numbers) numbers + WHERE + v2_sites.project_id IN (" . implode(',', $projectIds) . ") + AND ($conditions) + ) AS subquery"), function ($join) { + $join->on('v2_sites.project_id', '=', 'subquery.project_id'); + }) + ->groupBy('land_use') + ->get(); + } + + private function getLandTenures(array $projectIds) + { + $landTenures = ['private', 'public', 'indigenous', 'other', 'national_protected_area', 'communal']; + + $conditions = implode(' OR ', array_map(function ($type) { + return "JSON_UNQUOTE(JSON_EXTRACT(v2_sites.land_tenures, CONCAT('\$[', numbers.n, ']'))) = '$type'"; + }, $landTenures)); + + $numbers = implode(' UNION ALL ', array_map(function ($n) { + return "SELECT $n AS n"; + }, range(0, 4))); + + return Site::select('land_tenure', DB::raw('COUNT(DISTINCT v2_sites.project_id) as count_per_project')) + ->join(DB::raw("(SELECT project_id, + JSON_UNQUOTE(JSON_EXTRACT(land_tenures, CONCAT('\$[', numbers.n, ']'))) AS land_tenure + FROM v2_sites + CROSS JOIN + ($numbers) numbers + WHERE + v2_sites.project_id IN (" . implode(',', $projectIds) . ") + AND ($conditions) + ) AS subquery"), function ($join) { + $join->on('v2_sites.project_id', '=', 'subquery.project_id'); + }) + ->groupBy('land_tenure') + ->get(); + } + + private function getResultArray($data, $key) + { + return collect($data)->pluck('count_per_project', $key)->toArray(); + } +} \ No newline at end of file diff --git a/app/Http/Resources/V2/Dashboard/ViewRestorationStrategyResource.php b/app/Http/Resources/V2/Dashboard/ViewRestorationStrategyResource.php new file mode 100644 index 000000000..50e5d58b0 --- /dev/null +++ b/app/Http/Resources/V2/Dashboard/ViewRestorationStrategyResource.php @@ -0,0 +1,23 @@ + $this->resource['restorationStrategies'] ?? null, + 'landUseTypes' => $this->resource['landUseTypes'] ?? null, + 'landTenures' => $this->resource['landTenures'] ?? null, + ]; + } +} From fc38a1a514b9291584391b7b13b21507ad3e74c2 Mon Sep 17 00:00:00 2001 From: cesarLima1 Date: Tue, 28 May 2024 12:30:17 -0400 Subject: [PATCH 03/18] [TM-617] start up tree restoration goal controller --- .../ViewTreeRestorationGoalController.php | 182 ++++++++++++++++++ .../ViewTreeRestorationGoalResource.php | 29 +++ routes/api_v2.php | 6 + 3 files changed, 217 insertions(+) create mode 100644 app/Http/Controllers/V2/Dashboard/ViewTreeRestorationGoalController.php create mode 100644 app/Http/Resources/V2/Dashboard/ViewTreeRestorationGoalResource.php diff --git a/app/Http/Controllers/V2/Dashboard/ViewTreeRestorationGoalController.php b/app/Http/Controllers/V2/Dashboard/ViewTreeRestorationGoalController.php new file mode 100644 index 000000000..aaaa4f921 --- /dev/null +++ b/app/Http/Controllers/V2/Dashboard/ViewTreeRestorationGoalController.php @@ -0,0 +1,182 @@ +prepareProjectQuery($request); + $rawProjectIds = $this->getRawProjectIds($query); + $allProjectIds = $this->getAllProjectIds($rawProjectIds); + $siteIds = $this->getSiteIds($allProjectIds); + $distinctDates = $this->getDistinctDates($siteIds); + $latestDueDate = $this->getLatestDueDate($distinctDates); + + $forProfitProjectIds = $this->filterProjectIdsByType($rawProjectIds, 'for-profit-organization'); + $nonProfitProjectIds = $this->filterProjectIdsByType($rawProjectIds, 'non-profit-organization'); + $forProfitSiteIds = $this->getSiteIds($forProfitProjectIds); + $nonProfitSiteIds = $this->getSiteIds($nonProfitProjectIds); + + $forProfitTreeCount = $this->treeCountByDueDate($forProfitProjectIds); + $nonProfitTreeCount = $this->treeCountByDueDate($nonProfitProjectIds); + + $totalTreesGrownGoal = $query->sum('trees_grown_goal'); + + $treesUnderRestorationActualTotal = $this->treeCountPerPeriod($siteIds, $distinctDates, $totalTreesGrownGoal); + $treesUnderRestorationActualForProfit = $this->treeCountPerPeriod($forProfitSiteIds, $distinctDates, $totalTreesGrownGoal); + $treesUnderRestorationActualNonProfit = $this->treeCountPerPeriod($nonProfitSiteIds, $distinctDates, $totalTreesGrownGoal); + + $averageSurvivalRateTotal = $this->getAverageSurvival($allProjectIds); + $averageSurvivalRateForProfit = $this->getAverageSurvival($forProfitProjectIds); + $averageSurvivalRateNonProfit = $this->getAverageSurvival($nonProfitProjectIds); + + $result = [ + 'forProfitTreeCount' => (int) $forProfitTreeCount, + 'nonProfitTreeCount' => (int) $nonProfitTreeCount, + 'totalTreesGrownGoal' => (int) $totalTreesGrownGoal, + 'treesUnderRestorationActualTotal' => $treesUnderRestorationActualTotal, + 'treesUnderRestorationActualForProfit' => $treesUnderRestorationActualForProfit, + 'treesUnderRestorationActualNonProfit' => $treesUnderRestorationActualNonProfit, + 'averageSurvivalRateTotal' => floatval($averageSurvivalRateTotal), + 'averageSurvivalRateForProfit' => floatval($averageSurvivalRateForProfit), + 'averageSurvivalRateNonProfit' => floatval($averageSurvivalRateNonProfit), + ]; + + return new JsonResponse(ViewTreeRestorationGoalResource::make($result)); + } + + private function prepareProjectQuery(Request $request) + { + + $query = TerrafundDashboardQueryHelper::buildQueryFromRequest($request); + + return $query; + } + + private function getRawProjectIds($query) + { + return $query + ->join('organisations', 'v2_projects.organisation_id', '=', 'organisations.id') + ->select('v2_projects.id', 'organisations.type') + ->get(); + } + + private function getAllProjectIds($projectIds) + { + return $projectIds->pluck('id')->toArray(); + } + + private function getSiteIds($projectIds) + { + return Site::whereIn('project_id', $projectIds)->where('status', EntityStatusStateMachine::APPROVED)->pluck('id'); + } + + private function getDistinctDates($siteIds) + { + return SiteReport::selectRaw('YEAR(due_at) as year, MONTH(due_at) as month') + ->whereNotNull('due_at') + ->whereIn('site_id', $siteIds) + ->groupBy('year', 'month') + ->get() + ->toArray(); + } + + private function filterProjectIdsByType($projectIds, $type) + { + return collect($projectIds)->filter(function ($row) use ($type) { + return $row->type === $type; + })->pluck('id')->toArray(); + } + + private function treeCountByDueDate(array $projectIds) + { + $projects = Project::whereIn('id', $projectIds)->get(); + + return $projects->sum(function ($project) { + return $project->trees_planted_count; + }); + } + + private function treeCountPerPeriod($siteIds, $distinctDates, $totalTreesGrownGoal) + { + $treesUnderRestorationActual = []; + $totalAmount = 0; + + foreach ($distinctDates as $date) { + $year = $date['year']; + $month = $date['month']; + $treeSpeciesAmount = 0; + + $reports = SiteReport::whereIn('site_id', $siteIds) + ->whereNotIn('v2_site_reports.status', SiteReport::UNSUBMITTED_STATUSES) + ->whereYear('v2_site_reports.due_at', $year) + ->whereMonth('v2_site_reports.due_at', $month) + ->get(); + + foreach ($reports as $report) { + $treeSpeciesAmount += $report->treeSpecies()->where('collection', TreeSpecies::COLLECTION_PLANTED)->sum('amount'); + } + + $totalAmount = $totalTreesGrownGoal; + + $formattedDate = Carbon::create($year, $month, 1); + + $treesUnderRestorationActual[] = [ + 'dueDate' => $formattedDate, + 'treeSpeciesAmount' => (int) $treeSpeciesAmount, + 'treeSpeciesPercentage' => 0, + ]; + } + + foreach ($treesUnderRestorationActual as &$treeData) { + $percentage = ($totalAmount != 0) ? ($treeData['treeSpeciesAmount'] / $totalAmount) * 100 : 0; + $treeData['treeSpeciesPercentage'] = floatval(number_format($percentage, 3)); + } + + return $treesUnderRestorationActual; + } + + private function getLatestDueDate($distinctDates) + { + $latestYear = 0; + $latestMonth = 0; + + foreach ($distinctDates as $entry) { + $year = $entry['year']; + $month = $entry['month']; + + if ($year > $latestYear || ($year == $latestYear && $month > $latestMonth)) { + $latestYear = $year; + $latestMonth = $month; + } + } + + $latestDate = [ + 'year' => $latestYear, + 'month' => $latestMonth, + ]; + + return $latestDate; + } + + private function getAverageSurvival(array $projectIds) + { + $averageSurvivalRate = ProjectReport::whereIn('project_id', $projectIds)->avg('pct_survival_to_date'); + + return $averageSurvivalRate; + } +} diff --git a/app/Http/Resources/V2/Dashboard/ViewTreeRestorationGoalResource.php b/app/Http/Resources/V2/Dashboard/ViewTreeRestorationGoalResource.php new file mode 100644 index 000000000..19ec215e2 --- /dev/null +++ b/app/Http/Resources/V2/Dashboard/ViewTreeRestorationGoalResource.php @@ -0,0 +1,29 @@ + (int) $this->resource['forProfitTreeCount'], + 'nonProfitTreeCount' => (int) $this->resource['nonProfitTreeCount'], + 'totalTreesGrownGoal' => (int) $this->resource['totalTreesGrownGoal'], + 'treesUnderRestorationActualTotal' => $this->resource['treesUnderRestorationActualTotal'], + 'treesUnderRestorationActualForProfit' => $this->resource['treesUnderRestorationActualForProfit'], + 'treesUnderRestorationActualNonProfit' => $this->resource['treesUnderRestorationActualNonProfit'], + 'averageSurvivalRateTotal' => floatval($this->resource['averageSurvivalRateTotal']), + 'averageSurvivalRateForProfit' => floatval($this->resource['averageSurvivalRateForProfit']), + 'averageSurvivalRateNonProfit' => floatval($this->resource['averageSurvivalRateNonProfit']), + ]; + } +} \ No newline at end of file diff --git a/routes/api_v2.php b/routes/api_v2.php index 194f7197e..acfb65c66 100644 --- a/routes/api_v2.php +++ b/routes/api_v2.php @@ -16,6 +16,9 @@ use App\Http\Controllers\V2\CoreTeamLeader\StoreCoreTeamLeaderController; use App\Http\Controllers\V2\CoreTeamLeader\UpdateCoreTeamLeaderController; use App\Http\Controllers\V2\Dashboard\GetJobsCreatedController; +use App\Http\Controllers\V2\Dashboard\ProjectListExportController; +use App\Http\Controllers\V2\Dashboard\ViewRestorationStrategyController; +use App\Http\Controllers\V2\Dashboard\ViewTreeRestorationGoalController; use App\Http\Controllers\V2\Disturbances\DeleteDisturbanceController; use App\Http\Controllers\V2\Disturbances\GetDisturbancesForEntityController; use App\Http\Controllers\V2\Disturbances\StoreDisturbanceController; @@ -652,5 +655,8 @@ function () { //Route::delete('file/{uuid}', [FilePropertiesController::class, 'destroy']); Route::prefix('dashboard')->group(function () { + Route::get('/restoration-strategy', ViewRestorationStrategyController::class); Route::get('/jobs-created', GetJobsCreatedController::class); + Route::get('/tree-restoration-goal', ViewTreeRestorationGoalController::class); + Route::get('/project-list-export', ProjectListExportController::class); }); From 46b60351df0912d8c07b74a26f4f420a44b222b9 Mon Sep 17 00:00:00 2001 From: cesarLima1 Date: Tue, 28 May 2024 12:31:44 -0400 Subject: [PATCH 04/18] fix lint --- .../Controllers/V2/Dashboard/ActiveProjectsTableController.php | 2 +- .../V2/Dashboard/ViewRestorationStrategyController.php | 2 +- .../Resources/V2/Dashboard/ViewTreeRestorationGoalResource.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/V2/Dashboard/ActiveProjectsTableController.php b/app/Http/Controllers/V2/Dashboard/ActiveProjectsTableController.php index 71436c2f6..c38249bfe 100644 --- a/app/Http/Controllers/V2/Dashboard/ActiveProjectsTableController.php +++ b/app/Http/Controllers/V2/Dashboard/ActiveProjectsTableController.php @@ -114,4 +114,4 @@ public function paginate($items, $perPage = 10, $page = null, $options = []) return new LengthAwarePaginator($items->forPage($page, $perPage), $items->count(), $perPage, $page, $options); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/V2/Dashboard/ViewRestorationStrategyController.php b/app/Http/Controllers/V2/Dashboard/ViewRestorationStrategyController.php index 40aac1fca..32ca7ce77 100644 --- a/app/Http/Controllers/V2/Dashboard/ViewRestorationStrategyController.php +++ b/app/Http/Controllers/V2/Dashboard/ViewRestorationStrategyController.php @@ -129,4 +129,4 @@ private function getResultArray($data, $key) { return collect($data)->pluck('count_per_project', $key)->toArray(); } -} \ No newline at end of file +} diff --git a/app/Http/Resources/V2/Dashboard/ViewTreeRestorationGoalResource.php b/app/Http/Resources/V2/Dashboard/ViewTreeRestorationGoalResource.php index 19ec215e2..bf4af65ae 100644 --- a/app/Http/Resources/V2/Dashboard/ViewTreeRestorationGoalResource.php +++ b/app/Http/Resources/V2/Dashboard/ViewTreeRestorationGoalResource.php @@ -26,4 +26,4 @@ public function toArray($request) 'averageSurvivalRateNonProfit' => floatval($this->resource['averageSurvivalRateNonProfit']), ]; } -} \ No newline at end of file +} From 9b7c14fd515099bca0d8f57d795d36af22c0a11d Mon Sep 17 00:00:00 2001 From: cesarLima1 Date: Tue, 28 May 2024 14:44:40 -0400 Subject: [PATCH 05/18] [TM-621] add definition for jobs created controller --- .../definitions/DashboardJobsCreatedData.yml | 36 +++++ .../DashboardJobsCreatedResponse.yml | 4 + openapi-src/V2/definitions/_index.yml | 4 + .../get-v2-dashboard-jobs-created.yml | 22 +++ openapi-src/V2/paths/_index.yml | 3 + resources/docs/swagger-v2.yml | 139 ++++++++++++++++++ 6 files changed, 208 insertions(+) create mode 100644 openapi-src/V2/definitions/DashboardJobsCreatedData.yml create mode 100644 openapi-src/V2/definitions/DashboardJobsCreatedResponse.yml create mode 100644 openapi-src/V2/paths/Dashboard/get-v2-dashboard-jobs-created.yml diff --git a/openapi-src/V2/definitions/DashboardJobsCreatedData.yml b/openapi-src/V2/definitions/DashboardJobsCreatedData.yml new file mode 100644 index 000000000..01fcdf9f7 --- /dev/null +++ b/openapi-src/V2/definitions/DashboardJobsCreatedData.yml @@ -0,0 +1,36 @@ +type: object +properties: + totalJobsCreated: + type: integer + forProfitJobsCreated: + type: integer + nonProfitJobsCreated: + type: integer + total_ft: + type: integer + total_pt: + type: integer + total_men: + type: integer + total_pt_men: + type: integer + total_ft_men: + type: integer + total_women: + type: integer + total_pt_women: + type: integer + total_ft_women: + type: integer + total_youth: + type: integer + total_pt_youth: + type: integer + total_ft_youth: + type: integer + total_non_youth: + type: integer + total_pt_non_youth: + type: integer + total_ft_non_youth: + type: integer \ No newline at end of file diff --git a/openapi-src/V2/definitions/DashboardJobsCreatedResponse.yml b/openapi-src/V2/definitions/DashboardJobsCreatedResponse.yml new file mode 100644 index 000000000..871e86211 --- /dev/null +++ b/openapi-src/V2/definitions/DashboardJobsCreatedResponse.yml @@ -0,0 +1,4 @@ +type: object +properties: + data: + $ref: './_index.yml#/DashboardJobsCreatedData' \ No newline at end of file diff --git a/openapi-src/V2/definitions/_index.yml b/openapi-src/V2/definitions/_index.yml index fcff7d24b..330ba1260 100644 --- a/openapi-src/V2/definitions/_index.yml +++ b/openapi-src/V2/definitions/_index.yml @@ -276,3 +276,7 @@ GeoJSON: $ref: './GeoJSON.yml' SiteGeometryPost: $ref: './SiteGeometryPost.yml' +DashboardJobsCreatedResponse: + $ref: './DashboardJobsCreatedResponse.yml' +DashboardJobsCreatedData: + $ref: './DashboardJobsCreatedData.yml' diff --git a/openapi-src/V2/paths/Dashboard/get-v2-dashboard-jobs-created.yml b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-jobs-created.yml new file mode 100644 index 000000000..31cf88f42 --- /dev/null +++ b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-jobs-created.yml @@ -0,0 +1,22 @@ +operationId: get-v2-jobs-created.yml +summary: view Jobs created for dashboard +parameters: + - in: query + name: country + type: string + description: Optional. Filter counts and metrics by country. + - in: query + name: uuid + type: string + description: Optional. Filter counts and metrics by UUID. +responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '../../definitions/_index.yml#/DashboardJobsCreatedResponse' + '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 e1e615422..df4d71557 100644 --- a/openapi-src/V2/paths/_index.yml +++ b/openapi-src/V2/paths/_index.yml @@ -2523,3 +2523,6 @@ /v2/geometry/{UUID}: put: $ref: './Geometry/put-v2-geometry-uuid.yml' +/v2/dashboard/jobs-created: + get: + $ref: './Dashboard/get-v2-dashboard-jobs-created.yml' \ No newline at end of file diff --git a/resources/docs/swagger-v2.yml b/resources/docs/swagger-v2.yml index aa91d215f..336cd3739 100644 --- a/resources/docs/swagger-v2.yml +++ b/resources/docs/swagger-v2.yml @@ -44062,6 +44062,83 @@ definitions: message: type: string description: Human readable string in English to describe the error. + DashboardJobsCreatedResponse: + type: object + properties: + data: + type: object + properties: + totalJobsCreated: + type: integer + forProfitJobsCreated: + type: integer + nonProfitJobsCreated: + type: integer + total_ft: + type: integer + total_pt: + type: integer + total_men: + type: integer + total_pt_men: + type: integer + total_ft_men: + type: integer + total_women: + type: integer + total_pt_women: + type: integer + total_ft_women: + type: integer + total_youth: + type: integer + total_pt_youth: + type: integer + total_ft_youth: + type: integer + total_non_youth: + type: integer + total_pt_non_youth: + type: integer + total_ft_non_youth: + type: integer + DashboardJobsCreatedData: + type: object + properties: + totalJobsCreated: + type: integer + forProfitJobsCreated: + type: integer + nonProfitJobsCreated: + type: integer + total_ft: + type: integer + total_pt: + type: integer + total_men: + type: integer + total_pt_men: + type: integer + total_ft_men: + type: integer + total_women: + type: integer + total_pt_women: + type: integer + total_ft_women: + type: integer + total_youth: + type: integer + total_pt_youth: + type: integer + total_ft_youth: + type: integer + total_non_youth: + type: integer + total_pt_non_youth: + type: integer + total_ft_non_youth: + type: integer paths: '/v2/tree-species/{entity}/{UUID}': get: @@ -93944,3 +94021,65 @@ paths: description: This account does not have permission to update the polygon. '404': description: Geometry was not found. + /v2/dashboard/jobs-created: + get: + operationId: get-v2-jobs-created.yml + summary: view Jobs created for dashboard + parameters: + - in: query + name: country + type: string + description: Optional. Filter counts and metrics by country. + - in: query + name: uuid + type: string + description: Optional. Filter counts and metrics by UUID. + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + data: + type: object + properties: + totalJobsCreated: + type: integer + forProfitJobsCreated: + type: integer + nonProfitJobsCreated: + type: integer + total_ft: + type: integer + total_pt: + type: integer + total_men: + type: integer + total_pt_men: + type: integer + total_ft_men: + type: integer + total_women: + type: integer + total_pt_women: + type: integer + total_ft_women: + type: integer + total_youth: + type: integer + total_pt_youth: + type: integer + total_ft_youth: + type: integer + total_non_youth: + type: integer + total_pt_non_youth: + type: integer + total_ft_non_youth: + type: integer + '400': + description: Bad request + '500': + description: Internal server error From 0df4d69b0ced4680dd8bf372073cd173701ba4a3 Mon Sep 17 00:00:00 2001 From: cesarLima1 Date: Tue, 28 May 2024 14:59:57 -0400 Subject: [PATCH 06/18] [TM-626] add definition for restoration strategies controller --- .../DashboardRestorationStrategyResponse.yml | 47 +++++++ openapi-src/V2/definitions/_index.yml | 2 + .../get-v2-dashboard-restoration-strategy.yml | 21 ++++ openapi-src/V2/paths/_index.yml | 5 +- resources/docs/swagger-v2.yml | 117 ++++++++++++++++++ 5 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 openapi-src/V2/definitions/DashboardRestorationStrategyResponse.yml create mode 100644 openapi-src/V2/paths/Dashboard/get-v2-dashboard-restoration-strategy.yml diff --git a/openapi-src/V2/definitions/DashboardRestorationStrategyResponse.yml b/openapi-src/V2/definitions/DashboardRestorationStrategyResponse.yml new file mode 100644 index 000000000..faa59812d --- /dev/null +++ b/openapi-src/V2/definitions/DashboardRestorationStrategyResponse.yml @@ -0,0 +1,47 @@ +type: object +properties: + restorationStrategies: + type: object + properties: + direct-seeding: + type: integer + tree-planting: + type: integer + assisted-natural-regeneration: + type: integer + landUseTypes: + type: object + properties: + agroforest: + type: integer + open-natural-ecosystem: + type: integer + mangrove: + type: integer + natural-forest: + type: integer + peatland: + type: integer + riparian-area-or-wetland: + type: integer + silvopasture: + type: integer + urban-forest: + type: integer + woodlot-or-plantation: + type: integer + landTenures: + type: object + properties: + communal: + type: integer + indigenous: + type: integer + national_protected_area: + type: integer + other: + type: integer + private: + type: integer + public: + type: integer \ No newline at end of file diff --git a/openapi-src/V2/definitions/_index.yml b/openapi-src/V2/definitions/_index.yml index 330ba1260..6375c1e7b 100644 --- a/openapi-src/V2/definitions/_index.yml +++ b/openapi-src/V2/definitions/_index.yml @@ -280,3 +280,5 @@ DashboardJobsCreatedResponse: $ref: './DashboardJobsCreatedResponse.yml' DashboardJobsCreatedData: $ref: './DashboardJobsCreatedData.yml' +DashboardRestorationStrategyResponse: + $ref: './DashboardRestorationStrategyResponse.yml' \ No newline at end of file diff --git a/openapi-src/V2/paths/Dashboard/get-v2-dashboard-restoration-strategy.yml b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-restoration-strategy.yml new file mode 100644 index 000000000..711bcf3c9 --- /dev/null +++ b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-restoration-strategy.yml @@ -0,0 +1,21 @@ +summary: View Restoration Strategy for dashboard +parameters: + - in: query + name: country + type: string + description: Optional. Filter restoration strategy by country. + - in: query + name: uuid + type: string + description: Optional. Filter restoration strategy by UUID. +responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '../../definitions/_index.yml#/DashboardRestorationStrategyResponse' + '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 df4d71557..86f32c1ba 100644 --- a/openapi-src/V2/paths/_index.yml +++ b/openapi-src/V2/paths/_index.yml @@ -2525,4 +2525,7 @@ $ref: './Geometry/put-v2-geometry-uuid.yml' /v2/dashboard/jobs-created: get: - $ref: './Dashboard/get-v2-dashboard-jobs-created.yml' \ No newline at end of file + $ref: './Dashboard/get-v2-dashboard-jobs-created.yml' +'/v2/dashboard/restoration-strategy': + get: + $ref: './Dashboard/get-v2-dashboard-restoration-strategy.yml' \ No newline at end of file diff --git a/resources/docs/swagger-v2.yml b/resources/docs/swagger-v2.yml index 336cd3739..98276a063 100644 --- a/resources/docs/swagger-v2.yml +++ b/resources/docs/swagger-v2.yml @@ -44139,6 +44139,54 @@ definitions: type: integer total_ft_non_youth: type: integer + DashboardRestorationStrategyResponse: + type: object + properties: + restorationStrategies: + type: object + properties: + direct-seeding: + type: integer + tree-planting: + type: integer + assisted-natural-regeneration: + type: integer + landUseTypes: + type: object + properties: + agroforest: + type: integer + open-natural-ecosystem: + type: integer + mangrove: + type: integer + natural-forest: + type: integer + peatland: + type: integer + riparian-area-or-wetland: + type: integer + silvopasture: + type: integer + urban-forest: + type: integer + woodlot-or-plantation: + type: integer + landTenures: + type: object + properties: + communal: + type: integer + indigenous: + type: integer + national_protected_area: + type: integer + other: + type: integer + private: + type: integer + public: + type: integer paths: '/v2/tree-species/{entity}/{UUID}': get: @@ -94083,3 +94131,72 @@ paths: description: Bad request '500': description: Internal server error + /v2/dashboard/restoration-strategy: + get: + summary: View Restoration Strategy for dashboard + parameters: + - in: query + name: country + type: string + description: Optional. Filter restoration strategy by country. + - in: query + name: uuid + type: string + description: Optional. Filter restoration strategy by UUID. + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + restorationStrategies: + type: object + properties: + direct-seeding: + type: integer + tree-planting: + type: integer + assisted-natural-regeneration: + type: integer + landUseTypes: + type: object + properties: + agroforest: + type: integer + open-natural-ecosystem: + type: integer + mangrove: + type: integer + natural-forest: + type: integer + peatland: + type: integer + riparian-area-or-wetland: + type: integer + silvopasture: + type: integer + urban-forest: + type: integer + woodlot-or-plantation: + type: integer + landTenures: + type: object + properties: + communal: + type: integer + indigenous: + type: integer + national_protected_area: + type: integer + other: + type: integer + private: + type: integer + public: + type: integer + '400': + description: Bad request + '500': + description: Internal server error From e6d78895d4adf8da53e042d83effced4db4ecab3 Mon Sep 17 00:00:00 2001 From: cesarLima1 Date: Tue, 28 May 2024 15:09:18 -0400 Subject: [PATCH 07/18] [TM-617] add definition for tree restoartion goal controller --- .../DashboardTreeRestorationGoalResponse.yml | 26 ++++ .../DashboardTreesUnderRestorationActual.yml | 9 ++ openapi-src/V2/definitions/_index.yml | 6 +- ...get-v2-dashboard-tree-restoration-goal.yml | 21 +++ openapi-src/V2/paths/_index.yml | 5 +- resources/docs/swagger-v2.yml | 133 ++++++++++++++++++ 6 files changed, 198 insertions(+), 2 deletions(-) create mode 100644 openapi-src/V2/definitions/DashboardTreeRestorationGoalResponse.yml create mode 100644 openapi-src/V2/definitions/DashboardTreesUnderRestorationActual.yml create mode 100644 openapi-src/V2/paths/Dashboard/get-v2-dashboard-tree-restoration-goal.yml diff --git a/openapi-src/V2/definitions/DashboardTreeRestorationGoalResponse.yml b/openapi-src/V2/definitions/DashboardTreeRestorationGoalResponse.yml new file mode 100644 index 000000000..1721e0dbc --- /dev/null +++ b/openapi-src/V2/definitions/DashboardTreeRestorationGoalResponse.yml @@ -0,0 +1,26 @@ +type: object +properties: + forProfitTreeCount: + type: integer + nonProfitTreeCount: + type: integer + totalTreesGrownGoal: + type: integer + treesUnderRestorationActualTotal: + type: array + items: + $ref: './_index.yml#/DashboardTreesUnderRestorationActual' + treesUnderRestorationActualForProfit: + type: array + items: + $ref: './_index.yml#/DashboardTreesUnderRestorationActual' + treesUnderRestorationActualNonProfit: + type: array + items: + $ref: './_index.yml#/DashboardTreesUnderRestorationActual' + averageSurvivalRateTotal: + type: number + averageSurvivalRateForProfit: + type: number + averageSurvivalRateNonProfit: + type: number \ No newline at end of file diff --git a/openapi-src/V2/definitions/DashboardTreesUnderRestorationActual.yml b/openapi-src/V2/definitions/DashboardTreesUnderRestorationActual.yml new file mode 100644 index 000000000..8549a3627 --- /dev/null +++ b/openapi-src/V2/definitions/DashboardTreesUnderRestorationActual.yml @@ -0,0 +1,9 @@ +type: object +properties: + dueDate: + type: string + format: date + treeSpeciesAmount: + type: integer + treeSpeciesPercentage: + type: number \ No newline at end of file diff --git a/openapi-src/V2/definitions/_index.yml b/openapi-src/V2/definitions/_index.yml index 6375c1e7b..bc5beab0d 100644 --- a/openapi-src/V2/definitions/_index.yml +++ b/openapi-src/V2/definitions/_index.yml @@ -281,4 +281,8 @@ DashboardJobsCreatedResponse: DashboardJobsCreatedData: $ref: './DashboardJobsCreatedData.yml' DashboardRestorationStrategyResponse: - $ref: './DashboardRestorationStrategyResponse.yml' \ No newline at end of file + $ref: './DashboardRestorationStrategyResponse.yml' +DashboardTreeRestorationGoalResponse: + $ref: './DashboardTreeRestorationGoalResponse.yml' +DashboardTreesUnderRestorationActual: + $ref: './DashboardTreesUnderRestorationActual.yml' \ No newline at end of file diff --git a/openapi-src/V2/paths/Dashboard/get-v2-dashboard-tree-restoration-goal.yml b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-tree-restoration-goal.yml new file mode 100644 index 000000000..c8f1a9d8c --- /dev/null +++ b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-tree-restoration-goal.yml @@ -0,0 +1,21 @@ +summary: View Tree Restoration Goal for dashboard +parameters: + - in: query + name: country + type: string + description: Optional. Filter tree restoration goal by country. + - in: query + name: uuid + type: string + description: Optional. Filter tree restoration goal by UUID. +responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '../../definitions/_index.yml#/DashboardTreeRestorationGoalResponse' + '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 86f32c1ba..93fb1ddd0 100644 --- a/openapi-src/V2/paths/_index.yml +++ b/openapi-src/V2/paths/_index.yml @@ -2528,4 +2528,7 @@ $ref: './Dashboard/get-v2-dashboard-jobs-created.yml' '/v2/dashboard/restoration-strategy': get: - $ref: './Dashboard/get-v2-dashboard-restoration-strategy.yml' \ No newline at end of file + $ref: './Dashboard/get-v2-dashboard-restoration-strategy.yml' +'/v2/dashboard/tree-restoration-goal': + get: + $ref: './Dashboard/get-v2-dashboard-tree-restoration-goal.yml' \ No newline at end of file diff --git a/resources/docs/swagger-v2.yml b/resources/docs/swagger-v2.yml index 98276a063..95d366667 100644 --- a/resources/docs/swagger-v2.yml +++ b/resources/docs/swagger-v2.yml @@ -44187,6 +44187,67 @@ definitions: type: integer public: type: integer + DashboardTreeRestorationGoalResponse: + type: object + properties: + forProfitTreeCount: + type: integer + nonProfitTreeCount: + type: integer + totalTreesGrownGoal: + type: integer + treesUnderRestorationActualTotal: + type: array + items: + type: object + properties: + dueDate: + type: string + format: date + treeSpeciesAmount: + type: integer + treeSpeciesPercentage: + type: number + treesUnderRestorationActualForProfit: + type: array + items: + type: object + properties: + dueDate: + type: string + format: date + treeSpeciesAmount: + type: integer + treeSpeciesPercentage: + type: number + treesUnderRestorationActualNonProfit: + type: array + items: + type: object + properties: + dueDate: + type: string + format: date + treeSpeciesAmount: + type: integer + treeSpeciesPercentage: + type: number + averageSurvivalRateTotal: + type: number + averageSurvivalRateForProfit: + type: number + averageSurvivalRateNonProfit: + type: number + DashboardTreesUnderRestorationActual: + type: object + properties: + dueDate: + type: string + format: date + treeSpeciesAmount: + type: integer + treeSpeciesPercentage: + type: number paths: '/v2/tree-species/{entity}/{UUID}': get: @@ -94200,3 +94261,75 @@ paths: description: Bad request '500': description: Internal server error + /v2/dashboard/tree-restoration-goal: + get: + summary: View Tree Restoration Goal for dashboard + parameters: + - in: query + name: country + type: string + description: Optional. Filter tree restoration goal by country. + - in: query + name: uuid + type: string + description: Optional. Filter tree restoration goal by UUID. + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + forProfitTreeCount: + type: integer + nonProfitTreeCount: + type: integer + totalTreesGrownGoal: + type: integer + treesUnderRestorationActualTotal: + type: array + items: + type: object + properties: + dueDate: + type: string + format: date + treeSpeciesAmount: + type: integer + treeSpeciesPercentage: + type: number + treesUnderRestorationActualForProfit: + type: array + items: + type: object + properties: + dueDate: + type: string + format: date + treeSpeciesAmount: + type: integer + treeSpeciesPercentage: + type: number + treesUnderRestorationActualNonProfit: + type: array + items: + type: object + properties: + dueDate: + type: string + format: date + treeSpeciesAmount: + type: integer + treeSpeciesPercentage: + type: number + averageSurvivalRateTotal: + type: number + averageSurvivalRateForProfit: + type: number + averageSurvivalRateNonProfit: + type: number + '400': + description: Bad request + '500': + description: Internal server error From 132bf4aeaeec63985b84c084c92fa4a1189573dd Mon Sep 17 00:00:00 2001 From: cesarLima1 Date: Tue, 28 May 2024 15:13:56 -0400 Subject: [PATCH 08/18] [TM-658] add definition for project list export controller --- .../get-v2-dashboard-project-list-export.yml | 10 ++++++++++ openapi-src/V2/paths/_index.yml | 5 ++++- resources/docs/swagger-v2.yml | 12 ++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 openapi-src/V2/paths/Dashboard/get-v2-dashboard-project-list-export.yml diff --git a/openapi-src/V2/paths/Dashboard/get-v2-dashboard-project-list-export.yml b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-project-list-export.yml new file mode 100644 index 000000000..e954d9cd7 --- /dev/null +++ b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-project-list-export.yml @@ -0,0 +1,10 @@ +summary: Export CSV document of active projects +tags: + - Export +produces: + - text/plain +responses: + '200': + description: OK + schema: + type: file \ No newline at end of file diff --git a/openapi-src/V2/paths/_index.yml b/openapi-src/V2/paths/_index.yml index 93fb1ddd0..f4ab06285 100644 --- a/openapi-src/V2/paths/_index.yml +++ b/openapi-src/V2/paths/_index.yml @@ -2531,4 +2531,7 @@ $ref: './Dashboard/get-v2-dashboard-restoration-strategy.yml' '/v2/dashboard/tree-restoration-goal': get: - $ref: './Dashboard/get-v2-dashboard-tree-restoration-goal.yml' \ No newline at end of file + $ref: './Dashboard/get-v2-dashboard-tree-restoration-goal.yml' +'/v2/dashboard/project-list-export': + get: + $ref: './Dashboard/get-v2-dashboard-project-list-export.yml' \ No newline at end of file diff --git a/resources/docs/swagger-v2.yml b/resources/docs/swagger-v2.yml index 95d366667..9195ba99d 100644 --- a/resources/docs/swagger-v2.yml +++ b/resources/docs/swagger-v2.yml @@ -94333,3 +94333,15 @@ paths: description: Bad request '500': description: Internal server error + /v2/dashboard/project-list-export: + get: + summary: Export CSV document of active projects + tags: + - Export + produces: + - text/plain + responses: + '200': + description: OK + schema: + type: file From 56e7bf0828cc1d6f8486cac4a7622aea76508692 Mon Sep 17 00:00:00 2001 From: JORGE Date: Wed, 29 May 2024 09:54:20 -0400 Subject: [PATCH 09/18] [TM-671] return polygons, project and country data --- app/Helpers/GeometryHelper.php | 119 +++++++++ .../V2/Dashboard/CountryDataController.php | 110 ++++++++ .../V2/Dashboard/GetPolygonsController.php | 47 ++++ .../V2/Dashboard/GetPolygonsResource.php | 19 ++ app/Models/V2/Projects/Project.php | 43 ++- app/Models/V2/Sites/SitePolygon.php | 13 +- .../V2/definitions/DashboardBBOXCountry.yml | 4 + .../V2/definitions/DashboardBBOXProject.yml | 4 + .../DashboardGetPolygonStatusResponse.yml | 13 + .../definitions/DashboardGetProjectsData.yml | 12 + .../DashboardGetProjectsResponse.yml | 6 + .../V2/definitions/DashboardPolygonData.yml | 16 ++ openapi-src/V2/definitions/_index.yml | 14 +- .../Dashboard/get-v2-dashboard-country.yml | 16 ++ .../get-v2-dashboard-get-bbox-project.yml | 17 ++ ...get-v2-dashboard-get-polygons-statuses.yml | 19 ++ .../get-v2-dashboard-get-polygons.yml | 19 ++ .../get-v2-dashboard-jobs-created.yml | 1 - .../get-v2-dashboard-polygon-data-uuid.yml | 16 ++ .../get-v2-dashboard-project-data-uuid.yml | 16 ++ openapi-src/V2/paths/_index.yml | 20 +- package.json | 3 +- resources/docs/swagger-v2.yml | 252 +++++++++++++++++- routes/api_v2.php | 8 + 24 files changed, 771 insertions(+), 36 deletions(-) create mode 100644 app/Helpers/GeometryHelper.php create mode 100644 app/Http/Controllers/V2/Dashboard/CountryDataController.php create mode 100644 app/Http/Controllers/V2/Dashboard/GetPolygonsController.php create mode 100644 app/Http/Resources/V2/Dashboard/GetPolygonsResource.php create mode 100644 openapi-src/V2/definitions/DashboardBBOXCountry.yml create mode 100644 openapi-src/V2/definitions/DashboardBBOXProject.yml create mode 100644 openapi-src/V2/definitions/DashboardGetPolygonStatusResponse.yml create mode 100644 openapi-src/V2/definitions/DashboardGetProjectsData.yml create mode 100644 openapi-src/V2/definitions/DashboardGetProjectsResponse.yml create mode 100644 openapi-src/V2/definitions/DashboardPolygonData.yml create mode 100644 openapi-src/V2/paths/Dashboard/get-v2-dashboard-country.yml create mode 100644 openapi-src/V2/paths/Dashboard/get-v2-dashboard-get-bbox-project.yml create mode 100644 openapi-src/V2/paths/Dashboard/get-v2-dashboard-get-polygons-statuses.yml create mode 100644 openapi-src/V2/paths/Dashboard/get-v2-dashboard-get-polygons.yml create mode 100644 openapi-src/V2/paths/Dashboard/get-v2-dashboard-polygon-data-uuid.yml create mode 100644 openapi-src/V2/paths/Dashboard/get-v2-dashboard-project-data-uuid.yml diff --git a/app/Helpers/GeometryHelper.php b/app/Helpers/GeometryHelper.php new file mode 100644 index 000000000..b0ce5cfb8 --- /dev/null +++ b/app/Helpers/GeometryHelper.php @@ -0,0 +1,119 @@ +first(); + + if (! $project) { + return null; + } + + $sitePolygons = $project->sitePolygons; + + if ($sitePolygons->isEmpty()) { + return null; // Return null if no polygons are found for the given projectUuid + } + + $polyIds = $sitePolygons->pluck('poly_id')->toArray(); + + $centroids = PolygonGeometry::selectRaw('ST_AsGeoJSON(ST_Centroid(geom)) AS centroid') + ->whereIn('uuid', $polyIds) + ->get(); + + if ($centroids->isEmpty()) { + return null; // Return null if no centroids are found + } + + $centroidCount = $centroids->count(); + $totalLatitude = 0; + $totalLongitude = 0; + + foreach ($centroids as $centroid) { + $centroidData = json_decode($centroid->centroid, true); + $totalLatitude += $centroidData['coordinates'][1]; + $totalLongitude += $centroidData['coordinates'][0]; + } + + $averageLatitude = $totalLatitude / $centroidCount; + $averageLongitude = $totalLongitude / $centroidCount; + + $centroidOfCentroids = json_encode([ + 'type' => 'Point', + 'coordinates' => [$averageLongitude, $averageLatitude], + ]); + + return $centroidOfCentroids; + } + + public function updateProjectCentroid(string $projectUuid) + { + try { + $centroid = $this->centroidOfProject($projectUuid); + + if ($centroid === null) { + Log::warning("Invalid centroid for projectUuid: $projectUuid"); + } + + $centroidArray = json_decode($centroid, true); + + $latitude = $centroidArray['coordinates'][1]; + $longitude = $centroidArray['coordinates'][0]; + + + Project::where('uuid', $projectUuid) + ->update([ + 'lat' => $latitude, + 'long' => $longitude, + ]); + + + Log::info("Centroid updated for projectUuid: $projectUuid"); + + return 'Centroids updated successfully!'; + } catch (\Exception $e) { + Log::error("Error updating centroid for projectUuid: $projectUuid"); + + return response()->json([ + 'message' => 'Error updating centroid', + 'error' => $e->getMessage(), + ], 500); + } + + } + + public static function getPolygonsBbox($polygonsIds) + { + $envelopes = PolygonGeometry::whereIn('uuid', $polygonsIds) + ->selectRaw('ST_ASGEOJSON(ST_Envelope(geom)) as envelope') + ->get(); + + $maxX = $maxY = PHP_INT_MIN; + $minX = $minY = PHP_INT_MAX; + + foreach ($envelopes as $envelope) { + $geojson = json_decode($envelope->envelope); + $coordinates = $geojson->coordinates[0]; + + foreach ($coordinates as $point) { + $x = $point[0]; + $y = $point[1]; + $maxX = max($maxX, $x); + $minX = min($minX, $x); + $maxY = max($maxY, $y); + $minY = min($minY, $y); + } + } + + $bboxCoordinates = [$minX, $minY, $maxX, $maxY]; + + return $bboxCoordinates; + } +} diff --git a/app/Http/Controllers/V2/Dashboard/CountryDataController.php b/app/Http/Controllers/V2/Dashboard/CountryDataController.php new file mode 100644 index 000000000..a11ec59f6 --- /dev/null +++ b/app/Http/Controllers/V2/Dashboard/CountryDataController.php @@ -0,0 +1,110 @@ +selectRaw('ST_AsGeoJSON(ST_Envelope(geometry)) AS bbox, country') + ->first(); + + if (! $countryData) { + return response()->json(['error' => 'Country not found'], 404); + } + + // Decode the GeoJSON bbox + $geoJson = json_decode($countryData->bbox); + + // Extract the bounding box coordinates + $coordinates = $geoJson->coordinates[0]; + + // Get the country name + $countryName = $countryData->country; + + // Construct the bbox data in the specified format + $countryBbox = [ + $countryName, + [$coordinates[0][0], $coordinates[0][1], $coordinates[2][0], $coordinates[2][1]], + ]; + + return response()->json(['bbox' => $countryBbox]); + } + + public function getPolygonData(string $uuid) + { + $sitePolygon = SitePolygon::where('poly_id', $uuid)->first(); + + if (! $sitePolygon) { + return response()->json(['error' => 'Polygon not found'], 404); + } + + $project = $sitePolygon->project()->first(); + + if (! $project) { + Log::error("Project not found for site polygon with ID: $sitePolygon->id"); + } + + $site = $sitePolygon->site()->first(); + + if(! $site) { + Log::error("Site not found for site polygon with ID: $sitePolygon->id"); + + } + + $data = [ + ['key' => 'poly_name', 'title' => 'title', 'value' => $sitePolygon->poly_name ?? null], + ['key' => 'project_name', 'title' => 'Project', 'value' => $project->name ?? null], + ['key' => 'site_name', 'title' => 'Site', 'value' => $site?->name ?? null], + ['key' => 'num_trees', 'title' => 'Number of trees', 'value' => $sitePolygon->num_trees ?? null], + ['key' => 'plantstart', 'title' => 'Plant Start Date', 'value' => $sitePolygon->plantstart ?? null], + ['key' => 'status', 'title' => 'Status', 'value' => $sitePolygon->status ?? null], + + ]; + + return response()->json(['data' => $data]); + } + + public function getProjectData(string $uuid) + { + try { + $project = Project::isUuid($uuid)->first(); + + if (! $project) { + Log::error("Project not found for project with UUID: $uuid"); + } + $countSitePolygons = 0; + if($project) { + $countSitePolygons = $project->getTotalSitePolygons(); + } + + $organization = $project->organisation()->first(); + if (! $organization) { + Log::error("Organization not found for project with ID: $project->id"); + } + + $country = WorldCountryGeneralized::where('iso', $project->country)->first(); + $data = [ + ['key' => 'project_name', 'title' => 'title', 'value' => $project->name ?? null], + ['key' => 'country', 'title' => 'Country', 'value' => $country->country ?? null], + ['key' => 'polygon_counts', 'title' => 'No. of Site - Polygons', 'value' => $countSitePolygons ?? null], + ['key' => 'organizations', 'title' => 'Organization', 'value' => $organization->name ?? null], + ]; + + return response()->json(['data' => $data]); + } catch (\Exception $e) { + Log::error($e->getMessage()); + + return response()->json(['error' => 'An error occurred while fetching project data', 'message' => $e->getMessage()], 500); + } + + } +} diff --git a/app/Http/Controllers/V2/Dashboard/GetPolygonsController.php b/app/Http/Controllers/V2/Dashboard/GetPolygonsController.php new file mode 100644 index 000000000..cf210820b --- /dev/null +++ b/app/Http/Controllers/V2/Dashboard/GetPolygonsController.php @@ -0,0 +1,47 @@ +pluck('uuid'); + + return new GetPolygonsResource([ + 'data' => $polygons, + ]); + } + + public function getPolygonsByStatusOfProject(Request $request): GetPolygonsResource + { + $polygonsIds = TerrafundDashboardQueryHelper::getPolygonsByStatusOfProject($request); + + return new GetPolygonsResource([ + 'data' => $polygonsIds, + ]); + } + + public function getBboxOfCompleteProject(Request $request) + { + try { + $polygonsIds = TerrafundDashboardQueryHelper::getPolygonIdsOfProject($request); + $bboxCoordinates = GeometryHelper::getPolygonsBbox($polygonsIds); + + return response()->json(['bbox' => $bboxCoordinates]); + } catch (\Exception $e) { + Log::error($e->getMessage()); + + return response()->json(['error' => 'An error occurred while fetching the bounding box coordinates'], 404); + } + } +}; diff --git a/app/Http/Resources/V2/Dashboard/GetPolygonsResource.php b/app/Http/Resources/V2/Dashboard/GetPolygonsResource.php new file mode 100644 index 000000000..b01aaa77c --- /dev/null +++ b/app/Http/Resources/V2/Dashboard/GetPolygonsResource.php @@ -0,0 +1,19 @@ +resource; + } +} diff --git a/app/Models/V2/Projects/Project.php b/app/Models/V2/Projects/Project.php index c1d76cb03..1bc9c7a6a 100644 --- a/app/Models/V2/Projects/Project.php +++ b/app/Models/V2/Projects/Project.php @@ -25,8 +25,6 @@ use App\Models\V2\Sites\SiteReport; use App\Models\V2\Tasks\Task; use App\Models\V2\TreeSpecies\TreeSpecies; -use App\Models\V2\Workdays\Workday; -use App\Models\V2\Workdays\WorkdayDemographic; use App\StateMachines\EntityStatusStateMachine; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -36,6 +34,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasManyThrough; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Support\Facades\DB; use Laravel\Scout\Searchable; use OwenIt\Auditing\Auditable; use OwenIt\Auditing\Contracts\Auditable as AuditableContract; @@ -121,18 +120,10 @@ class Project extends Model implements MediaModel, AuditableContract, EntityMode 'pct_beneficiaries_large', 'pct_beneficiaries_youth', 'land_tenure_project_area', + 'lat', + 'long', 'answers', 'ppc_external_id', - 'detailed_intervention_types', - 'proj_impact_foodsec', - 'pct_employees_marginalised', - 'pct_beneficiaries_marginalised', - 'pct_beneficiaries_men', - 'proposed_gov_partners', - 'proposed_num_nurseries', - 'proj_boundary', - 'states', - 'proj_impact_biodiv', ]; public $fileConfiguration = [ @@ -181,8 +172,6 @@ class Project extends Model implements MediaModel, AuditableContract, EntityMode 'restoration_strategy' => 'array', 'sdgs_impacted' => 'array', 'answers' => 'array', - 'detailed_intervention_types' => 'array', - 'states' => 'array', ]; public const PROJECT_STATUS_NEW = 'new_project'; @@ -359,17 +348,16 @@ public function getRegeneratedTreesCountAttribute(): int public function getWorkdayCountAttribute(): int { - return WorkdayDemographic::whereIn( - 'workday_id', - Workday::where('workdayable_type', SiteReport::class) - ->whereIn('workdayable_id', $this->submittedSiteReports()->select('v2_site_reports.id')) - ->select('id') - )->orWhereIn( - 'workday_id', - Workday::where('workdayable_type', ProjectReport::class) - ->whereIn('workdayable_id', $this->reports()->hasBeenSubmitted()->select('id')) - ->select('id') - )->gender()->sum('amount') ?? 0; + $sumQueries = [ + DB::raw('sum(`workdays_paid`) as paid'), + DB::raw('sum(`workdays_volunteer`) as volunteer'), + ]; + $projectTotals = $this->reports()->hasBeenSubmitted()->get($sumQueries)->first(); + // The groupBy is superfluous, but required because Laravel adds "v2_sites.project_id as laravel_through_key" to + // the SQL select. + $siteTotals = $this->submittedSiteReports()->groupBy('v2_sites.project_id')->get($sumQueries)->first(); + + return $projectTotals?->paid + $projectTotals?->volunteer + $siteTotals?->paid + $siteTotals?->volunteer; } public function getTotalJobsCreatedAttribute(): int @@ -481,4 +469,9 @@ private function submittedSiteReportIds(): array { return $this->submittedSiteReports()->pluck('v2_site_reports.id')->toArray(); } + + public function getTotalSitePolygons() + { + return $this->sitePolygons()->count(); + } } diff --git a/app/Models/V2/Sites/SitePolygon.php b/app/Models/V2/Sites/SitePolygon.php index dd420a334..ebd7b3a79 100644 --- a/app/Models/V2/Sites/SitePolygon.php +++ b/app/Models/V2/Sites/SitePolygon.php @@ -28,22 +28,18 @@ class SitePolygon extends Model protected $table = 'site_polygon'; protected $fillable = [ - 'proj_name', - 'org_name', 'poly_id', 'poly_name', 'site_id', - 'site_name', 'project_id', - 'poly_label', 'plantstart', 'plantend', 'practice', 'target_sys', 'distr', 'num_trees', - 'est_area', - 'country', + 'calc_area', + 'status', 'created_by', ]; @@ -67,6 +63,11 @@ public function project(): BelongsToThrough ); } + public function site() + { + return $this->belongsTo(Site::class, 'site_id', 'id'); + } + public function createdBy(): HasOne { return $this->hasOne(User::class, 'id', 'created_by'); diff --git a/openapi-src/V2/definitions/DashboardBBOXCountry.yml b/openapi-src/V2/definitions/DashboardBBOXCountry.yml new file mode 100644 index 000000000..ba34c2f2a --- /dev/null +++ b/openapi-src/V2/definitions/DashboardBBOXCountry.yml @@ -0,0 +1,4 @@ +type: object +properties: + bbox: + type: array \ No newline at end of file diff --git a/openapi-src/V2/definitions/DashboardBBOXProject.yml b/openapi-src/V2/definitions/DashboardBBOXProject.yml new file mode 100644 index 000000000..ba34c2f2a --- /dev/null +++ b/openapi-src/V2/definitions/DashboardBBOXProject.yml @@ -0,0 +1,4 @@ +type: object +properties: + bbox: + type: array \ No newline at end of file diff --git a/openapi-src/V2/definitions/DashboardGetPolygonStatusResponse.yml b/openapi-src/V2/definitions/DashboardGetPolygonStatusResponse.yml new file mode 100644 index 000000000..c32e7b879 --- /dev/null +++ b/openapi-src/V2/definitions/DashboardGetPolygonStatusResponse.yml @@ -0,0 +1,13 @@ +properties: + data: + type: array + properties: + NeedsMoreInfo: + type: array + description: Ids of polygons that need more information + Submitted: + type: array + description: Ids of submitted polygons + Approved: + type: array + description: Ids of approved polygons \ No newline at end of file diff --git a/openapi-src/V2/definitions/DashboardGetProjectsData.yml b/openapi-src/V2/definitions/DashboardGetProjectsData.yml new file mode 100644 index 000000000..1a11b4df2 --- /dev/null +++ b/openapi-src/V2/definitions/DashboardGetProjectsData.yml @@ -0,0 +1,12 @@ +type: object +properties: + uuid: + type: string + name: + type: string + lat: + type: number + format: double + long: + type: number + format: double \ No newline at end of file diff --git a/openapi-src/V2/definitions/DashboardGetProjectsResponse.yml b/openapi-src/V2/definitions/DashboardGetProjectsResponse.yml new file mode 100644 index 000000000..1b5258c43 --- /dev/null +++ b/openapi-src/V2/definitions/DashboardGetProjectsResponse.yml @@ -0,0 +1,6 @@ +type: object +properties: + data: + type: array + items: + $ref: './_index.yml#/DashboardGetProjectsData' \ No newline at end of file diff --git a/openapi-src/V2/definitions/DashboardPolygonData.yml b/openapi-src/V2/definitions/DashboardPolygonData.yml new file mode 100644 index 000000000..476d85e3a --- /dev/null +++ b/openapi-src/V2/definitions/DashboardPolygonData.yml @@ -0,0 +1,16 @@ +type: object +properties: + data: + type: array + items: + type: object + properties: + title: + type: string + description: Title of the data field + value: + type: string + description: Value of the data field + key: + type: string + description: Key of the data field \ No newline at end of file diff --git a/openapi-src/V2/definitions/_index.yml b/openapi-src/V2/definitions/_index.yml index bc5beab0d..4bfbf593b 100644 --- a/openapi-src/V2/definitions/_index.yml +++ b/openapi-src/V2/definitions/_index.yml @@ -285,4 +285,16 @@ DashboardRestorationStrategyResponse: DashboardTreeRestorationGoalResponse: $ref: './DashboardTreeRestorationGoalResponse.yml' DashboardTreesUnderRestorationActual: - $ref: './DashboardTreesUnderRestorationActual.yml' \ No newline at end of file + $ref: './DashboardTreesUnderRestorationActual.yml' +DashboardGetProjectsResponse: + $ref: './DashboardGetProjectsResponse.yml' +DashboardGetProjectsData: + $ref: './DashboardGetProjectsData.yml' +DashboardGetPolygonStatusResponse: + $ref: './DashboardGetPolygonStatusResponse.yml' +DashboardBBOXProject: + $ref: './DashboardBBOXProject.yml' +DashboardBBOXCountry: + $ref: './DashboardBBOXCountry.yml' +DashboardPolygonData: + $ref: './DashboardPolygonData.yml' \ No newline at end of file diff --git a/openapi-src/V2/paths/Dashboard/get-v2-dashboard-country.yml b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-country.yml new file mode 100644 index 000000000..e2cea3338 --- /dev/null +++ b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-country.yml @@ -0,0 +1,16 @@ +summary: Get the bounding box of a country +tags: + - Country +parameters: + - in: path + name: country + type: string + description: ISO code of the country + required: true +responses: + '200': + description: Successful response + schema: + $ref: '../../definitions/_index.yml#/DashboardBBOXCountry' + '404': + description: Country not found \ No newline at end of file diff --git a/openapi-src/V2/paths/Dashboard/get-v2-dashboard-get-bbox-project.yml b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-get-bbox-project.yml new file mode 100644 index 000000000..ffef58233 --- /dev/null +++ b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-get-bbox-project.yml @@ -0,0 +1,17 @@ +get: + summary: Get Bbox of all polygons of project + tags: + - Projects + parameters: + - in: query + name: uuid + type: string + description: UUID of the project + required: true + responses: + '200': + description: Successful response + schema: + $ref: ../../definitions/_index.yml#/DashboardBBOXProject + '404': + description: Project not found \ No newline at end of file diff --git a/openapi-src/V2/paths/Dashboard/get-v2-dashboard-get-polygons-statuses.yml b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-get-polygons-statuses.yml new file mode 100644 index 000000000..e78e1137c --- /dev/null +++ b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-get-polygons-statuses.yml @@ -0,0 +1,19 @@ +summary: Retrieve all polygons. +description: | + This endpoint returns all polygons by project uuid. +parameters: + - in: query + name: uuid + type: string + description: uuid for the given project +responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '../../definitions/_index.yml#/DashboardGetPolygonStatusResponse' + '400': + description: Bad request + '500': + description: Internal server error \ No newline at end of file diff --git a/openapi-src/V2/paths/Dashboard/get-v2-dashboard-get-polygons.yml b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-get-polygons.yml new file mode 100644 index 000000000..280dbb9e4 --- /dev/null +++ b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-get-polygons.yml @@ -0,0 +1,19 @@ +summary: Retrieve all polygons. +description: | + This endpoint returns all polygons by project uuid. +parameters: + - in: query + name: uuid + type: string + description: uuid for the given project +responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '../../definitions/_index.yml#/DashboardGetProjectsResponse' + '400': + description: Bad request + '500': + description: Internal server error \ No newline at end of file diff --git a/openapi-src/V2/paths/Dashboard/get-v2-dashboard-jobs-created.yml b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-jobs-created.yml index 31cf88f42..cb42b61cc 100644 --- a/openapi-src/V2/paths/Dashboard/get-v2-dashboard-jobs-created.yml +++ b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-jobs-created.yml @@ -1,4 +1,3 @@ -operationId: get-v2-jobs-created.yml summary: view Jobs created for dashboard parameters: - in: query diff --git a/openapi-src/V2/paths/Dashboard/get-v2-dashboard-polygon-data-uuid.yml b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-polygon-data-uuid.yml new file mode 100644 index 000000000..c74a4d4ec --- /dev/null +++ b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-polygon-data-uuid.yml @@ -0,0 +1,16 @@ +summary: Get Get allowed to project +tags: + - Get allowed to project +parameters: + - in: path + name: uuid + type: string + description: UUID of the polygon + required: true +responses: + '200': + description: Successful response + schema: + $ref: '../../definitions/_index.yml#/DashboardPolygonData' + '404': + description: Polygon not found \ No newline at end of file diff --git a/openapi-src/V2/paths/Dashboard/get-v2-dashboard-project-data-uuid.yml b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-project-data-uuid.yml new file mode 100644 index 000000000..964c02a42 --- /dev/null +++ b/openapi-src/V2/paths/Dashboard/get-v2-dashboard-project-data-uuid.yml @@ -0,0 +1,16 @@ +summary: Get project point data by UUID +tags: + - Project point data by UUID +parameters: + - in: path + name: uuid + type: string + description: UUID of the project point + required: true +responses: + '200': + description: Successful response + schema: + $ref: '../../definitions/_index.yml#/DashboardPolygonData' + '500': + description: Error in queries \ No newline at end of file diff --git a/openapi-src/V2/paths/_index.yml b/openapi-src/V2/paths/_index.yml index f4ab06285..3f13e1bdc 100644 --- a/openapi-src/V2/paths/_index.yml +++ b/openapi-src/V2/paths/_index.yml @@ -2534,4 +2534,22 @@ $ref: './Dashboard/get-v2-dashboard-tree-restoration-goal.yml' '/v2/dashboard/project-list-export': get: - $ref: './Dashboard/get-v2-dashboard-project-list-export.yml' \ No newline at end of file + $ref: './Dashboard/get-v2-dashboard-project-list-export.yml' +/v2/dashboard/get-polygons: + get: + $ref: './Dashboard/get-v2-dashboard-get-polygons.yml' +/v2/dashboard/get-polygons/statuses: + get: + $ref: './Dashboard/get-v2-dashboard-get-polygons-statuses.yml' +/v2/dashboard/get-bbox-project: + get: + $ref: './Dashboard/get-v2-dashboard-get-bbox-project.yml' +/v2/dashboard/country/{country}: + get: + $ref: './Dashboard/get-v2-dashboard-country.yml' +/v2/dashboard/polygon-data/{uuid}: + get: + $ref: './Dashboard/get-v2-dashboard-polygon-data-uuid.yml' +/v2/dashboard/project-data/{uuid}: + get: + $ref: './Dashboard/get-v2-dashboard-project-data-uuid.yml' \ No newline at end of file diff --git a/package.json b/package.json index 6a32704fa..f5150c91a 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "hot": "mix watch --hot", "prod": "npm run production", "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", - "doc-v2": "swagger-cli bundle openapi-src/v2.yml --outfile resources/docs/swagger-v2.yml --type yaml --dereference" + "doc-v2": "swagger-cli bundle openapi-src/v2.yml --outfile resources/docs/swagger-v2.yml --type yaml --dereference", + "doc-main": "swagger-cli bundle openapi-src/v2.yml --outfile resources/docs/swagger-v2.yml --type yaml --dereference" }, "devDependencies": { "ajv": "^8.12.0", diff --git a/resources/docs/swagger-v2.yml b/resources/docs/swagger-v2.yml index 9195ba99d..2e6d77a77 100644 --- a/resources/docs/swagger-v2.yml +++ b/resources/docs/swagger-v2.yml @@ -44248,6 +44248,78 @@ definitions: type: integer treeSpeciesPercentage: type: number + DashboardGetProjectsResponse: + type: object + properties: + data: + type: array + items: + type: object + properties: + uuid: + type: string + name: + type: string + lat: + type: number + format: double + long: + type: number + format: double + DashboardGetProjectsData: + type: object + properties: + uuid: + type: string + name: + type: string + lat: + type: number + format: double + long: + type: number + format: double + DashboardGetPolygonStatusResponse: + properties: + data: + type: array + properties: + NeedsMoreInfo: + type: array + description: Ids of polygons that need more information + Submitted: + type: array + description: Ids of submitted polygons + Approved: + type: array + description: Ids of approved polygons + DashboardBBOXProject: + type: object + properties: + bbox: + type: array + DashboardBBOXCountry: + type: object + properties: + bbox: + type: array + DashboardPolygonData: + type: object + properties: + data: + type: array + items: + type: object + properties: + title: + type: string + description: Title of the data field + value: + type: string + description: Value of the data field + key: + type: string + description: Key of the data field paths: '/v2/tree-species/{entity}/{UUID}': get: @@ -94132,7 +94204,6 @@ paths: description: Geometry was not found. /v2/dashboard/jobs-created: get: - operationId: get-v2-jobs-created.yml summary: view Jobs created for dashboard parameters: - in: query @@ -94345,3 +94416,182 @@ paths: description: OK schema: type: file + /v2/dashboard/get-polygons: + get: + summary: Retrieve all polygons. + description: | + This endpoint returns all polygons by project uuid. + parameters: + - in: query + name: uuid + type: string + description: uuid for the given project + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + type: object + properties: + uuid: + type: string + name: + type: string + lat: + type: number + format: double + long: + type: number + format: double + '400': + description: Bad request + '500': + description: Internal server error + /v2/dashboard/get-polygons/statuses: + get: + summary: Retrieve all polygons. + description: | + This endpoint returns all polygons by project uuid. + parameters: + - in: query + name: uuid + type: string + description: uuid for the given project + responses: + '200': + description: Successful response + content: + application/json: + schema: + properties: + data: + type: array + properties: + NeedsMoreInfo: + type: array + description: Ids of polygons that need more information + Submitted: + type: array + description: Ids of submitted polygons + Approved: + type: array + description: Ids of approved polygons + '400': + description: Bad request + '500': + description: Internal server error + /v2/dashboard/get-bbox-project: + get: + get: + summary: Get Bbox of all polygons of project + tags: + - Projects + parameters: + - in: query + name: uuid + type: string + description: UUID of the project + required: true + responses: + '200': + description: Successful response + schema: + type: object + properties: + bbox: + type: array + '404': + description: Project not found + '/v2/dashboard/country/{country}': + get: + summary: Get the bounding box of a country + tags: + - Country + parameters: + - in: path + name: country + type: string + description: ISO code of the country + required: true + responses: + '200': + description: Successful response + schema: + type: object + properties: + bbox: + type: array + '404': + description: Country not found + '/v2/dashboard/polygon-data/{uuid}': + get: + summary: Get Get allowed to project + tags: + - Get allowed to project + parameters: + - in: path + name: uuid + type: string + description: UUID of the polygon + required: true + responses: + '200': + description: Successful response + schema: + type: object + properties: + data: + type: array + items: + type: object + properties: + title: + type: string + description: Title of the data field + value: + type: string + description: Value of the data field + key: + type: string + description: Key of the data field + '404': + description: Polygon not found + '/v2/dashboard/project-data/{uuid}': + get: + summary: Get project point data by UUID + tags: + - Project point data by UUID + parameters: + - in: path + name: uuid + type: string + description: UUID of the project point + required: true + responses: + '200': + description: Successful response + schema: + type: object + properties: + data: + type: array + items: + type: object + properties: + title: + type: string + description: Title of the data field + value: + type: string + description: Value of the data field + key: + type: string + description: Key of the data field + '500': + description: Error in queries diff --git a/routes/api_v2.php b/routes/api_v2.php index acfb65c66..951dd7727 100644 --- a/routes/api_v2.php +++ b/routes/api_v2.php @@ -15,7 +15,9 @@ use App\Http\Controllers\V2\CoreTeamLeader\DeleteCoreTeamLeaderController; use App\Http\Controllers\V2\CoreTeamLeader\StoreCoreTeamLeaderController; use App\Http\Controllers\V2\CoreTeamLeader\UpdateCoreTeamLeaderController; +use App\Http\Controllers\V2\Dashboard\CountryDataController; use App\Http\Controllers\V2\Dashboard\GetJobsCreatedController; +use App\Http\Controllers\V2\Dashboard\GetPolygonsController; use App\Http\Controllers\V2\Dashboard\ProjectListExportController; use App\Http\Controllers\V2\Dashboard\ViewRestorationStrategyController; use App\Http\Controllers\V2\Dashboard\ViewTreeRestorationGoalController; @@ -659,4 +661,10 @@ function () { Route::get('/jobs-created', GetJobsCreatedController::class); Route::get('/tree-restoration-goal', ViewTreeRestorationGoalController::class); Route::get('/project-list-export', ProjectListExportController::class); + Route::get('/get-polygons', [GetPolygonsController::class, 'getPolygonsOfProject']); + Route::get('/get-polygons/statuses', [GetPolygonsController::class, 'getPolygonsByStatusOfProject']); + Route::get('/get-bbox-project', [GetPolygonsController::class, 'getBboxOfCompleteProject']); + Route::get('/country/{country}', [CountryDataController::class, 'getCountryBbox']); + Route::get('/polygon-data/{uuid}', [CountryDataController::class, 'getPolygonData']); + Route::get('/project-data/{uuid}', [CountryDataController::class, 'getProjectData']); }); From 99d84ae13e232ea9d26e3b77caf1352480466470 Mon Sep 17 00:00:00 2001 From: JORGE Date: Tue, 4 Jun 2024 14:09:15 -0400 Subject: [PATCH 10/18] [TM-671] improve queries, remove superfluos code, update Project model from staging --- app/Helpers/GeometryHelper.php | 18 ++++----- .../V2/Dashboard/CountryDataController.php | 12 +++--- app/Models/V2/Projects/Project.php | 37 +++++++++++++------ package.json | 3 +- 4 files changed, 40 insertions(+), 30 deletions(-) diff --git a/app/Helpers/GeometryHelper.php b/app/Helpers/GeometryHelper.php index b0ce5cfb8..eca5040eb 100644 --- a/app/Helpers/GeometryHelper.php +++ b/app/Helpers/GeometryHelper.php @@ -15,15 +15,12 @@ public function centroidOfProject($projectUuid) if (! $project) { return null; } + $polyIds = $project->sitePolygons()->pluck('poly_id')->toArray(); - $sitePolygons = $project->sitePolygons; - - if ($sitePolygons->isEmpty()) { - return null; // Return null if no polygons are found for the given projectUuid + if (empty($polyIds)) { + return null; } - $polyIds = $sitePolygons->pluck('poly_id')->toArray(); - $centroids = PolygonGeometry::selectRaw('ST_AsGeoJSON(ST_Centroid(geom)) AS centroid') ->whereIn('uuid', $polyIds) ->get(); @@ -77,7 +74,10 @@ public function updateProjectCentroid(string $projectUuid) Log::info("Centroid updated for projectUuid: $projectUuid"); - return 'Centroids updated successfully!'; + return response()->json([ + 'message' => 'Centroid updated', + 'centroid' => $centroid, + ], 200); } catch (\Exception $e) { Log::error("Error updating centroid for projectUuid: $projectUuid"); @@ -112,8 +112,6 @@ public static function getPolygonsBbox($polygonsIds) } } - $bboxCoordinates = [$minX, $minY, $maxX, $maxY]; - - return $bboxCoordinates; + return [$minX, $minY, $maxX, $maxY]; } } diff --git a/app/Http/Controllers/V2/Dashboard/CountryDataController.php b/app/Http/Controllers/V2/Dashboard/CountryDataController.php index a11ec59f6..a6e884461 100644 --- a/app/Http/Controllers/V2/Dashboard/CountryDataController.php +++ b/app/Http/Controllers/V2/Dashboard/CountryDataController.php @@ -80,21 +80,19 @@ public function getProjectData(string $uuid) if (! $project) { Log::error("Project not found for project with UUID: $uuid"); + return response()->json(['error' => 'Project not found'], 404); } - $countSitePolygons = 0; - if($project) { - $countSitePolygons = $project->getTotalSitePolygons(); - } + $countSitePolygons = $project?->getTotalSitePolygons() ?? 0; $organization = $project->organisation()->first(); if (! $organization) { Log::error("Organization not found for project with ID: $project->id"); } - $country = WorldCountryGeneralized::where('iso', $project->country)->first(); + $country = $project ? WorldCountryGeneralized::where('iso', $project->country)->first() : null; $data = [ - ['key' => 'project_name', 'title' => 'title', 'value' => $project->name ?? null], - ['key' => 'country', 'title' => 'Country', 'value' => $country->country ?? null], + ['key' => 'project_name', 'title' => 'title', 'value' => $project?->name ?? null], + ['key' => 'country', 'title' => 'Country', 'value' => $country?->country ?? null], ['key' => 'polygon_counts', 'title' => 'No. of Site - Polygons', 'value' => $countSitePolygons ?? null], ['key' => 'organizations', 'title' => 'Organization', 'value' => $organization->name ?? null], ]; diff --git a/app/Models/V2/Projects/Project.php b/app/Models/V2/Projects/Project.php index a5d522186..05502b403 100644 --- a/app/Models/V2/Projects/Project.php +++ b/app/Models/V2/Projects/Project.php @@ -25,6 +25,8 @@ use App\Models\V2\Sites\SiteReport; use App\Models\V2\Tasks\Task; use App\Models\V2\TreeSpecies\TreeSpecies; +use App\Models\V2\Workdays\Workday; +use App\Models\V2\Workdays\WorkdayDemographic; use App\StateMachines\EntityStatusStateMachine; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -124,6 +126,16 @@ class Project extends Model implements MediaModel, AuditableContract, EntityMode 'long', 'answers', 'ppc_external_id', + 'detailed_intervention_types', + 'proj_impact_foodsec', + 'pct_employees_marginalised', + 'pct_beneficiaries_marginalised', + 'pct_beneficiaries_men', + 'proposed_gov_partners', + 'proposed_num_nurseries', + 'proj_boundary', + 'states', + 'proj_impact_biodiv', ]; public $fileConfiguration = [ @@ -172,6 +184,8 @@ class Project extends Model implements MediaModel, AuditableContract, EntityMode 'restoration_strategy' => 'array', 'sdgs_impacted' => 'array', 'answers' => 'array', + 'detailed_intervention_types' => 'array', + 'states' => 'array', ]; public const PROJECT_STATUS_NEW = 'new_project'; @@ -348,16 +362,17 @@ public function getRegeneratedTreesCountAttribute(): int public function getWorkdayCountAttribute(): int { - $sumQueries = [ - DB::raw('sum(`workdays_paid`) as paid'), - DB::raw('sum(`workdays_volunteer`) as volunteer'), - ]; - $projectTotals = $this->reports()->hasBeenSubmitted()->get($sumQueries)->first(); - // The groupBy is superfluous, but required because Laravel adds "v2_sites.project_id as laravel_through_key" to - // the SQL select. - $siteTotals = $this->submittedSiteReports()->groupBy('v2_sites.project_id')->get($sumQueries)->first(); - - return $projectTotals?->paid + $projectTotals?->volunteer + $siteTotals?->paid + $siteTotals?->volunteer; + return WorkdayDemographic::whereIn( + 'workday_id', + Workday::where('workdayable_type', SiteReport::class) + ->whereIn('workdayable_id', $this->submittedSiteReports()->select('v2_site_reports.id')) + ->select('id') + )->orWhereIn( + 'workday_id', + Workday::where('workdayable_type', ProjectReport::class) + ->whereIn('workdayable_id', $this->reports()->hasBeenSubmitted()->select('id')) + ->select('id') + )->gender()->sum('amount') ?? 0; } public function getSelfReportedWorkdayCountAttribute(): int @@ -488,4 +503,4 @@ public function getTotalSitePolygons() { return $this->sitePolygons()->count(); } -} +} \ No newline at end of file diff --git a/package.json b/package.json index f5150c91a..6a32704fa 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,7 @@ "hot": "mix watch --hot", "prod": "npm run production", "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", - "doc-v2": "swagger-cli bundle openapi-src/v2.yml --outfile resources/docs/swagger-v2.yml --type yaml --dereference", - "doc-main": "swagger-cli bundle openapi-src/v2.yml --outfile resources/docs/swagger-v2.yml --type yaml --dereference" + "doc-v2": "swagger-cli bundle openapi-src/v2.yml --outfile resources/docs/swagger-v2.yml --type yaml --dereference" }, "devDependencies": { "ajv": "^8.12.0", From 1310d5720c3aa049eb5bb4d539d6ed7300724a6e Mon Sep 17 00:00:00 2001 From: JORGE Date: Tue, 4 Jun 2024 15:02:27 -0400 Subject: [PATCH 11/18] [TM-671] remove inappropiate code, make more idiomatic uses of queries --- .../V2/Dashboard/CountryDataController.php | 12 ++++++------ app/Models/V2/Projects/Project.php | 8 ++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/app/Http/Controllers/V2/Dashboard/CountryDataController.php b/app/Http/Controllers/V2/Dashboard/CountryDataController.php index a6e884461..533ff5ec5 100644 --- a/app/Http/Controllers/V2/Dashboard/CountryDataController.php +++ b/app/Http/Controllers/V2/Dashboard/CountryDataController.php @@ -82,19 +82,19 @@ public function getProjectData(string $uuid) Log::error("Project not found for project with UUID: $uuid"); return response()->json(['error' => 'Project not found'], 404); } - $countSitePolygons = $project?->getTotalSitePolygons() ?? 0; + $countSitePolygons = $project->total_site_polygons; $organization = $project->organisation()->first(); if (! $organization) { Log::error("Organization not found for project with ID: $project->id"); } - $country = $project ? WorldCountryGeneralized::where('iso', $project->country)->first() : null; + $country = WorldCountryGeneralized::where('iso', $project->country)->first(); $data = [ - ['key' => 'project_name', 'title' => 'title', 'value' => $project?->name ?? null], - ['key' => 'country', 'title' => 'Country', 'value' => $country?->country ?? null], - ['key' => 'polygon_counts', 'title' => 'No. of Site - Polygons', 'value' => $countSitePolygons ?? null], - ['key' => 'organizations', 'title' => 'Organization', 'value' => $organization->name ?? null], + ['key' => 'project_name', 'title' => 'title', 'value' => $project->name], + ['key' => 'country', 'title' => 'Country', 'value' => $country?->country], + ['key' => 'polygon_counts', 'title' => 'No. of Site - Polygons', 'value' => $countSitePolygons], + ['key' => 'organizations', 'title' => 'Organization', 'value' => $organization?->name], ]; return response()->json(['data' => $data]); diff --git a/app/Models/V2/Projects/Project.php b/app/Models/V2/Projects/Project.php index 05502b403..70a57fd0e 100644 --- a/app/Models/V2/Projects/Project.php +++ b/app/Models/V2/Projects/Project.php @@ -131,11 +131,7 @@ class Project extends Model implements MediaModel, AuditableContract, EntityMode 'pct_employees_marginalised', 'pct_beneficiaries_marginalised', 'pct_beneficiaries_men', - 'proposed_gov_partners', - 'proposed_num_nurseries', - 'proj_boundary', - 'states', - 'proj_impact_biodiv', + 'proposed_gov_partners' ]; public $fileConfiguration = [ @@ -499,7 +495,7 @@ private function submittedSiteReportIds(): array return $this->submittedSiteReports()->pluck('v2_site_reports.id')->toArray(); } - public function getTotalSitePolygons() + public function getTotalSitePolygonsAttribute() { return $this->sitePolygons()->count(); } From 21fb955f736b7b0c4378581fad06dab1b9a3fb1d Mon Sep 17 00:00:00 2001 From: JORGE Date: Wed, 5 Jun 2024 15:47:28 -0400 Subject: [PATCH 12/18] return fillable values from staging --- app/Models/V2/Projects/Project.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Models/V2/Projects/Project.php b/app/Models/V2/Projects/Project.php index 70a57fd0e..b7509644e 100644 --- a/app/Models/V2/Projects/Project.php +++ b/app/Models/V2/Projects/Project.php @@ -131,7 +131,11 @@ class Project extends Model implements MediaModel, AuditableContract, EntityMode 'pct_employees_marginalised', 'pct_beneficiaries_marginalised', 'pct_beneficiaries_men', - 'proposed_gov_partners' + 'proposed_gov_partners', + 'proposed_num_nurseries', + 'proj_boundary', + 'states', + 'proj_impact_biodiv' ]; public $fileConfiguration = [ From b256cbc2295b3e730cd805a58c1a320a58926159 Mon Sep 17 00:00:00 2001 From: JORGE Date: Thu, 6 Jun 2024 11:33:10 -0400 Subject: [PATCH 13/18] [TM-617, 626, 627, 658] remove unnecesary class for activeprojecttableresource --- .../ActiveProjectsTableController.php | 5 ++--- .../Dashboard/ActiveProjectsTableResource.php | 19 ------------------- 2 files changed, 2 insertions(+), 22 deletions(-) delete mode 100644 app/Http/Resources/V2/Dashboard/ActiveProjectsTableResource.php diff --git a/app/Http/Controllers/V2/Dashboard/ActiveProjectsTableController.php b/app/Http/Controllers/V2/Dashboard/ActiveProjectsTableController.php index c38249bfe..3ece3a76b 100644 --- a/app/Http/Controllers/V2/Dashboard/ActiveProjectsTableController.php +++ b/app/Http/Controllers/V2/Dashboard/ActiveProjectsTableController.php @@ -4,7 +4,6 @@ use App\Helpers\TerrafundDashboardQueryHelper; use App\Http\Controllers\Controller; -use App\Http\Resources\V2\Dashboard\ActiveProjectsTableResource; use App\Models\V2\Forms\FormOptionList; use App\Models\V2\Forms\FormOptionListOption; use Illuminate\Http\Request; @@ -13,7 +12,7 @@ class ActiveProjectsTableController extends Controller { - public function __invoke(Request $request): ActiveProjectsTableResource + public function __invoke(Request $request) { $perPage = $request->input('per_page', PHP_INT_MAX); $page = $request->input('page', 1); @@ -21,7 +20,7 @@ public function __invoke(Request $request): ActiveProjectsTableResource $projects = $this->getAllProjects($request, $perPage, $page); $count = $this->getQuery($request)->count(); - return new ActiveProjectsTableResource([ + return response()->json([ 'current_page' => $page, 'data' => $projects, 'per_page' => $perPage, diff --git a/app/Http/Resources/V2/Dashboard/ActiveProjectsTableResource.php b/app/Http/Resources/V2/Dashboard/ActiveProjectsTableResource.php deleted file mode 100644 index 5fd0250ac..000000000 --- a/app/Http/Resources/V2/Dashboard/ActiveProjectsTableResource.php +++ /dev/null @@ -1,19 +0,0 @@ -resource; - } -} From 89caf9f992bcb4e7bacefbafeaffff5898fb4b82 Mon Sep 17 00:00:00 2001 From: JORGE Date: Thu, 6 Jun 2024 11:36:58 -0400 Subject: [PATCH 14/18] [TM-617, 626, 627, 658] remove unnecesary parse of class --- .../V2/Dashboard/ViewRestorationStrategyController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/V2/Dashboard/ViewRestorationStrategyController.php b/app/Http/Controllers/V2/Dashboard/ViewRestorationStrategyController.php index 32ca7ce77..a4e15eab5 100644 --- a/app/Http/Controllers/V2/Dashboard/ViewRestorationStrategyController.php +++ b/app/Http/Controllers/V2/Dashboard/ViewRestorationStrategyController.php @@ -30,7 +30,7 @@ public function __invoke(Request $request): JsonResponse 'landTenures' => $this->getResultArray($landTenures, 'land_tenure'), ]; - return new JsonResponse(ViewRestorationStrategyResource::make($result)); + return response()->json($result); } private function buildProjectQuery(Request $request) From 28815843d8225f0330ffa87e56ef6581565ace79 Mon Sep 17 00:00:00 2001 From: JORGE Date: Thu, 6 Jun 2024 11:42:57 -0400 Subject: [PATCH 15/18] [TM-617, 626, 627, 658] move to constant of the class --- .../Dashboard/ProjectListExportController.php | 9 ++++---- .../ViewRestorationStrategyController.php | 22 +++++++----------- .../ViewRestorationStrategyResource.php | 23 ------------------- 3 files changed, 12 insertions(+), 42 deletions(-) delete mode 100644 app/Http/Resources/V2/Dashboard/ViewRestorationStrategyResource.php diff --git a/app/Http/Controllers/V2/Dashboard/ProjectListExportController.php b/app/Http/Controllers/V2/Dashboard/ProjectListExportController.php index 9d6d1611e..16336cf90 100644 --- a/app/Http/Controllers/V2/Dashboard/ProjectListExportController.php +++ b/app/Http/Controllers/V2/Dashboard/ProjectListExportController.php @@ -53,11 +53,10 @@ public function exportCsv($request) $csvContent = $csv->toString(); - $fileName = 'activeProject.csv'; - return response($csvContent, 200, [ - 'Content-Type' => 'text/csv', - 'Content-Disposition' => 'attachment; filename="' . $fileName . '"', - ]); + 'Content-Type' => 'text/csv', + 'Content-Disposition' => 'attachment; filename=activeProject.csv', + ]); + } } diff --git a/app/Http/Controllers/V2/Dashboard/ViewRestorationStrategyController.php b/app/Http/Controllers/V2/Dashboard/ViewRestorationStrategyController.php index a4e15eab5..b749c7ac3 100644 --- a/app/Http/Controllers/V2/Dashboard/ViewRestorationStrategyController.php +++ b/app/Http/Controllers/V2/Dashboard/ViewRestorationStrategyController.php @@ -4,7 +4,6 @@ use App\Helpers\TerrafundDashboardQueryHelper; use App\Http\Controllers\Controller; -use App\Http\Resources\V2\Dashboard\ViewRestorationStrategyResource; use App\Models\V2\Sites\Site; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -12,6 +11,10 @@ class ViewRestorationStrategyController extends Controller { + protected const RESTORATION_STRATEGIES = ['direct-seeding', 'tree-planting', 'assisted-natural-regeneration']; + protected const LAND_USE_TYPES = ['agroforest', 'open-natural-ecosystem', 'mangrove', 'natural-forest', 'peatland', 'riparian-area-or-wetland', 'silvopasture', 'urban-forest', 'woodlot-or-plantation']; + protected const LAND_TENURES = ['private', 'public', 'indigenous', 'other', 'national_protected_area', 'communal']; + public function __invoke(Request $request): JsonResponse { $query = $this->buildProjectQuery($request); @@ -35,19 +38,14 @@ public function __invoke(Request $request): JsonResponse private function buildProjectQuery(Request $request) { - - $query = TerrafundDashboardQueryHelper::buildQueryFromRequest($request); - - return $query; + return TerrafundDashboardQueryHelper::buildQueryFromRequest($request); } private function getRestorationStrategy(array $projectIds) { - $strategies = ['direct-seeding', 'tree-planting', 'assisted-natural-regeneration']; - $conditions = implode(' OR ', array_map(function ($strategy) { return "JSON_UNQUOTE(JSON_EXTRACT(restoration_strategy, CONCAT('\$[', numbers.n, ']'))) = '$strategy'"; - }, $strategies)); + }, self::RESTORATION_STRATEGIES)); $numbers = implode(' UNION ALL ', array_map(function ($n) { return "SELECT $n AS n"; @@ -71,11 +69,9 @@ private function getRestorationStrategy(array $projectIds) private function getLandUseType(array $projectIds) { - $landUseTypes = ['agroforest', 'open-natural-ecosystem', 'mangrove', 'natural-forest', 'peatland', 'riparian-area-or-wetland', 'silvopasture', 'urban-forest', 'woodlot-or-plantation']; - $conditions = implode(' OR ', array_map(function ($type) { return "JSON_UNQUOTE(JSON_EXTRACT(v2_sites.land_use_types, CONCAT('\$[', numbers.n, ']'))) = '$type'"; - }, $landUseTypes)); + }, self::LAND_USE_TYPES)); $numbers = implode(' UNION ALL ', array_map(function ($n) { return "SELECT $n AS n"; @@ -99,11 +95,9 @@ private function getLandUseType(array $projectIds) private function getLandTenures(array $projectIds) { - $landTenures = ['private', 'public', 'indigenous', 'other', 'national_protected_area', 'communal']; - $conditions = implode(' OR ', array_map(function ($type) { return "JSON_UNQUOTE(JSON_EXTRACT(v2_sites.land_tenures, CONCAT('\$[', numbers.n, ']'))) = '$type'"; - }, $landTenures)); + }, self::LAND_TENURES)); $numbers = implode(' UNION ALL ', array_map(function ($n) { return "SELECT $n AS n"; diff --git a/app/Http/Resources/V2/Dashboard/ViewRestorationStrategyResource.php b/app/Http/Resources/V2/Dashboard/ViewRestorationStrategyResource.php deleted file mode 100644 index 50e5d58b0..000000000 --- a/app/Http/Resources/V2/Dashboard/ViewRestorationStrategyResource.php +++ /dev/null @@ -1,23 +0,0 @@ - $this->resource['restorationStrategies'] ?? null, - 'landUseTypes' => $this->resource['landUseTypes'] ?? null, - 'landTenures' => $this->resource['landTenures'] ?? null, - ]; - } -} From 62779f6a64f3977a2144b308ffe78dd7e11c65ea Mon Sep 17 00:00:00 2001 From: JORGE Date: Thu, 6 Jun 2024 12:09:11 -0400 Subject: [PATCH 16/18] [TM-617, 626, 627, 658] add filter for approved and submitted in PROJECT REPORT --- .../V2/Dashboard/ViewTreeRestorationGoalController.php | 10 +++------- app/Models/V2/Projects/ProjectReport.php | 4 ++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/V2/Dashboard/ViewTreeRestorationGoalController.php b/app/Http/Controllers/V2/Dashboard/ViewTreeRestorationGoalController.php index aaaa4f921..25fb7000b 100644 --- a/app/Http/Controllers/V2/Dashboard/ViewTreeRestorationGoalController.php +++ b/app/Http/Controllers/V2/Dashboard/ViewTreeRestorationGoalController.php @@ -61,10 +61,7 @@ public function __invoke(Request $request): JsonResponse private function prepareProjectQuery(Request $request) { - - $query = TerrafundDashboardQueryHelper::buildQueryFromRequest($request); - - return $query; + return TerrafundDashboardQueryHelper::buildQueryFromRequest($request); } private function getRawProjectIds($query) @@ -175,8 +172,7 @@ private function getLatestDueDate($distinctDates) private function getAverageSurvival(array $projectIds) { - $averageSurvivalRate = ProjectReport::whereIn('project_id', $projectIds)->avg('pct_survival_to_date'); - - return $averageSurvivalRate; + return ProjectReport::hasBeenSubmittedOrApproved()->whereIn('project_id', $projectIds)->avg('pct_survival_to_date'); } + } diff --git a/app/Models/V2/Projects/ProjectReport.php b/app/Models/V2/Projects/ProjectReport.php index cd2835fb7..98cacfb52 100644 --- a/app/Models/V2/Projects/ProjectReport.php +++ b/app/Models/V2/Projects/ProjectReport.php @@ -406,4 +406,8 @@ public function parentEntity(): BelongsTo { return $this->project(); } + public function scopeHasBeenSubmittedOrApproved($query) + { + return $query->whereIn('status', ['submitted', 'approved']); + } } From 09d30cc056d9ef336e9b316fc758a5117d0e31a9 Mon Sep 17 00:00:00 2001 From: JORGE Date: Thu, 6 Jun 2024 12:25:05 -0400 Subject: [PATCH 17/18] [TM-617, 626, 627, 658] add filter for approved only in PROJECT REPORT --- .../V2/Dashboard/ViewTreeRestorationGoalController.php | 3 +-- app/Models/V2/Projects/ProjectReport.php | 5 +---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/V2/Dashboard/ViewTreeRestorationGoalController.php b/app/Http/Controllers/V2/Dashboard/ViewTreeRestorationGoalController.php index 25fb7000b..c9ba5469c 100644 --- a/app/Http/Controllers/V2/Dashboard/ViewTreeRestorationGoalController.php +++ b/app/Http/Controllers/V2/Dashboard/ViewTreeRestorationGoalController.php @@ -172,7 +172,6 @@ private function getLatestDueDate($distinctDates) private function getAverageSurvival(array $projectIds) { - return ProjectReport::hasBeenSubmittedOrApproved()->whereIn('project_id', $projectIds)->avg('pct_survival_to_date'); + return ProjectReport::isApproved()->whereIn('project_id', $projectIds)->avg('pct_survival_to_date'); } - } diff --git a/app/Models/V2/Projects/ProjectReport.php b/app/Models/V2/Projects/ProjectReport.php index 98cacfb52..03f7a8bdd 100644 --- a/app/Models/V2/Projects/ProjectReport.php +++ b/app/Models/V2/Projects/ProjectReport.php @@ -23,6 +23,7 @@ use App\Models\V2\User; use App\Models\V2\Workdays\Workday; use App\Models\V2\Workdays\WorkdayDemographic; +use App\StateMachines\ReportStatusStateMachine; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -406,8 +407,4 @@ public function parentEntity(): BelongsTo { return $this->project(); } - public function scopeHasBeenSubmittedOrApproved($query) - { - return $query->whereIn('status', ['submitted', 'approved']); - } } From a5438ab2d27dcaaef189b4362f765ea6fb4c0c28 Mon Sep 17 00:00:00 2001 From: cesarLima1 Date: Thu, 6 Jun 2024 15:06:56 -0400 Subject: [PATCH 18/18] [TM-617] make lint fix --- app/Helpers/GeometryHelper.php | 2 +- app/Http/Controllers/V2/Dashboard/CountryDataController.php | 1 + .../Controllers/V2/Dashboard/ProjectListExportController.php | 2 +- app/Models/V2/Projects/Project.php | 4 ++-- app/Models/V2/Projects/ProjectReport.php | 1 - 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Helpers/GeometryHelper.php b/app/Helpers/GeometryHelper.php index eca5040eb..55c25234c 100644 --- a/app/Helpers/GeometryHelper.php +++ b/app/Helpers/GeometryHelper.php @@ -18,7 +18,7 @@ public function centroidOfProject($projectUuid) $polyIds = $project->sitePolygons()->pluck('poly_id')->toArray(); if (empty($polyIds)) { - return null; + return null; } $centroids = PolygonGeometry::selectRaw('ST_AsGeoJSON(ST_Centroid(geom)) AS centroid') diff --git a/app/Http/Controllers/V2/Dashboard/CountryDataController.php b/app/Http/Controllers/V2/Dashboard/CountryDataController.php index 533ff5ec5..48fcb98d2 100644 --- a/app/Http/Controllers/V2/Dashboard/CountryDataController.php +++ b/app/Http/Controllers/V2/Dashboard/CountryDataController.php @@ -80,6 +80,7 @@ public function getProjectData(string $uuid) if (! $project) { Log::error("Project not found for project with UUID: $uuid"); + return response()->json(['error' => 'Project not found'], 404); } $countSitePolygons = $project->total_site_polygons; diff --git a/app/Http/Controllers/V2/Dashboard/ProjectListExportController.php b/app/Http/Controllers/V2/Dashboard/ProjectListExportController.php index 16336cf90..1aa65a7af 100644 --- a/app/Http/Controllers/V2/Dashboard/ProjectListExportController.php +++ b/app/Http/Controllers/V2/Dashboard/ProjectListExportController.php @@ -57,6 +57,6 @@ public function exportCsv($request) 'Content-Type' => 'text/csv', 'Content-Disposition' => 'attachment; filename=activeProject.csv', ]); - + } } diff --git a/app/Models/V2/Projects/Project.php b/app/Models/V2/Projects/Project.php index c4ca52300..9350d5cd0 100644 --- a/app/Models/V2/Projects/Project.php +++ b/app/Models/V2/Projects/Project.php @@ -135,7 +135,7 @@ class Project extends Model implements MediaModel, AuditableContract, EntityMode 'proposed_num_nurseries', 'proj_boundary', 'states', - 'proj_impact_biodiv' + 'proj_impact_biodiv', ]; public $fileConfiguration = [ @@ -503,4 +503,4 @@ public function getTotalSitePolygonsAttribute() { return $this->sitePolygons()->count(); } -} \ No newline at end of file +} diff --git a/app/Models/V2/Projects/ProjectReport.php b/app/Models/V2/Projects/ProjectReport.php index 03b7118ab..6a740cc22 100644 --- a/app/Models/V2/Projects/ProjectReport.php +++ b/app/Models/V2/Projects/ProjectReport.php @@ -23,7 +23,6 @@ use App\Models\V2\User; use App\Models\V2\Workdays\Workday; use App\Models\V2\Workdays\WorkdayDemographic; -use App\StateMachines\ReportStatusStateMachine; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model;