wip
This commit is contained in:
parent
3fb55ddcce
commit
4c22ab6758
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
}
|
||||
//
|
||||
}
|
||||
65
laravel_app/app/Jobs/ProjectDownloadTiffJob.php
Normal file
65
laravel_app/app/Jobs/ProjectDownloadTiffJob.php
Normal 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',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -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',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
@ -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', [
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,4 +8,10 @@
|
|||
class ProjectDownload extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'path',
|
||||
'status',
|
||||
];
|
||||
}
|
||||
|
|
|
|||
25
laravel_app/app/Models/ProjectMosaic.php
Normal file
25
laravel_app/app/Models/ProjectMosaic.php
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
45
laravel_app/app/Rules/DownloadDateRangeRule.php
Normal file
45
laravel_app/app/Rules/DownloadDateRangeRule.php
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
};
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
6
laravel_app/package-lock.json
generated
6
laravel_app/package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,4 @@
|
|||
import './bootstrap';
|
||||
import('./alpine');
|
||||
import flatpckr from 'flatpickr';
|
||||
window.flatpckr = flatpckr;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
83
laravel_app/tests/Unit/Rules/DownloadDateRangeRuleTest.php
Normal file
83
laravel_app/tests/Unit/Rules/DownloadDateRangeRuleTest.php
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
BIN
r_app/Rplots.pdf
BIN
r_app/Rplots.pdf
Binary file not shown.
26
runpython.sh
26
runpython.sh
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in a new issue