$formData['id'] ?? null]; $project = Project::updateOrCreate($uniqueIdentifier, $formData); if ($formData['pivot_json_path']) { $path = $formData['pivot_json_path']->storeAs($project->download_path .'/Data', 'pivot.geojson'); $project->pivot_json_path = $path; $project->save(); } if ($formData['span_json_path']) { $path = $formData['span_json_path']->storeAs($project->download_path .'/Data', 'span.geojson'); $project->pivot_json_path = $path; $project->save(); } $project->upsertBoundingBox($formData); $project->upsertMailRecipients($formData); } private function upsertBoundingBox($formData) { $boundingBoxesData = array_map(function ($boundingBox) { $boundingBox['project_id'] = $this->id; unset($boundingBox['created_at']); unset($boundingBox['updated_at']); $boundingBox['id'] ??= null; return $boundingBox; }, $formData['boundingBoxes'] ?? []); ProjectBoundingBox::upsert( $boundingBoxesData, ['id', 'project_id'], [ 'name', 'top_left_latitude', 'top_left_longitude', 'bottom_right_latitude', 'bottom_right_longitude' ] ); } 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(): \Illuminate\Support\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->boundingBoxes()->delete(); $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 boundingBoxes() { return $this->hasMany(ProjectBoundingBox::class); } 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('End date is in the future'); } $mosaicsNotPresentInFilesystem = $this->getMosaicFilenameListByEndDate($endDate) ->filter(function ($filename) { return !$this->getMosaicList()->contains(function ($mosaicFilename) use ($filename) { 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 function getMosaicFilenameListByEndDate(Carbon $endDate): \Illuminate\Support\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 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) { $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(); return $return; } public function getMosaicsFor($year, $startWeekNumber) { $return = collect(range(0, 3)) ->map(fn($weekDiff) => ProjectMosiacGeneratorJob::handleFor($this, $year, $startWeekNumber - $weekDiff)) ->filter() ->toArray(); return $return; } public function getBoundingBoxesAsArray() { return $this->boundingBoxes->map(function ($boundingBox) { return $boundingBox->toArrayCustom(); })->toArray(); } }