This commit is contained in:
Martin Folkerts 2024-01-03 22:06:03 +01:00
parent 3fb55ddcce
commit 4c22ab6758
35 changed files with 582 additions and 267 deletions

View file

@ -19,12 +19,12 @@ while [ "$#" -gt 0 ]; do
done
# Controleer of de vereiste argumenten zijn ingesteld
if [ -z "$filename" ] || [ -z "$weeks_ago" ] || [ -z $report_date ]; then
echo "Missende argumenten. Gebruik: build_reports.sh --filename=hello.txt --weeks_ago=3 --report_date=2020-01-01"
if [ -z "$weeks_ago" ]; then
echo "Missende argumenten. Gebruik: build_mosiac.sh --weeks_ago=3"
exit 1
fi
echo "Weeks ago: $weeks_ago"
cd /Users/mfolkerts/smartCane/r_app
Rscript 2_CI_data_prep.R $weeks_ago
Rscript 2_CI_data_prep.R $weeks_ago

View file

@ -1,13 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class DownloadController extends Controller
{
public function show() {
return view('download.show', ['project' => $project]);
}
//
}

View file

@ -0,0 +1,65 @@
<?php
namespace App\Jobs;
use App\Models\Project;
use App\Models\ProjectDownload;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Process\Exceptions\ProcessFailedException;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Carbon\Carbon;
use Symfony\Component\Process\Process;
class ProjectDownloadTiffJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected Carbon $date;
protected ProjectDownload $download;
protected $days = 1;
/**
* Create a new job instance.
*/
public function __construct(ProjectDownload $download, Carbon $date, )
{
$this->date = $date;
$this->download = $download;
}
/**
* Execute the job.
*/
public function handle(): void
{
$projectFolder = base_path('../');
$command = [
sprintf('%srunpython.sh', $projectFolder),
sprintf('--date=%s', $this->date->format('Y-m-d')),
sprintf('--days=%d', $this->days),
];
// Convert commands array to a single string
$process = new Process($command);
$process->setTimeout(3600); // stel een geschikte timeout in
$process->start();
try {
$process->wait(function ($type, $buffer) use (&$myOutput){
logger($buffer);
});
} catch (ProcessFailedException $exception) {
logger('error', $exception->getMessage());
}
$this->download->update([
'status' => 'completed',
]);
}
}

View file

@ -3,6 +3,8 @@
namespace App\Jobs;
use App\Models\Project;
use App\Models\ProjectDownload;
use App\Models\ProjectMosaic;
use App\Models\ProjectReport;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
@ -16,18 +18,16 @@ class ProjectMosiacGeneratorJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public ProjectMosaic $mosaic;
protected $timeout = 120;
public $year;
public $week;
public Project $project;
/**
* Create a new job instance.
*/
public function __construct(Project $project, $year, $week)
public function __construct(ProjectMosaic $mosaic)
{
$this->year = $year;
$this->week = $week;
//
$this->mosaic = $mosaic;
}
/**
@ -35,7 +35,7 @@ public function __construct(Project $project, $year, $week)
*/
public function handle(): void
{
$weeksAgo = ProjectReport::weeksAgoForYearAndWeek($this->year, $this->week);
$weeksAgo = ProjectReport::weeksAgoForYearAndWeek($this->mosaic->year, $this->mosaic->week);
$projectFolder = base_path('../');
@ -63,7 +63,9 @@ public function handle(): void
} catch (ProcessFailedException $exception) {
echo $exception->getMessage();
}
$this->mosaic->update([
'status' => 'complete',
]);
}
}

View file

@ -1,61 +0,0 @@
<?php
namespace App\Livewire\Download;
use Livewire\Component;
use Symfony\Component\Process\Process;
class DownloadForm extends Component
{
public $output='';
public $days = 7;
public $buttonText = 'Start';
public function mount() {
$this->buttonText = __('Start');
}
public function start()
{
$this->buttonText = __('Downloading...');
// Commands to run
$projectFolder = base_path('../');
$command = [
sprintf('%srunpython.sh', $projectFolder),
sprintf('--days=%d', $this->days),
];
// Convert commands array to a single string
$process = new Process($command);
$process->setTimeout(3600); // stel een geschikte timeout in
$process->start();
try {
$myOutput = [];
$process->wait(function ($type, $buffer) use (&$myOutput){
$this->stream(to: 'output', content: $buffer);
$myOutput[] = $buffer;
});
$this->output = collect($myOutput)->join('<BR>');
} catch (ProcessFailedException $exception) {
logger('error', $exception->getMessage());
echo $exception->getMessage();
}
$this->buttonText = __('Done');
}
public function render()
{
return view('livewire.download.download-form');
}
}

View file

@ -1,22 +0,0 @@
<?php
namespace App\Livewire\Download;
use App\Models\Project;
use Livewire\Component;
class DownloadGrid extends Component
{
private $files = [];
public function mount(Project $project)
{
$this->files = $project->getMosaicList();
// dd($this->directories);
}
public function render()
{
return view('livewire.download.download-grid')->with(['files' => $this->files]);
}
}

View file

@ -3,15 +3,61 @@
namespace App\Livewire\Projects;
use App\Models\Project;
use App\Rules\DownloadDateRangeRule;
use Carbon\Carbon;
use Carbon\CarbonPeriod;
use Livewire\Component;
use Symfony\Component\Process\Process;
class DownloadManager extends Component
{
public $project;
public function mount(Project $project) {
public $formData;
public $showDownloadModal = false;
public function mount(Project $project)
{
$this->path = $project->download_path;
$this->formData = [
'dateRange' => sprintf('%s to %s',
now()->subDays(6)->format('Y/m/d'),
now()->format('Y/m/d')
)
];
}
public function openDownloadModal()
{
$this->showDownloadModal = true;
}
public function saveDownloads()
{
$this->validate([
'formData.dateRange' => [
'required',
new DownloadDateRangeRule(),
]
]);
$dateRange = explode(' to ', $this->formData['dateRange']);
$period = CarbonPeriod::create(
Carbon::parse($dateRange[0]),
Carbon::parse($dateRange[1])
);
collect($period)->each(function ($date) {
$this->project->startDownload($date);
});
$this->showDownloadModal = false;
}
public function render()
{
return view('livewire.projects.download-manager', [

View file

@ -33,7 +33,16 @@ public function saveMosiac(){
'formData.week' => 'required',
]);
ProjectMosiacGeneratorJob::dispatch($this->project, $this->formData['year'], $this->formData['week']);
$mosaic = $this->project->mosaics()->updateOrCreate([
'name' => sprintf('Week %s, %s', $this->formData['week'], $this->formData['year']),
'year' => $this->formData['year'],
'week' => $this->formData['week'],
],[
'status' => 'pending',
'path' => $this->project->getMosaicPath(),
]);
ProjectMosiacGeneratorJob::dispatch($mosaic);
$this->showCreateModal = false;
}

View file

@ -2,9 +2,10 @@
namespace App\Models;
use App\Jobs\ProjectDownloadTiffJob;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
@ -69,7 +70,7 @@ private function upsertMailRecipients($formData)
);
}
private function getMosaicPath()
public function getMosaicPath()
{
return sprintf('%s/%s', $this->download_path, 'weekly_mosaic');
}
@ -129,12 +130,18 @@ public function mailings()
return $this->hasMany(ProjectMailing::class);
}
public function downloads()
public function downloads(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(ProjectDownload::class);
}
public function allMosaicsPresent(Carbon $endDate)
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()) {
@ -220,6 +227,38 @@ public function allMergedTiffsPresent(Collection $haystack, Collection $needles)
public function getMergedTiffList()
{
return collect([]);;
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()->where('status', 'pending')->count() > 0;
}
public function hasPendingMosaic() : bool
{
return $this->mosaics()->where('status', 'pending')->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')),
'status' => 'pending',
]
);
ProjectDownloadTiffJob::dispatch($downloadRequest, $date);
}
}

View file

@ -8,4 +8,10 @@
class ProjectDownload extends Model
{
use HasFactory;
protected $fillable = [
'name',
'path',
'status',
];
}

View file

@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ProjectMosaic extends Model
{
use HasFactory;
protected $fillable = [
'name',
'path',
'status',
'year',
'week',
];
public function project()
{
return $this->belongsTo(Project::class);
}
}

View file

@ -2,35 +2,30 @@
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use App\Models\Project;
use App\Models\ProjectReport;
class AllMergedTiffsPresentRule implements Rule
class AllMergedTiffsPresentRule implements ValidationRule
{
protected $project;
protected $errorMessage = '';
protected Project $project;
public function __construct(Project $project)
{
$this->project = $project;
}
public function passes($attribute, $value)
public function validate(string $attribute, mixed $value, Closure $fail): void
{
try {
return $this->project->allMergedTiffsPresent(
$this->project->allMergedTiffsPresent(
$this->project->getMergedTiffList(),
Project::getAllDatesOfWeeksInYear($value['year'], $value['week'])
);
} catch (\Exception $e) {
$this->errorMessage = $e->getMessage();
return false;
$fail($e->getMessage());
}
}
public function message()
{
return $this->errorMessage;
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Carbon;
class DownloadDateRangeRule implements ValidationRule
{
protected Carbon $startDate;
protected Carbon $endDate;
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (!$this->validateDateString($value)) {
$fail('Date range must be in the format YYYY/MM/DD to YYYY/MM/DD.');
return;
}
if (!$this->startDate->isPast() || !$this->endDate->isPast()) {
$fail('Date range cannot be in the future.');
return;
}
if ($this->startDate->greaterThan($this->endDate)) {
$fail('Start date must be before end date.');
}
}
function validateDateString($dateString): bool
{
$regex = '/^(\d{4}\/\d{2}\/\d{2}) to (\d{4}\/\d{2}\/\d{2})$/';
if (preg_match($regex, $dateString, $matches)) {
$this->startDate = Carbon::parse($matches[1]);
$this->endDate = Carbon::parse($matches[2]);
return true;
}
return false;
}
}

View file

@ -16,6 +16,7 @@ public function up(): void
$table->foreignId('project_id');
$table->string('name');
$table->string('path');
$table->string('status')->default('pending');
$table->timestamps();
});
}

View file

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('project_mosaics', function (Blueprint $table) {
$table->id();
$table->foreignId('project_id');
$table->string('week');
$table->string('name');
$table->string('year');
$table->string('path');
$table->string('status')->default('pending');
$table->timestamps();
}); //
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('project_mosaics');
}
};

View file

@ -61,6 +61,16 @@ private function createChembaProject()
],
]);
foreach($chembaProject->getMergedTiffList() as $mergedTiff) {
$chembaProject->downloads()->create([
'name' => basename($mergedTiff),
'path' => $mergedTiff,
'status' => 'completed',
'created_at' => '2021-01-01 00:00:00',
'updated_at' => '2021-01-01 00:00:00'
]);
}
$chembaProject->emailRecipients()->createMany([
[
'name' => 'Martin Folkerts',

View file

@ -12,6 +12,7 @@
"@ryangjchandler/alpine-clipboard": "^2.3.0",
"@tailwindcss/aspect-ratio": "^0.4.2",
"alpinejs": "^3.13.3",
"flatpickr": "^4.6.13",
"tailwindcss": "^3.3.3"
},
"devDependencies": {
@ -919,6 +920,11 @@
"node": ">=8"
}
},
"node_modules/flatpickr": {
"version": "4.6.13",
"resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz",
"integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw=="
},
"node_modules/focus-trap": {
"version": "6.9.4",
"resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-6.9.4.tgz",

View file

@ -23,6 +23,7 @@
"@ryangjchandler/alpine-clipboard": "^2.3.0",
"@tailwindcss/aspect-ratio": "^0.4.2",
"alpinejs": "^3.13.3",
"flatpickr": "^4.6.13",
"tailwindcss": "^3.3.3"
}
}

View file

@ -1,9 +1,9 @@
@import 'flatpickr/dist/flatpickr.css';
@import url('https://rsms.me/inter/inter.css');
@tailwind base;
@tailwind components;
@tailwind utilities;
[x-cloak] {
display: none;
}

View file

@ -1,2 +1,4 @@
import './bootstrap';
import('./alpine');
import flatpckr from 'flatpickr';
window.flatpckr = flatpckr;

View file

@ -0,0 +1,68 @@
@props([
'formData',
/** @var \App\Livewire\Projects\MosaicManager */
'manager'
])
<x-modal wire:model.live="showDownloadModal" {{ $attributes }} >
<x-form-modal submit="saveDownloads" wire:loading.class="opacity-50">
<x-slot name="title">
{{ __('Downloads') }}
</x-slot>
<x-slot name="description">
{{ __('Download for project') }}
</x-slot>
<x-slot name="form">
<div class="col-span-6 sm:col-span-4">
<div
x-data="{
init() {
let picker = flatpickr(this.$refs.picker, {
mode: 'range',
dateFormat: 'Y/m/d',
defaultDate: this.$wire.formData.dateRange,
onChange: (date, dateString) => {
this.value = dateString.split(' to ');
}
});
// Watcher voor het updaten van de picker
this.$watch('value', () => picker.setDate(this.value));
},
}"
class="max-w-sm w-full"
>
<div class="mb-2 font-bold">Date Range:</div>
<input wire:model="formData.dateRange" class="w-full rounded-md border border-gray-200 px-3 py-2.5" x-ref="picker" type="text">
</div>
<x-input-error for="formData.date_range" class="mt-2"/>
<x-input-error for="formData" class="mt-2"/>
</div>
<div>
<span class="whitespace-nowrap"></span>
</div>
</x-slot>
<x-slot name="actions">
<x-action-message class="mr-3" on="saved">
{{ __('Saved.') }}
</x-action-message>
<x-secondary-button class="mr-3"
type="button"
x-on:click="$wire.showDownloadModal = false"
>
{{ __('Cancel') }}
</x-secondary-button>
<x-button wire:loading.disabled>
{{ __('Save') }}
</x-button>
</x-slot>
</x-form-modal>
</x-modal>

View file

@ -1,20 +0,0 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Download') }}
</h2>
</x-slot>
<div>
<div class="max-w-7xl mx-auto py-10 sm:px-6 lg:px-8">
<div class="mt-10 sm:mt-0">
<livewire:download.download-grid :project="$project" />
</div>
<x-section-border />
<div class="mt-10 sm:mt-0">
@livewire('download.download-form')
</div>
</div>
</div>
</x-app-layout>

View file

@ -1,47 +0,0 @@
<x-action-section>
<x-slot name="title">
{{ __('Satellite data') }}
</x-slot>
<x-slot name="description">
{{ __('Add additional satellite data to your account.') }}
</x-slot>
<x-slot name="content">
<h3 class="text-lg font-medium text-gray-900">
{{ __('Download satellite data') }}
</h3>
<div class="mt-3 max-w-xl text-sm text-gray-600">
<p>
{{ __('Vul een aantal dagen in en druk op start om de betreffende sateliet data te downloaden') }}
<x-input type="text" wire:model="days" class="mt-1 block w-full"/>
</p>
</div>
<div class="mt-4 max-w-xl text-sm text-gray-600">
<p class="font-semibold">
{{ __('') }}
</p>
</div>
@if ($this->output)
<div
class="grid gap-1 mt-4 px-4 py-4 font-mono overflow-hidden break-words text-sm bg-gray-100 rounded-lg"
wire:stream="output">{{ $output }}
</div>
@endif
<div class="mt-5">
<x-secondary-button class="mr-3"
wire:click="start"
wire:target="start"
wire:loading.attr="disabled"
>
{{ $buttonText }}
</x-secondary-button>
</div>
</x-slot>
</x-action-section>

View file

@ -1,24 +0,0 @@
<div class="mt-10 sm:mt-0">
<x-action-section>
<x-slot name="title">
{{ __('Available Satellite data') }}
</x-slot>
<x-slot name="description">
{{ __('The grid has a date for every date the data has already been downloaded.') }}
</x-slot>
<!-- API Token List -->
<x-slot name="content">
<div class="space-y-6">
@foreach ($files as $file)
<div class="flex items-center justify-between">
<div class="break-all">
{{ $file }}
</div>
</div>
@endforeach
</div>
</x-slot>
</x-action-section>
</div>

View file

@ -1,16 +1,60 @@
<x-tab-section>
<x-slot name="title">Download details</x-slot>
<x-slot name="description">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsa, reprehenderit.</x-slot>
<x-slot name="form">
<div class="max-w-7xl mx-auto py-10 sm:px-6 lg:px-8">
<div class="mt-10 sm:mt-0">
<livewire:download.download-grid :project="$project"/>
<div
@if($project->hasPendingDownload())
wire:poll.1s=""
@endif
>
<div class="px-4 sm:px-6 lg:px-8">
<div class="sm:flex sm:items-center">
<div class="sm:flex-auto">
<h1 class="text-base font-semibold leading-6 text-gray-900">Downloads</h1>
<p class="mt-2 text-sm text-gray-700">
@if ($project->hasPendingDownload())
Pending downloads for this project: {{ $project->downloads()->where('status', 'pending')->count() }}
@endif
</p>
</div>
<x-section-border />
<div class="mt-10 sm:mt-0">
@livewire('download.download-form')
<div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
<x-button wire:click="openDownloadModal"
class="block rounded-md bg-indigo-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
{{ __('Create Download') }}
</x-button>
</div>
</div>
</x-slot>
</x-tab-section>
<div class="mt-8 flow-root">
<div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="inline-block min-w-full py-2 align-middle">
<table class="min-w-full divide-y divide-gray-300">
<thead>
<tr>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6 lg:pl-8">
Name
</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
Status
</th>
<th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6 lg:pr-8">
<span class="sr-only">Edit</span>
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
@foreach($project->downloads()->orderBy('name', 'desc')->get() as $download)
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6 lg:pl-8">{{ $download->name }}</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
{{ $download->path }}
</td>
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8">
<x-badge :status="$download->status"></x-badge>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
<x-download-create-modal :manager="$this"/>
</div>

View file

@ -1,8 +1,12 @@
<div>
<div
@if($project->hasPendingMosaic())
wire:poll.1s=""
@endif
>
<div class="px-4 sm:px-6 lg:px-8">
<div class="sm:flex sm:items-center">
<div class="sm:flex-auto">
<h1 class="text-base font-semibold leading-6 text-gray-900">Reports</h1>
<h1 class="text-base font-semibold leading-6 text-gray-900">Mosaics</h1>
<p class="mt-2 text-sm text-gray-700"></p>
</div>
<div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
@ -31,14 +35,14 @@ class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6 lg
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
@foreach($project->getMosaicList() as $mosaic)
@foreach($project->mosaics()->orderBy('year','desc')->orderBy('week', 'desc')->get() as $mosaic)
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6 lg:pl-8">{{ $project->name }}</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
{{ $mosaic }}
{{ $mosaic->name }}-{{ $mosaic->week}}
</td>
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8">
<x-badge :status="$mosaic->status"></x-badge>
</td>
</tr>
@endforeach

View file

@ -37,7 +37,7 @@ class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6 lg
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
@foreach($project->reports()->orderBy('created_at', 'desc')->get() as $report)
@foreach($project->reports()->orderBy('year', 'desc')->orderBy('week', 'desc')->get() as $report)
<livewire:project.report-row :$report :key="$report->id"/>
@endforeach
</tbody>

View file

@ -15,9 +15,6 @@
<x-nav-link href="{{ route('dashboard') }}" :active="request()->routeIs('dashboard')">
{{ __('Dashboard') }}
</x-nav-link>
<x-nav-link href="{{ route('download') }}" :active="request()->routeIs('download')">
{{ __('Download') }}
</x-nav-link>
<x-nav-link href="{{ route('report') }}" :active="request()->routeIs('report')">
{{ __('Report') }}
</x-nav-link>

View file

@ -29,7 +29,6 @@
Route::get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
Route::get('/download', [\App\Http\Controllers\DownloadController::class, 'show'])->name('download');
Route::get('/report', [\App\Http\Controllers\ReportController::class, 'index'])->name('report');
Route::get('/projects/{project}', [\App\Http\Controllers\ProjectController::class, 'show'])->name('project.show');
Route::get('/projects/{projectReport}/download', [\App\Http\Controllers\ProjectReportController::class, 'download'])->name('project.report.download');

View file

@ -7,6 +7,7 @@
use App\Rules\AllMergedTiffsPresentRule;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Validator;
use Mockery;
use Tests\TestCase;
@ -48,10 +49,12 @@ public function validatesCorrectValue()
$rule = new AllMergedTiffsPresentRule($projectMock);
$this->assertTrue($rule->passes(
'attribute',
['year' => '2021', 'week' => '1']
));
$val = Validator::make(
['attribute' => ['year' => '2021', 'week' => '1']],
['attribute' => $rule]
);
$this->assertTrue($val->passes());
}
public function testInvalidatesIncorrectValue()
@ -81,13 +84,15 @@ public function testInvalidatesIncorrectValue()
$rule = new AllMergedTiffsPresentRule($projectMock);
$this->assertFalse($rule->passes(
'attribute',
['year' => '2021', 'week' => '1']
));
$val = Validator::make(
['attribute' => ['year' => '2021', 'week' => '1']],
['attribute' => $rule]
);
$this->assertFalse($val->passes());
$this->assertEquals(
'Missing merged tiffs: 2021-01-04',
$rule->message()
$val->errors()->first()
);
}

View file

@ -0,0 +1,83 @@
<?php
namespace Tests\Unit\Rules;
use App\Models\Project;
use App\Rules\AllMergedTiffsPresentRule;
use App\Rules\DownloadDateRangeRule;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Validator;
use Mockery;
use Tests\TestCase;
class DownloadDateRangeRuleTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp(); // TODO: Change the autogenerated stub
}
/**
* @test
*/
public function validatesCorrectValue()
{
$val = Validator::make(
['dateRange' => '2023/12/26 to 2024/01/02'],
['dateRange' => new DownloadDateRangeRule()]
);
$this->assertTrue($val->passes());
$this->assertEmpty(
$val->errors()
);
}
public function testInvalidatesIncorrectValue()
{
Carbon::setTestNow(Carbon::create(2021, 1, 1));
$val = Validator::make(
['dateRange' => '2023/12/26 to 2024/01/02'],
['dateRange' => new DownloadDateRangeRule()]
);
$this->assertFalse($val->passes());
$this->assertEquals(
'Date range cannot be in the future.',
$val->errors()->first()
);
}
public function testInvalidFormat()
{
$val = Validator::make(
['dateRange' => '2023/12/26 tot 2024/01/02'],
['dateRange' => new DownloadDateRangeRule()]
);
$this->assertFalse($val->passes());
$this->assertEquals(
'Date range must be in the format YYYY/MM/DD to YYYY/MM/DD.',
$val->errors()->first()
);
}
public function testEndDateBeforeStartDate()
{
$val = Validator::make(
['dateRange' => '2024/01/02 to 2023/12/26'],
['dateRange' => new DownloadDateRangeRule()]
);
$this->assertFalse($val->passes());
$this->assertEquals(
'Start date must be before end date.',
$val->errors()->first()
);
}
}

View file

@ -333,8 +333,16 @@
"\n",
"days_needed = int(os.environ.get(\"DAYS\", 28))\n",
" # Adjust the number of days needed\n",
" \n",
"date_str = os.environ.get(\"DATE\")\n",
"if date_str:\n",
" # Parse de datumstring naar een datetime.date object\n",
" end = datetime.datetime.strptime(date_str, \"%Y-%m-%d\").date()\n",
"else:\n",
" # Gebruik de huidige datum als fallback\n",
" end = datetime.date.today() \n",
"\n",
"\n",
"end = datetime.date.today()\n",
"#end = datetime.datetime(2023, 11, 10)\n",
"#start = datetime.datetime(2023, 10, 19)\n",
"start = end - datetime.timedelta(days=days_needed - 1)\n",

View file

@ -37,7 +37,7 @@ extracted_CI_dir <- here(data_dir, "extracted_ci")
daily_CI_vals_dir <- here(extracted_CI_dir, "daily_vals")
cumulative_CI_vals_dir <- here(extracted_CI_dir, "cumulative_vals")
weekly_CI_mosaic <- here(data_dir, "weekly_mosaic")
weekly_CI_mosaic <- here(laravel_storage_dir, "chemba/weekly_mosaic")
daily_vrt <- here(data_dir, "vrt")
harvest_dir <- here(data_dir, "HarvestData")
@ -53,16 +53,18 @@ dir.create(merged_final)
# Creating weekly mosaic
dates <- date_list(weeks_ago)
head(dates)
print(planet_tif_folder)
#load pivot geojson
pivot_sf_q <- st_read(here( "pivot_20210625.geojson")) %>% dplyr::select(pivot, pivot_quadrant) %>% vect()
raster_files <- list.files(planet_tif_folder,full.names = T, pattern = ".tif")
head(raster_files)
filtered_files <- map(dates$days_filter, ~ raster_files[grepl(pattern = .x, x = raster_files)]) %>%
compact() %>%
flatten_chr()
#rasters_masked <- map(filtered_files, mask_raster, fields = pivot_sf_q) %>% set_names(filtered_files)
@ -79,7 +81,9 @@ message("starting ", file)
CI <- rast(file)
# names(CI) <- c("green","nir")
message("raster loaded")
print(CI)
CI <- CI[[2]]/CI[[1]]-1
# CI <- CI$nir/CI$green-1
message("CI calculated")
@ -228,6 +232,7 @@ plot(x$CI, main = paste("CI map", dates$week))
# writeRaster(x, here(weekly_CI_mosaic ,paste0("week_", dates$week, "_", dates$year, ".tif")), overwrite=TRUE)
writeRaster(x, here(weekly_CI_mosaic ,paste0("week_", dates$week, "_", dates$year, ".tif")), overwrite=TRUE)
message("raster written/ made")
message( here(weekly_CI_mosaic ,paste0("week_", dates$week, "_", dates$year, ".tif")))
# Extracting CI

Binary file not shown.

View file

@ -1,24 +1,27 @@
#!/bin/bash
date=$(date +%Y-%m-%d)
# Standaardwaarde voor days
days=7
days=1
# Loop door alle argumenten
for arg in "$@"
do
case $arg in
while [ "$#" -gt 0 ]; do
case "$1" in
--days=*)
days="${arg#*=}"
shift # Verwijder --days=5 van $@
;;
days="${1#*=}"
;;
--date=*)
date="${1#*=}"
;;
*)
# Andere argumenten die je wilt verwerken
shift
;;
echo "Onbekende optie: $1"
exit 1
;;
esac
shift
done
# Gebruik de variabele in je script
echo "Datum: $date"
echo "Aantal dagen: $days"
# Activeer de virtuele omgeving
@ -26,6 +29,7 @@ script_dir="$(dirname "$0")"
source "$script_dir/python_app/myenv/bin/activate"
export DAYS=$days
export DATE=$date
# Hier kan je verdere stappen toevoegen, zoals het uitvoeren van je Python-script of Jupyter Notebook
jupyter nbconvert --execute --to script --stdout "$script_dir/python_app/Chemba_download.ipynb"