SmartCane/laravel_app/app/Models/Project.php
guillaume91 026e2601c4 wip
2024-05-30 17:03:32 +02:00

347 lines
11 KiB
PHP

<?php
namespace App\Models;
use App\Jobs\ProjectDownloadTiffJob;
use App\Jobs\ProjectMosiacGeneratorJob;
use App\Jobs\ProjectReportGeneratorJob;
use Carbon\CarbonPeriod;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class Project extends Model
{
use HasFactory;
protected $fillable = [
'name',
'mail_template',
'mail_subject',
'mail_frequency',
'mail_day',
'mail_scheduled',
'download_path',
'pivot_json_path',
'span_json_path',
'harvest_json_path'
];
public static function saveWithFormData(mixed $formData)
{
$uniqueIdentifier = ['id' => $formData['id'] ?? null];
$project = Project::updateOrCreate($uniqueIdentifier, $formData);
$baseFrom = 'livewire-tmp/';
$baseTo = $project->download_path.'/Data/';
if ($formData['pivot_file']) {
Storage::copy($baseFrom.$formData['pivot_file']['tmpFilename'],$baseTo.'pivot.geojson');
$project->update(['pivot_json_path' => $baseTo.'pivot.geojson']);
}
if ($formData['span_file']) {
Storage::copy($baseFrom.$formData['span_file']['tmpFilename'],$baseTo.'span.geojson');
$project->update(['span_json_path' => $baseTo.'span.geojson']);
}
if ($formData['harvest_file']) {
Storage::copy($baseFrom.$formData['harvest_file']['tmpFilename'],$baseTo.'harvest.'.$formData['harvest_file']['extension']);
$project->update(['harvest_json_path' => $baseTo.'harvest.'.$formData['harvest_file']['extension']]);
}
}
private function upsertMailRecipients($formData)
{
$mailRecipientsData = array_map(function ($mailRecipient) {
$mailRecipient['project_id'] = $this->id;
unset($mailRecipient['created_at']);
unset($mailRecipient['updated_at']);
$mailRecipient['id'] ??= null;
return $mailRecipient;
}, $formData['mail_recipients'] ?? []);
ProjectEmailRecipient::upsert(
$mailRecipientsData,
['id', 'project_id'],
['name', 'email',]
);
}
public function getMosaicPath()
{
return sprintf('%s/%s', $this->download_path, 'weekly_mosaic');
}
public function getMosaicList(): Collection
{
return collect(Storage::files($this->getMosaicPath()))
->filter(fn($file) => Str::endsWith($file, '.tif'))
->sortByDesc(function ($file) {
$parts = explode('_', str_replace('.tif', '', $file));
$week = $parts[1];
$year = $parts[2];
return $year.sprintf('%02d', $week);
})
->values();
}
protected static function boot()
{
parent::boot(); // TODO: Change the autogenerated stub
static::deleting(function ($project) {
$project->emailRecipients()->delete();
$project->mailings()->each(function ($mailing) {
$mailing->attachments()->delete();
$mailing->recipients()->delete();
});
$project->mailings()->delete();
});
}
public function reports()
{
return $this->hasMany(ProjectReport::class);
}
public function getAttachmentPathAttribute()
{
return storage_path(sprintf('%s/attachments', $this->download_path));
return '/storage/'.$this->download_path.'/attachments';
}
public function emailRecipients()
{
return $this->hasMany(ProjectEmailRecipient::class);
}
public function mailings()
{
return $this->hasMany(ProjectMailing::class);
}
public function downloads(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(ProjectDownload::class);
}
public function mosaics(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(ProjectMosaic::class);
}
public function allMosaicsPresent(Carbon $endDate): bool
{
// end date is in the future
if ($endDate->isFuture()) {
throw new \Exception('Mosaic can be generated for the future. Change the end date.');
}
$mosaicsNotPresentInFilesystem = $this->getMosaicFilenameListByEndDate($endDate)
->filter(function ($filename) {
return !$this->getMosaicList()->contains(function ($mosaicFilename) use ($filename) {
// TODO check the value of the week leading 0
return Str::endsWith($mosaicFilename, substr($filename, -16));
});
});
if ($mosaicsNotPresentInFilesystem->count() === 0) {
return true;
}
$message = sprintf(
'Missing mosaics: %s',
$mosaicsNotPresentInFilesystem->implode(', ')
);
throw new \Exception($message);
}
public static function getMosaicFilenameListByEndDate(Carbon $endDate): Collection
{
$result = collect([]);
for ($i = 0; $i < 4; $i++) {
$week = $endDate->weekOfYear;
$year = $endDate->year;
if ($week === 53) {
$year--;
}
$result->add(sprintf('week_%02d_%04d.tif', $week, $year));
$endDate = $endDate->subWeek();
}
return $result;
}
public function getMosiacList()
{
return collect(Storage::files($this->getMosaicPath()))
->filter(fn($file) => Str::endsWith($file, '.tif'))
->sortByDesc(function ($file) {
$parts = explode('_', str_replace('.tif', '', $file));
$week = $parts[1];
$year = $parts[2];
return $year.sprintf('%02d', $week);
})
->values();
}
public static function getAllDatesOfWeeksInYear($year, $weekNumber): Collection
{
$startOfWeek = Carbon::now()->setISODate($year, $weekNumber)->startOfWeek();
$dates = collect([]);
for ($day = 0; $day < 7; $day++) {
$dates->push((clone $startOfWeek)->addDays($day)->toDateString());
}
return $dates;
}
public function allMergedTiffsPresent(Collection $haystack, Collection $needles)
{
$needlesNotInHaystack = $needles->filter(function ($needle) use ($haystack) {
return !$haystack->contains(function ($item) use ($needle) {
return str_contains($item, $needle);
});
});
if ($needlesNotInHaystack->count() > 0) {
$message = sprintf(
'Missing merged tiffs: %s',
$needlesNotInHaystack->implode(', ')
);
throw new \Exception($message);
}
return true;
}
public function getMergedTiffList()
{
return collect(Storage::files($this->download_path.'/merged_final_tif'))
->filter(fn($file) => Str::endsWith($file, '.tif'))
->sortByDesc(function ($file) {
$parts = explode('_', str_replace('.tif', '', $file));
$date = $parts[1];
return $date;
})
->values();
}
public function hasPendingDownload(): bool
{
return $this->downloads()->statusPending()->count() > 0;
}
public function hasPendingReport(): bool
{
return $this->reports()->statusPending()->count() > 0;
}
public function hasPendingMosaic(): bool
{
return $this->mosaics()->statusPending()->count() > 0;
}
public function startDownload(Carbon $date)
{
$downloadRequest = $this->downloads()->updateOrCreate(
[
'project_id' => $this->id, // of een andere manier om project_id te bepalen
'name' => sprintf('%s.tif', $date->format('Y-m-d')),
],
[
'path' => sprintf('%s/%s/%s.tif', $this->download_path, 'merged_final_tif', $date->format('Y-m-d')),
]
);
ProjectDownloadTiffJob::dispatch($downloadRequest, $date);
}
public function schedule()
{
// if ($this->shouldSchedule()) {
$this->scheduleReport();
// }
}
public function shouldSchedule(): bool
{
if (strtolower($this->mail_day) === strtolower(now()->englishDayOfWeek)) {
return strtolower($this->mail_frequency) === 'weekly' || now()->day <= 7;
}
return false;
}
public function hasInvalidMosaicFor($year, $week): bool
{
$dayOfWeekIso = Carbon::parse($this->mail_day)->dayOfWeekIso;
$min_updated_at_date = now()
->setISODate($year, $week)
->startOfWeek()
->addDays($dayOfWeekIso - 1)
->format('Y-m-d');
return $this->mosaics()
->where('updated_at', '>=', $min_updated_at_date)
->statusSuccess()
->where(['year' => $year, 'week' => $week])
->exists();
}
public function scheduleReport($year = null, $week = null)
{
// if year, week is in the future set year and week to null;
if (now()->year < $year || (now()->year === $year && now()->weekOfYear < $week)) {
$year = null;
$week = null;
logger('year and week is in the future default to now');
}
$year = $year ?? now()->year;
$week = $week ?? now()->weekOfYear;
Bus::chain([
Bus::batch($this->getFileDownloadsFor($year, $week)),
Bus::batch($this->getMosaicsFor($year, $week)),
Bus::batch([$this->getReportFor($year, $week, true)]),
])
->dispatch();
return "done";
}
public function getReportFor($year, $week, $sendMail = false)
{
$report = $this->reports()->create([
'name' => 'Report for week '.$week.' of '.$year,
'week' => $week,
'year' => $year,
'path' => 'reports/week_'.$week.'_'.$year.'.docx',
]);
return (new ProjectReportGeneratorJob($report, $sendMail));
}
public function getFileDownloadsFor($year, $startWeekNumber):array
{
$endOfRange = now()->setISODate($year, $startWeekNumber)->endOfWeek();
$startOfRange = (clone $endOfRange)->subWeeks(3)->startOfWeek();
$dateRange = CarbonPeriod::create($startOfRange, $endOfRange);
return collect($dateRange)
->map(fn($date) => ProjectDownloadTiffJob::handleForDate($this, $date))
->filter()
->toArray();
}
public function getMosaicsFor($year, $startWeekNumber)
{
logger(sprintf('in Get Mosaics for year %s week %d', $year, $startWeekNumber));
return collect(range(0, 3))
->map(fn($weekDiff) => ProjectMosiacGeneratorJob::handleFor($this, $year, $startWeekNumber - $weekDiff))
->filter()
->toArray();
}
}