Skip to content

Commit

Permalink
Merge pull request #444 from wri/release/nimble-neem
Browse files Browse the repository at this point in the history
[RELEASE] Nimble Neem
  • Loading branch information
roguenet authored Sep 5, 2024
2 parents c5cdd22 + a379de7 commit be7fa61
Show file tree
Hide file tree
Showing 43 changed files with 739 additions and 94 deletions.
83 changes: 83 additions & 0 deletions app/Console/Commands/OneOff/BackfillHidden.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

namespace App\Console\Commands\OneOff;

use App\Models\V2\Nurseries\NurseryReport;
use App\Models\V2\Projects\ProjectReport;
use App\Models\V2\Sites\SiteReport;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

class BackfillHidden extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'one-off:backfill-hidden';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Fills the \'hidden\' field of report-associated models based on form responses';

private const REPORT_TYPES = [
ProjectReport::class,
SiteReport::class,
NurseryReport::class,
];

/**
* Execute the console command.
*/
public function handle()
{
$count = 0;
foreach (self::REPORT_TYPES as $reportType) {
$count += $reportType::count();
}
$bar = $this->output->createProgressBar($count);
$bar->start();

foreach (self::REPORT_TYPES as $reportType) {
$reportType::chunkById(100, function ($reports) use ($bar) {
foreach ($reports as $report) {
$this->findRelationsToHide($report);
$bar->advance();
}
});
}
$bar->finish();
}

protected function findRelationsToHide($entity)
{
$questions = $entity->getForm()->sections->map(fn ($section) => $section->questions)->flatten();
$formConfig = $entity->getFormConfig();
$linkedFieldQuestions = $questions->filter(function ($question) use ($formConfig) {
$property = data_get($formConfig, "relations.$question->linked_field_key.property");

return ! empty($property) && ! empty($question->parent_id);
});

foreach ($linkedFieldQuestions as $question) {
if (empty($entity->answers) || $entity->answers[$question->parent_id] !== false) {
continue;
}

$relation = $entity->{$question->input_type}();
$collection = $question->collection;
if (! empty($collection)) {
$relation->where('collection', $collection);
}

$count = $relation->count();
if ($count > 0) {
$relation->update(['hidden' => true, 'updated_at' => DB::raw('updated_at')]);
}
}
}
}
8 changes: 6 additions & 2 deletions app/Exports/V2/BaseExportFormSubmission.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,15 @@ protected function getAnswer(array $field, array $answers): string
return $this->stringifyModel($answer, ['first_name', 'last_name', 'title', 'gender', 'percent_ownership', 'year_of_birth',]);

default:
return json_encode($answer);
$utf8Answer = mb_convert_encoding($answer, 'UTF-8', mb_detect_encoding($answer));

return json_encode($utf8Answer, JSON_UNESCAPED_UNICODE);
}
}

return $answer ?? '';
$utf8Answer = mb_convert_encoding($answer, 'UTF-8', mb_detect_encoding($answer));

return json_encode($utf8Answer, JSON_UNESCAPED_UNICODE);
}

protected function generateFieldMap(Form $form): array
Expand Down
20 changes: 14 additions & 6 deletions app/Exports/V2/EntityExport.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public function headings(): array
$headings = $this->getAttachedHeadingsForEntity();

foreach ($this->fieldMap as $field) {
// Skip the boundary_geojson field as it is not a field that should be exported,
// until being removed from the database.
if (isset($field['heading']) && $field['heading'] === 'boundary_geojson') {
continue;
}
$headings[] = data_get($field, 'heading', 'unknown') ;
}

Expand All @@ -53,11 +58,14 @@ public function map($entity): array
$mapped = $this->getAttachedMappedForEntity($entity);

foreach ($fieldMap as $field) {
$mapped[] = $this->getAnswer($field, $answers) ;
if (isset($field['heading']) && $field['heading'] === 'boundary_geojson') {
continue;
}
$mapped[] = $this->getAnswer($field, $answers);
}

foreach ($this->auditFields as $key => $value) {
$mapped[] = data_get($entity, $key, '');
$mapped[] = mb_convert_encoding(data_get($entity, $key, ''), 'UTF-8', 'auto');
}

return $mapped;
Expand All @@ -83,8 +91,8 @@ protected function getAttachedMappedForEntity($entity): array

$mapped = array_merge($mapped, [
$organisation->readable_type ?? null,
$organisation->name ?? null,
$entity->project->name ?? null,
mb_convert_encoding($organisation->name ?? null, 'UTF-8', 'auto'),
mb_convert_encoding($entity->project->name ?? null, 'UTF-8', 'auto'),
$entity->status ?? null,
$entity->update_request_status ?? null,
$entity->due_at ?? null,
Expand All @@ -103,12 +111,12 @@ protected function getAttachedMappedForEntity($entity): array
}
if ($this->form->type === 'nursery-report') {
$mapped[] = $entity->nursery->old_id ?? ($entity->nursery->id ?? null);
$mapped[] = $entity->nursery->name ?? null;
$mapped[] = mb_convert_encoding($entity->nursery->name ?? null, 'UTF-8', 'auto');
}

if ($this->form->type === 'site-report') {
$mapped[] = $entity->site->ppc_external_id ?? $entity->site->id ?? null;
$mapped[] = $entity->site->name ?? null;
$mapped[] = mb_convert_encoding($entity->site->name ?? null, 'UTF-8', 'auto');
$sumTreeSpecies = $entity->treeSpecies()->sum('amount');
$mapped[] = $sumTreeSpecies > 0 ? $sumTreeSpecies : null;
$mapped[] = $entity->site->trees_planted_count ?? null;
Expand Down
17 changes: 10 additions & 7 deletions app/Http/Controllers/V2/AuditStatus/GetAuditStatusController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Http\Controllers\Controller;
use App\Http\Resources\V2\AuditStatusResource;
use App\Models\DTOs\AuditStatusDTO;
use App\Models\V2\AuditableModel;
use Illuminate\Http\Request;

Expand All @@ -16,17 +17,17 @@ public function __invoke(Request $request, AuditableModel $auditable)
->orderBy('created_at', 'desc')
->get();

foreach ($auditStatuses as $auditStatus) {
$auditStatus->entity_name = $auditable->getAuditableNameAttribute();
}
$list = $auditStatuses->map(function ($auditStatus) {
return AuditStatusDTO::fromAuditStatus($auditStatus);
});

$combinedData = $auditStatuses->concat($this->getAudits($auditable));
$combinedData = $list->concat($this->getAudits($auditable));

$sortedData = $combinedData->sortByDesc(function ($item) {
return $item->updated_at ?? $item->created_at;
return $item->date_created;
});

return AuditStatusResource::collection($sortedData);
return AuditStatusResource::collection($sortedData->unique('comment'));
}

private function getAudits($auditable)
Expand All @@ -40,6 +41,8 @@ private function getAudits($auditable)
->orderBy('created_at', 'desc')
->get();

return $audits;
return $audits->map(function ($audit) {
return AuditStatusDTO::fromAudits($audit);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function __invoke(Request $request, string $entity, string $framework)
$filename = Str::of(data_get($form, 'title', 'Form'))->replace(['/', '\\'], '-') . ' '.Str::of($entity)->replace('-', ' ')->ucfirst().' - ' . now() . '.csv';

return response()->streamDownload(function () use ($csv) {
echo $csv->toString();
echo "\xEF\xBB\xBF" . $csv->toString();
}, $filename, [
'Content-Type' => 'text/csv',
]);
Expand Down
32 changes: 29 additions & 3 deletions app/Http/Controllers/V2/Geometry/GeometryController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
use App\Helpers\GeometryHelper;
use App\Http\Controllers\Controller;
use App\Http\Requests\V2\Geometry\StoreGeometryRequest;
use App\Models\V2\PointGeometry;
use App\Models\V2\PolygonGeometry;
use App\Models\V2\Sites\Site;
use App\Models\V2\Sites\SitePolygon;
use App\Services\PolygonService;
use App\Validators\SitePolygonValidator;
use Illuminate\Auth\Access\AuthorizationException;
Expand Down Expand Up @@ -68,9 +70,11 @@ protected function storeAndValidateGeometries($geometries): array
foreach ($results as $index => $result) {
$polygonErrors = [];
foreach ($result['polygon_uuids'] as $polygonUuid) {
$errors = $this->runStoredGeometryValidations($polygonUuid);
if (! empty($errors)) {
$polygonErrors[$polygonUuid] = $errors;
$validationErrors = $this->runStoredGeometryValidations($polygonUuid);
$estAreaErrors = $this->runStoredGeometryEstAreaValidation($polygonUuid);
$allErrors = array_merge($validationErrors, $estAreaErrors);
if (! empty($allErrors)) {
$polygonErrors[$polygonUuid] = $allErrors;
}
}

Expand Down Expand Up @@ -176,6 +180,28 @@ public function updateGeometry(Request $request, PolygonGeometry $polygon): Json
return response()->json(['errors' => $errors], 200);
}

protected function runStoredGeometryEstAreaValidation($polygonUuid): array
{
$errors = [];
$sitePolygon = SitePolygon::where('poly_id', $polygonUuid)->first();

if ($sitePolygon && $sitePolygon->point_id) {
$pointGeometry = PointGeometry::isUuid($sitePolygon->point_id)->first();

if ($pointGeometry && isset($pointGeometry->est_area)) {
if ($pointGeometry->est_area > 5) {
$errors[] = [
'key' => 'EXCEEDS_EST_AREA',
'message' => 'The est_area is bigger than 5',
'est_area' => $pointGeometry->est_area,
];
}
}
}

return $errors;
}

protected function runStoredGeometryValidations(string $polygonUuid): array
{
// TODO: remove when the point transformation ticket is complete
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public function __invoke(Request $request): NurseryReportsCollection
->join('v2_projects', function ($join) {
$join->on('v2_nurseries.project_id', '=', 'v2_projects.id');
})
->join('organisations', 'v2_projects.organisation_id', '=', 'organisations.id')
->selectRaw('
v2_nursery_reports.*,
v2_projects.name as project_name,
Expand All @@ -39,6 +40,7 @@ public function __invoke(Request $request): NurseryReportsCollection
AllowedFilter::exact('status'),
AllowedFilter::exact('update_request_status'),
AllowedFilter::exact('framework_key'),
AllowedFilter::scope('organisation_uuid', 'organisationUuid'),
]);

$this->sort($query, [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,12 @@ public function __invoke(Request $request): ProjectReportsCollection
->join('v2_projects', function ($join) {
$join->on('v2_project_reports.project_id', '=', 'v2_projects.id');
})
->selectRaw('
v2_project_reports.*,
(SELECT name FROM organisations WHERE organisations.id = v2_projects.organisation_id) as organisation_name
')
->join('organisations', 'v2_projects.organisation_id', '=', 'organisations.id')
->select('v2_project_reports.*', 'organisations.name as organisation_name', 'organisations.id as organisation_id')
->allowedFilters([
AllowedFilter::scope('project_uuid', 'projectUuid'),
AllowedFilter::scope('country'),
AllowedFilter::scope('organisation_uuid', 'organisationUuid'),
AllowedFilter::exact('status'),
AllowedFilter::exact('update_request_status'),
AllowedFilter::exact('framework_key'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@ public function __invoke(CreateProjectInviteRequest $request, Project $project):
if ($existingUser->organisation_id == null) {
$existingUser->update(['organisation_id' => $project->organisation_id]);
}
$url = str_replace('/reset-password', '/login', $url);
Mail::to($data['email_address'])->queue(new V2ProjectMonitoringNotification($project->name, $url));
} else {
$user = User::create([
'email_address' => $data['email_address'],
'organisation_id' => $project->organisation_id,
// Accounts need to have a password assigned in order to go through the password reset flow.
'password' => $this->generateRandomPassword(),
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public function __invoke(Request $request): SiteReportsCollection
->join('v2_projects', function ($join) {
$join->on('v2_sites.project_id', '=', 'v2_projects.id');
})
->join('organisations', 'v2_projects.organisation_id', '=', 'organisations.id')
->selectRaw('
v2_site_reports.*,
(SELECT name FROM organisations WHERE organisations.id = v2_projects.organisation_id) as organisation_name
Expand All @@ -38,6 +39,7 @@ public function __invoke(Request $request): SiteReportsCollection
AllowedFilter::exact('status'),
AllowedFilter::exact('update_request_status'),
AllowedFilter::exact('framework_key'),
AllowedFilter::scope('organisation_uuid', 'organisationUuid'),
]);

$this->sort($query, [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,42 @@ public function deletePolygonAndSitePolygon(string $uuid)
}
}

public function deleteMultiplePolygonsAndSitePolygons(Request $request)
{
try {
$uuids = $request->input('uuids');

if (empty($uuids)) {
return response()->json(['message' => 'No UUIDs provided.'], 400);
}

$deletedUuids = [];
$failedUuids = [];

foreach ($uuids as $uuid) {
try {
$this->deletePolygonAndSitePolygon($uuid);
$deletedUuids[] = ['uuid' => $uuid];
} catch (\Exception $e) {
Log::error('An error occurred while deleting polygon and site polygon for UUID: ' . $uuid . '. Error: ' . $e->getMessage());
$failedUuids[] = ['uuid' => $uuid, 'error' => $e->getMessage()];
}
}

$response = [
'message' => 'Polygon geometries and associated site polygons deleted successfully.',
'deleted' => $deletedUuids,
'failed' => $failedUuids,
];

return response()->json($response);
} catch (\Exception $e) {
Log::error('An error occurred: ' . $e->getMessage());

return response()->json(['error' => 'An error occurred: ' . $e->getMessage()], 500);
}
}

public function deletePolygonAndProjectPolygon(string $uuid)
{
try {
Expand Down
30 changes: 30 additions & 0 deletions app/Http/Controllers/V2/User/AdminUsersOrganizationController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace App\Http\Controllers\V2\User;

use App\Http\Controllers\Controller;
use App\Models\V2\Organisation;
use App\Models\V2\User;
use Illuminate\Http\Request;

class AdminUsersOrganizationController extends Controller
{
public function __invoke(Organisation $organisation, Request $request)
{
$this->authorize('readAll', User::class);
$OwnersWithRequestStatus = $organisation->owners->map(function ($user) {
$user['status'] = 'approved';

return $user;
});
$partnersWithRequestStatus = $organisation->partners->map(function ($user) {
$user['status'] = $user->pivot->status;

return $user;
});

$usersOrganisation = $OwnersWithRequestStatus->merge($partnersWithRequestStatus);

return response()->json($usersOrganisation);
}
}
Loading

0 comments on commit be7fa61

Please sign in to comment.