Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TM-1718] Convert jobs data to the Demographics tables #715

Merged
merged 8 commits into from
Feb 26, 2025
152 changes: 152 additions & 0 deletions app/Console/Commands/OneOff/MigrateJobsToDemographics.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<?php

namespace App\Console\Commands\OneOff;

use App\Models\V2\Demographics\Demographic;
use App\Models\V2\Projects\ProjectReport;
use Illuminate\Console\Command;

class MigrateJobsToDemographics extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'one-off:migrate-jobs-to-demographics';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Move jobs data to demographics';

protected const JOBS_MAPPING = [
'full-time' => [
'gender' => [
'male' => 'ft_men',
'female' => 'ft_women',
'non-binary' => 'ft_other',
],
'age' => [
'youth' => 'ft_youth',
'non-youth' => 'ft_jobs_non_youth',
],
'total' => 'ft_total',
],
'part-time' => [
'gender' => [
'male' => 'pt_men',
'female' => 'pt_women',
'non-binary' => 'pt_other',
],
'age' => [
'youth' => 'pt_youth',
'non-youth' => 'pt_non_youth',
],
'total' => 'pt_total',
],
];

protected const VOLUNTEERS_MAPPING = [
'volunteer' => [
'gender' => [
'male' => 'volunteer_men',
'female' => 'volunteer_women',
'non-binary' => 'volunteer_other',
],
'age' => [
'youth' => 'volunteer_youth',
'non-youth' => 'volunteer_non_youth',
],
'caste' => [
'marginalized' => 'volunteer_scstobc',
],
'total' => 'volunteer_total',
],
];

protected const MIGRATION_MAPPING = [
'jobs' => self::JOBS_MAPPING,
'volunteers' => self::VOLUNTEERS_MAPPING,
];

/**
* Execute the console command.
*/
public function handle()
{
$this->info('Moving project report jobs data to Demographics...');
$this->withProgressBar(ProjectReport::count(), function ($progressBar) {
ProjectReport::chunkById(100, function ($projectReports) use ($progressBar) {
foreach ($projectReports as $projectReport) {
$this->convertJobs($projectReport);
$progressBar->advance();
}
});
});

$this->info("\n\nCompleted moving project report jobs data to Demographics.");
}

private function convertJobs(ProjectReport $projectReport): void
{
foreach (self::MIGRATION_MAPPING as $demographicType => $mapping) {
foreach ($mapping as $collection => $types) {
/** @var Demographic $demographic */
$demographic = null;
foreach ($types as $type => $subtypes) {
if ($type == 'total') {
$field = $subtypes;
if ($demographic != null) {
// Make sure gender / age demographics are balanced and reach at least to the "_total" field
// for this type of job from the original report. Pad gender and age demographics with an
// "unknown" if needed.
$genderTotal = $demographic->entries()->gender()->sum('amount');
$ageTotal = $demographic->entries()->age()->sum('amount');
$targetTotal = max($genderTotal, $ageTotal, $projectReport[$field]);
if ($genderTotal < $targetTotal) {
$demographic->entries()->create([
'type' => 'gender',
'subtype' => 'unknown',
'amount' => $targetTotal - $genderTotal,
]);
}
if ($ageTotal < $targetTotal) {
$demographic->entries()->create([
'type' => 'age',
'subtype' => 'unknown',
'amount' => $targetTotal - $ageTotal,
]);
}
}
} else {
// If none of the fields for this type exist, skip
$fields = collect(array_values($subtypes));
if ($fields->first(fn ($field) => $projectReport[$field] > 0) == null) {
continue;
}

if ($demographic == null) {
$demographic = $projectReport->demographics()->create([
'type' => $demographicType,
'collection' => $collection,
]);
}
foreach ($subtypes as $subtype => $field) {
$value = $projectReport[$field];
if ($value > 0) {
$demographic->entries()->create([
'type' => $type,
'subtype' => $subtype,
'amount' => $value,
]);
}
}
}
}
}
}
}
}
4 changes: 3 additions & 1 deletion app/Exports/V2/BaseExportFormSubmission.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ protected function getAnswer(array $field, array $answers, ?string $frameworkKey

case 'workdays':
case 'restorationPartners':
case 'jobs':
case 'volunteers':
$list = [];
$demographic = $answer->first();
if ($demographic == null) {
Expand All @@ -88,7 +90,7 @@ protected function getAnswer(array $field, array $answers, ?string $frameworkKey
$list[] = 'age:(' . implode(')(', $types['age']) . ')';
if ($frameworkKey == 'hbf') {
$list[] = 'caste:(' . implode(')(', $types['caste']) . ')';
} else {
} elseif ($field['input_type'] == 'workdays' || $field['input_type'] == 'restorationPartners') {
$list[] = 'ethnicity:(' . implode(')(', $types['ethnicity']) . ')';
}

Expand Down

This file was deleted.

24 changes: 0 additions & 24 deletions app/Http/Resources/V2/ProjectReports/ProjectReportResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ public function toArray($request)
'due_at' => $this->due_at,
'completion' => $this->completion,
'readable_completion_status' => $this->readable_completion_status,
'workdays_paid' => $this->workdays_paid,
'workdays_volunteer' => $this->workdays_volunteer,
'total_unique_restoration_partners' => $this->total_unique_restoration_partners,
'direct_restoration_partners' => $this->direct_restoration_partners,
'indirect_restoration_partners' => $this->indirect_restoration_partners,
Expand All @@ -42,35 +40,18 @@ public function toArray($request)
'pct_survival_to_date' => $this->pct_survival_to_date,
'survival_calculation' => $this->survival_calculation,
'survival_comparison' => $this->survival_comparison,
'ft_women' => $this->ft_women,
'ft_men' => $this->ft_men,
'ft_youth' => $this->ft_youth,
'ft_smallholder_farmers' => $this->ft_smallholder_farmers,
'ft_total' => $this->ft_total,
'pt_non_youth' => $this->pt_non_youth,
'pt_women' => $this->pt_women,
'pt_men' => $this->pt_men,
'pt_youth' => $this->pt_youth,
'pt_smallholder_farmers' => $this->pt_smallholder_farmers,
'pt_total' => $this->pt_total,
'workdays_total' => $this->workdays_total,
'seasonal_women' => $this->seasonal_women,
'seasonal_men' => $this->seasonal_men,
'seasonal_youth' => $this->seasonal_youth,
'seasonal_smallholder_farmers' => $this->seasonal_smallholder_farmers,
'seasonal_total' => $this->seasonal_total,
'volunteer_women' => $this->volunteer_women,
'volunteer_men' => $this->volunteer_men,
'volunteer_youth' => $this->volunteer_youth,
'volunteer_smallholder_farmers' => $this->volunteer_smallholder_farmers,
'volunteer_total' => $this->volunteer_total,
'shared_drive_link' => $this->shared_drive_link,
'planted_trees' => $this->planted_trees,
'new_jobs_description' => $this->new_jobs_description,
'volunteers_work_description' => $this->volunteers_work_description,
'ft_jobs_non_youth' => $this->ft_jobs_non_youth,
'ft_jobs_youth' => $this->ft_jobs_youth,
'volunteer_non_youth' => $this->volunteer_non_youth,
'beneficiaries' => $this->beneficiaries,
'beneficiaries_description' => $this->beneficiaries_description,
'beneficiaries_women' => $this->beneficiaries_women,
Expand All @@ -87,7 +68,6 @@ public function toArray($request)
'project' => new ProjectLiteResource($this->project),
'site_reports_count' => $this->site_reports_count,
'nursery_reports_count' => $this->nursery_reports_count,
'total_jobs_created' => $this->total_jobs_created,
'task_uuid' => $this->task_uuid,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
Expand All @@ -112,7 +92,6 @@ public function toArray($request)
'convergence_schemes' => $this->convergence_schemes,
'convergence_amount' => $this->convergence_amount,
'community_partners_assets_description' => $this->community_partners_assets_description,
'volunteer_scstobc' => $this->volunteer_scstobc,
'beneficiaries_scstobc_farmers' => $this->beneficiaries_scstobc_farmers,
'beneficiaries_scstobc' => $this->beneficiaries_scstobc,
'people_knowledge_skills_increased' => $this->people_knowledge_skills_increased,
Expand All @@ -123,9 +102,6 @@ public function toArray($request)
'non_tree_total' => $this->non_tree_total,
'total_community_partners' => $this->total_community_partners,
'business_milestones' => $this->business_milestones,
'ft_other' => $this->ft_other,
'pt_other' => $this->pt_other,
'volunteer_other' => $this->volunteer_other,
'beneficiaries_other' => $this->beneficiaries_other,
'beneficiaries_training_women' => $this->beneficiaries_training_women,
'beneficiaries_training_men' => $this->beneficiaries_training_men,
Expand Down
3 changes: 0 additions & 3 deletions app/Http/Resources/V2/SiteReports/SiteReportResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ public function toArray($request)
'nothing_to_report' => $this->nothing_to_report,

'title' => $this->title,
'workdays_paid' => $this->workdays_paid,
'workdays_volunteer' => $this->workdays_volunteer,
'seeds_planted' => $this->seeds_planted,
'technical_narrative' => $this->technical_narrative,
'public_narrative' => $this->public_narrative,
Expand All @@ -40,7 +38,6 @@ public function toArray($request)
'polygon_status' => $this->polygon_status,
'paid_other_activity_description' => $this->paid_other_activity_description,

'total_workdays_count' => $this->total_workdays_count,
'total_trees_planted_count' => $this->total_trees_planted_count,
'total_seeds_planted_count' => $this->total_seeds_planted_count,

Expand Down
10 changes: 10 additions & 0 deletions app/Models/Traits/HasDemographics.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ trait HasDemographics
'workdaysConvergenceTotal' => ['type' => Demographic::WORKDAY_TYPE, 'collections' => 'convergence'],
'directRestorationPartners' => ['type' => Demographic::RESTORATION_PARTNER_TYPE, 'collections' => 'direct'],
'indirectRestorationPartners' => ['type' => Demographic::RESTORATION_PARTNER_TYPE, 'collections' => 'indirect'],
'jobsFullTimeTotal' => ['type' => Demographic::JOBS_TYPE, 'collections' => 'full-time'],
'jobsPartTimeTotal' => ['type' => Demographic::JOBS_TYPE, 'collections' => 'part-time'],
'volunteersTotal' => ['type' => Demographic::VOLUNTEERS_TYPE, 'collections' => 'volunteer'],
];

public static function bootHasDemographics()
Expand All @@ -40,6 +43,13 @@ public static function bootHasDemographics()
$collectionSets['direct'],
$collectionSets['indirect'],
])->flatten(),
Demographic::JOBS_TYPE => collect([
$collectionSets['full-time'],
$collectionSets['part-time'],
])->flatten(),
Demographic::VOLUNTEERS_TYPE => collect([
$collectionSets['volunteer'],
])->flatten(),
default => throw new InternalErrorException("Unrecognized demographic type: $demographicType"),
};
$collections->each(function ($collection) use ($attributePrefix) {
Expand Down
2 changes: 2 additions & 0 deletions app/Models/Traits/UsesLinkedFields.php
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ private function syncRelation(string $property, string $inputType, $data, bool $
'disturbances',
'workdays',
'restorationPartners',
'jobs',
'volunteers',
'stratas',
'invasive',
'seedings',
Expand Down
10 changes: 9 additions & 1 deletion app/Models/V2/Demographics/Demographic.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ class Demographic extends Model implements HandlesLinkedFieldSync

public const WORKDAY_TYPE = 'workdays';
public const RESTORATION_PARTNER_TYPE = 'restoration-partners';
public const JOBS_TYPE = 'jobs';
public const VOLUNTEERS_TYPE = 'volunteers';

public const VALID_TYPES = [self::WORKDAY_TYPE, self::RESTORATION_PARTNER_TYPE];
public const VALID_TYPES = [self::WORKDAY_TYPE, self::RESTORATION_PARTNER_TYPE, self::JOBS_TYPE, self::VOLUNTEERS_TYPE];

// In TM-1681 we moved several "name" values to "subtype". This check helps make sure that both in-flight
// work at the time of release, and updates from update requests afterward honor that change.
Expand Down Expand Up @@ -191,6 +193,12 @@ public function getReadableCollectionAttribute(): ?string
SiteReport::class => DemographicCollections::WORKDAYS_SITE_COLLECTIONS,
default => null
},
self::JOBS_TYPE => match ($this->demographical_type) {
ProjectReport::class => DemographicCollections::JOBS_PROJECT_COLLECTIONS,
},
self::VOLUNTEERS_TYPE => match ($this->demographical_type) {
ProjectReport::class => DemographicCollections::VOLUNTEERS_PROJECT_COLLECTIONS,
},
default => null
};
if (empty($collections)) {
Expand Down
14 changes: 14 additions & 0 deletions app/Models/V2/Demographics/DemographicCollections.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,18 @@ class DemographicCollections
self::DIRECT_OTHER => 'Direct Other',
self::INDIRECT_OTHER => 'Indirect Other',
];

public const FULL_TIME = 'full-time';
public const PART_TIME = 'part-time';

public const JOBS_PROJECT_COLLECTIONS = [
self::FULL_TIME => 'Full-time',
self::PART_TIME => 'Part-time',
];

public const VOLUNTEER = 'volunteer';

public const VOLUNTEERS_PROJECT_COLLECTIONS = [
self::VOLUNTEER => 'Volunteer',
];
}
Loading
Loading