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

Create volume flow v2 #709

Merged
merged 120 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
120 commits
Select commit Hold shift + click to select a range
a8600dc
Create PendingVolume model
mzur Nov 29, 2023
25389f0
Start to implement pending volume API endpoint
mzur Nov 30, 2023
0550180
Implement Project::pendingVolumes
mzur Nov 30, 2023
87cd37a
Start implementing the CSV metadata parser
mzur Nov 30, 2023
01ee815
Merge branch 'master' into create-volume-v2
mzur Dec 6, 2023
23734e2
WIP Implement ImageCsvParser
mzur Dec 6, 2023
be8b6d0
Continue implementing ImageCsvParser
mzur Dec 7, 2023
c2b1872
Implement ParserFactory
mzur Dec 11, 2023
f1c8122
Update the ImageMetadata validation rule
mzur Dec 11, 2023
993044c
Implement VideoCsvParser
mzur Dec 12, 2023
480cec7
Merge branch 'master' into create-volume-v2
mzur Dec 13, 2023
2437e98
Fix VideoMetadata validation rule for new metadata objects
mzur Dec 18, 2023
b6fe976
Merge branch 'master' into create-volume-v2
mzur Mar 6, 2024
08f73ef
Add media type to metadata file detection
mzur Mar 6, 2024
2310e99
Do not match files in metadata validation
mzur Mar 6, 2024
b535d7f
Implement metadata file upload for new pending volumes
mzur Mar 6, 2024
4d962ba
Implement pending volume update API endpoint
mzur Mar 7, 2024
a432727
WIP Implement metadata support in the v1 create volume controller
mzur Mar 7, 2024
46ad300
Fix test for v1 create volume controller
mzur Mar 11, 2024
684246c
Implement methods to save, get and delete volume metadata
mzur Mar 11, 2024
7096741
Implement download and deletion of generic volume metadata files
mzur Mar 11, 2024
696a36c
Remove ParsesMetadata trait
mzur Mar 11, 2024
c05bc0b
Remove ParseIfdoController
mzur Mar 11, 2024
6b66cb9
Implement 500MB file size for volume metadata files
mzur Mar 11, 2024
a740de7
WIP Implement getInsertData for file metadata classes
mzur Mar 11, 2024
c7a1f39
Implement getInsertData for VideoMetadata
mzur Mar 12, 2024
ad99725
Update CreateNewImagesOrVideos with new metadata handling
mzur Mar 12, 2024
8137363
Update volume MetadataController with new metadata handling
mzur Mar 12, 2024
ddf8e1e
Update CloneImagesorVideos job with new metadata handling
mzur Mar 12, 2024
1a5c2e8
Fix destructor of StoreVolume request
mzur Mar 12, 2024
dc71922
Fix leaking metadata files from tests
mzur Mar 12, 2024
c9afa06
Fix volume metadata file extension
mzur Mar 12, 2024
b1b9c4a
Update volume edit view for new metadata handling
mzur Mar 12, 2024
67746f8
Apply CS fixes
mzur Mar 12, 2024
75b328f
Fix geo info cache in UpdateVolumeMetadata job
mzur Mar 12, 2024
549e9e3
Implement create volume v2 storage of metadata file
mzur Mar 12, 2024
7eb79c5
Implement the first step of the new volume creation UI
mzur Mar 13, 2024
9f74bce
Fix pending volume metadata file extension
mzur Mar 13, 2024
d2298c3
Finish create volume v2 UI without metadata import
mzur Mar 14, 2024
6455364
Improve UX of create volume v2 step 1
mzur Mar 14, 2024
7352ab4
Fix CS
mzur Mar 14, 2024
6d52278
Implement continuing of previous pending volume
mzur Mar 14, 2024
61d9b7c
Improve create volume v2 step 2 UX
mzur Mar 14, 2024
2b44bae
Implement a way to extend metadata parsers from a module
mzur Mar 14, 2024
9e9283d
Add deprecated note to create volume v1 endpoint
mzur Mar 14, 2024
06a0326
Implement MIME type validation for metadata parsers
mzur Mar 15, 2024
a11482c
Fix markup of create volume v2 forms
mzur Mar 15, 2024
ed462b9
Remove numbers from form labels
mzur Mar 15, 2024
5a795cd
Implement HasMetadataFile trait for volumes and pending volumes
mzur Mar 15, 2024
b1685c7
Implement metadata auto-fill in create volume step 2
mzur Mar 15, 2024
86d973a
Remove statically prefilled volume filenames again
mzur Mar 15, 2024
a652e6f
Implement warning if metadata files do not match chosen files
mzur Mar 15, 2024
1600d32
Add annotation metadata classes
mzur Mar 20, 2024
cfc3ddc
Update gitignore for metadata storage disks
mzur Mar 20, 2024
a85a761
Implement file label metadata support
mzur Mar 20, 2024
d2e4460
Implement keep attribute of pending volume update endpoint
mzur Mar 20, 2024
b4ad427
Implement ability to mock metadata parsers for testing
mzur Mar 20, 2024
6acee05
Apply CS fixes
mzur Mar 20, 2024
9074839
Replace keep attribute with import attributes
mzur Mar 20, 2024
8789979
Replace metadata parser mocking with cached metadata
mzur Mar 20, 2024
41f334c
Add convenience methods for metadata labels
mzur Mar 21, 2024
b566283
Add API endpoint to choose annotation labels for import
mzur Mar 21, 2024
f7396c5
Add API endpoint to choose file labels for import
mzur Mar 21, 2024
c944c55
Add API endpoint to choose a label map for import
mzur Mar 21, 2024
f858846
Fix test
mzur Mar 21, 2024
389b38e
Add convenience methods to get metadata users
mzur Mar 21, 2024
f904b20
Add API endpoint to choose a user map for import
mzur Mar 21, 2024
beb67c0
Move metadata import endpoints to own controller
mzur Mar 21, 2024
2ab11b6
Use PUT for metadata import endpoints again
mzur Mar 21, 2024
519a82e
WIP Implement start metadata import API endpoint
mzur Mar 21, 2024
39800e2
Fix linter errors
mzur Mar 21, 2024
5883597
Implement user matching for metadata import
mzur Mar 22, 2024
b150c62
Implement label matching for metadata import
mzur Mar 22, 2024
cc056ad
Implement basic features for ImportVolumeMetadata job
mzur Mar 22, 2024
dcddb25
Fix array_merge with associative array unpacking
mzur Mar 22, 2024
1d8363e
Implement more tests for ImportVolumeMetadata job
mzur Mar 22, 2024
d798388
WIP Add tests to highlight issue with union of ignored labels
mzur Mar 22, 2024
a2e9f3d
Fix userMap and labelMap filtering for metadata import
mzur Mar 25, 2024
7d64d64
Configure default values for PendingVolume arrays
mzur Mar 25, 2024
1e4159b
Finish implementing ImportVolumeMetadata
mzur Mar 25, 2024
ce23e7a
Add validation methods for metadata annotations
mzur Mar 25, 2024
3bcc71d
Merge branch 'master' into create-volume-v2
mzur Mar 25, 2024
d8748a1
Implement metadata annotation validation before import
mzur Mar 25, 2024
3482e0a
Fix linter error
mzur Mar 25, 2024
4d0e87d
Add import annotations/file labels buttons to create volume step 2
mzur Mar 25, 2024
208252e
Start to implement volumes.create.annotationLabels view
mzur Mar 25, 2024
1c4a979
Fix warning display in create volume v2 step 2
mzur Mar 26, 2024
1d409bb
WIP Implement the select annotation labels view for metadata import
mzur Mar 27, 2024
8462468
Fix linter error
mzur Mar 27, 2024
1fc82da
Implement pending volume redirect to select annotation label view
mzur Apr 4, 2024
72299b7
Implement select metadata import step 4 view
mzur Apr 4, 2024
34ca147
Add API redirect for import metadata step 3 UI requests
mzur Apr 4, 2024
48d09e7
Implement empty metadata import label map view
mzur Apr 4, 2024
ad177a3
Implement empty metadata import user map view
mzur Apr 4, 2024
35d6c38
Fix CS
mzur Apr 4, 2024
99a54d6
WIP Work on label map view for metadata import
mzur Apr 5, 2024
da51131
Fix LabelTypeahead component
mzur Apr 9, 2024
c8eb820
Improve volume import label map endpoint
mzur Apr 9, 2024
9aa6a44
Implement basic features for volume import label map view
mzur Apr 9, 2024
efd0a51
Finish volume import label map view
mzur Apr 9, 2024
1f2cd4d
Polish label map view
mzur Apr 10, 2024
1f94051
Implement volume import user map view
mzur Apr 10, 2024
a028d87
Add missing use
mzur Apr 10, 2024
86ecebe
Implement finish volume metadata import view
mzur Apr 11, 2024
0a8aa81
Remove unnecessary steps
mzur Apr 11, 2024
c8c67f4
Update manual for new create volume flow
mzur Apr 11, 2024
92fc963
WIP Start to implement explicit metadata parser specification
mzur Apr 12, 2024
3e2d0a0
Fix CS
mzur Apr 15, 2024
064728d
Implement metadata parser selection in the UI
mzur Apr 15, 2024
f73effd
Remove PHP YAML extension from Docker images
mzur Apr 24, 2024
952015b
Remove metadata label label tree UUID
mzur Apr 24, 2024
569a273
Improve final overview before annotation import
mzur May 22, 2024
8548804
Merge branch 'master' into create-volume-v2
mzur Sep 2, 2024
949d8fc
Work on type errors
mzur Sep 3, 2024
5a4399f
Fix type issues
mzur Sep 3, 2024
16e85cf
Configure lint PHP action with GitHub output and add test error
mzur Sep 3, 2024
6995a74
Fix intentional type error
mzur Sep 3, 2024
b97415e
Fix type issues
mzur Sep 3, 2024
aa37ac9
Fix metadata import issues
mzur Sep 3, 2024
7075889
Add timestamps to imported annotations and labels
mzur Sep 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .docker/all-php.ini

This file was deleted.

11 changes: 0 additions & 11 deletions .docker/app.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ LABEL org.opencontainers.image.authors="Martin Zurowietz <m.zurowietz@uni-bielef
LABEL org.opencontainers.image.source="https://github.com/biigle/core"

RUN ln -s "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
ADD ".docker/all-php.ini" "$PHP_INI_DIR/conf.d/all.ini"
ADD ".docker/app-php.ini" "$PHP_INI_DIR/conf.d/app.ini"

RUN apk add --no-cache \
Expand All @@ -27,16 +26,6 @@ RUN apk add --no-cache \
soap \
&& apk del --purge .build-deps

# Configure proxy if there is any. See: https://stackoverflow.com/a/2266500/1796523
RUN [ -z "$HTTP_PROXY" ] || pear config-set http_proxy $HTTP_PROXY
RUN apk add --no-cache yaml \
&& apk add --no-cache --virtual .build-deps g++ make autoconf yaml-dev \
&& pecl install yaml \
&& docker-php-ext-enable yaml \
&& apk del --purge .build-deps
# Unset proxy configuration again.
RUN [ -z "$HTTP_PROXY" ] || pear config-set http_proxy ""

ARG PHPREDIS_VERSION=6.0.2
RUN curl -L -o /tmp/redis.tar.gz https://github.com/phpredis/phpredis/archive/${PHPREDIS_VERSION}.tar.gz \
&& tar -xzf /tmp/redis.tar.gz \
Expand Down
14 changes: 0 additions & 14 deletions .docker/worker.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ RUN LC_ALL=C.UTF-8 apt-get update \
&& rm -r /var/lib/apt/lists/*

RUN ln -s "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
ADD ".docker/all-php.ini" "$PHP_INI_DIR/conf.d/all.ini"
# Enable FFI for jcupitt/vips.
# See: https://github.com/libvips/php-vips?tab=readme-ov-file#install
RUN echo "ffi.enable = true" > "$PHP_INI_DIR/conf.d/vips.ini"
Expand Down Expand Up @@ -58,19 +57,6 @@ RUN LC_ALL=C.UTF-8 apt-get update \
# Configure proxy if there is any. See: https://stackoverflow.com/a/2266500/1796523
RUN [ -z "$HTTP_PROXY" ] || pear config-set http_proxy $HTTP_PROXY

RUN LC_ALL=C.UTF-8 apt-get update \
&& apt-get install -y --no-install-recommends \
libyaml-dev \
&& apt-get install -y --no-install-recommends \
libyaml-0-2 \
&& pecl install yaml \
&& printf "\n" | docker-php-ext-enable yaml \
&& apt-get purge -y \
libyaml-dev \
&& apt-get -y autoremove \
&& apt-get clean \
&& rm -r /var/lib/apt/lists/*

ARG PHPREDIS_VERSION=6.0.2
RUN curl -L -o /tmp/redis.tar.gz https://github.com/phpredis/phpredis/archive/${PHPREDIS_VERSION}.tar.gz \
&& tar -xzf /tmp/redis.tar.gz \
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
run: echo "APP_KEY=base64:STZFA4bQKDjE2mlpRPmsJ/okG0eCh4RHd9BghtZeYmQ=" >> .env

- name: Run Linter
run: composer lint
run: composer lint -- --error-format=github

cs-php:

Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
/public/vendor
/storage/*.key
/storage/largo_patches
/storage/metadata
/storage/pending-metadata
/vendor
.env
.env.backup
Expand Down
203 changes: 203 additions & 0 deletions app/Http/Controllers/Api/PendingVolumeController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
<?php

namespace Biigle\Http\Controllers\Api;

use Biigle\Http\Requests\StorePendingVolume;
use Biigle\Http\Requests\UpdatePendingVolume;
use Biigle\Http\Requests\UpdatePendingVolumeAnnotationLabels;
use Biigle\Jobs\CreateNewImagesOrVideos;
use Biigle\PendingVolume;
use Biigle\Volume;
use DB;
use Illuminate\Http\Request;
use Queue;
use Storage;

class PendingVolumeController extends Controller
{
/**
* Limit for the number of files above which volume files are created asynchronously.
*
* @var int
*/
const CREATE_SYNC_LIMIT = 10000;

/**
* Creates a new pending volume associated to the specified project.
*
* @api {post} projects/:id/pending-volumes Create a new pending volume
* @apiGroup Volumes
* @apiName StoreProjectPendingVolumes
* @apiPermission projectAdmin
*
* @apiParam {Number} id The project ID.
*
* @apiParam (Required attributes) {String} media_type The media type of the new volume (`image` or `video`).
*
* @apiParam (Optional attributes) {File} metadata_file A file with volume and image/video metadata. By default, this can be a CSV. See "metadata columns" for the possible columns. Each column may occur only once. There must be at least one column other than `filename`. For video metadata, multiple rows can contain metadata from different times of the same video. In this case, the `filename` of the rows must match and each row needs a (different) `taken_at` timestamp. Other file formats may be supported through modules. This attribute is required if `metadata_parser` is specified.
* @apiParam (Optional attributes) {String} metadata_parser The class namespace of the metadata parser to use. The default CSV parsers are: `Biigle\Services\MetadataParsing\ImageCsvParser` and `Biigle\Services\MetadataParsing\VideoCsvParser`. This attribute is required if `metadata_file` is specified.
*
* @apiParam (metadata columns) {String} filename The filename of the file the metadata belongs to. This column is required.
* @apiParam (metadata columns) {String} taken_at The date and time where the file was taken. Example: `2016-12-19 12:49:00`
* @apiParam (metadata columns) {Number} lng Longitude where the file was taken in decimal form. If this column is present, `lat` must be present, too. Example: `52.3211`
* @apiParam (metadata columns) {Number} lat Latitude where the file was taken in decimal form. If this column is present, `lng` must be present, too. Example: `28.775`
* @apiParam (metadata columns) {Number} gps_altitude GPS Altitude where the file was taken in meters. Negative for below sea level. Example: `-1500.5`
* @apiParam (metadata columns) {Number} distance_to_ground Distance to the sea floor in meters. Example: `30.25`
* @apiParam (metadata columns) {Number} area Area shown by the file in m². Example `2.6`.
*
* @apiSuccessExample {json} Success response:
* {
* "id": 2,
* "created_at": "2015-02-19 16:10:17",
* "updated_at": "2015-02-19 16:10:17",
* "media_type_id": 1,
* "user_id": 2,
* "project_id": 3,
* "volume_id": null
* }
*/
public function store(StorePendingVolume $request)
{
$pv = $request->project->pendingVolumes()->create([
'media_type_id' => $request->input('media_type_id'),
'user_id' => $request->user()->id,
'metadata_parser' => $request->input('metadata_parser', null),
]);

if ($request->has('metadata_file')) {
$pv->saveMetadata($request->file('metadata_file'));
}

if ($this->isAutomatedRequest()) {
return $pv;
}

return redirect()->route('pending-volume', $pv->id);
}

/**
* Update a pending volume to create an actual volume
*
* @api {put} pending-volumes/:id Create a new volume (v2)
* @apiGroup Volumes
* @apiName UpdatePendingVolume
* @apiPermission projectAdminAndPendingVolumeOwner
*
* @apiDescription When this endpoint is called, the new volume is already created. Then there are two ways forward: 1) The user wants to import annotations and/or file labels. Then the pending volume is kept and used for the next steps (see the `import_*` attributes). Continue with (#Volumes:UpdatePendingVolumeAnnotationLabels) in this case. 2) Otherwise the pending volume will be deleted here. In both cases the endpoint returns the pending volume (even if it was deleted) which was updated with the new volume ID.
*
* @apiParam {Number} id The pending volume ID.
*
* @apiParam (Required attributes) {String} name The name of the new volume.
* @apiParam (Required attributes) {String} url The base URL of the image/video files. Can be a path to a storage disk like `local://volumes/1` or a remote path like `https://example.com/volumes/1`.
* @apiParam (Required attributes) {Array} files Array of file names of the images/videos that can be found at the base URL. Example: With the base URL `local://volumes/1` and the image `1.jpg`, the file `volumes/1/1.jpg` of the `local` storage disk will be used. This can also be a plain string of comma-separated filenames.
*
* @apiParam (Optional attributes) {String} handle Handle or DOI of the dataset that is represented by the new volume.
* @apiParam (Optional attributes) {Boolean} import_annotations Set to `true` to keep the pending volume for annotation import. Otherwise the pending volume will be deleted after this request.
* @apiParam (Optional attributes) {Boolean} import_file_labels Set to `true` to keep the pending volume for file label import. Otherwise the pending volume will be deleted after this request.
*
* @apiSuccessExample {json} Success response:
* {
* "id": 2,
* "created_at": "2015-02-19 16:10:17",
* "updated_at": "2015-02-19 16:10:17",
* "media_type_id": 1,
* "user_id": 2,
* "project_id": 3,
* "volume_id": 4,
* "import_annotations": true,
* "import_file_labels": false
* }
*
*/
public function update(UpdatePendingVolume $request)
{
$pv = $request->pendingVolume;
$volume = DB::transaction(function () use ($request, $pv) {

$volume = Volume::create([
'name' => $request->input('name'),
'url' => $request->input('url'),
'media_type_id' => $pv->media_type_id,
'handle' => $request->input('handle'),
'creator_id' => $request->user()->id,
]);

$pv->project->volumes()->attach($volume);

if ($pv->hasMetadata()) {
$volume->update([
'metadata_file_path' => $volume->id.'.'.pathinfo($pv->metadata_file_path, PATHINFO_EXTENSION),
'metadata_parser' => $pv->metadata_parser,
]);
$stream = Storage::disk(config('volumes.pending_metadata_storage_disk'))
->readStream($pv->metadata_file_path);
Storage::disk(config('volumes.metadata_storage_disk'))
->writeStream($volume->metadata_file_path, $stream);
}

$files = $request->input('files');

// If too many files should be created, do this asynchronously in the
// background. Else the script will run in the 30s execution timeout.
$job = new CreateNewImagesOrVideos($volume, $files);
if (count($files) > self::CREATE_SYNC_LIMIT) {
Queue::pushOn('high', $job);
$volume->creating_async = true;
$volume->save();
} else {
Queue::connection('sync')->push($job);
}

return $volume;
});

if ($request->input('import_annotations') || $request->input('import_file_labels')) {
$pv->update([
'volume_id' => $volume->id,
'import_annotations' => $request->input('import_annotations', false),
'import_file_labels' => $request->input('import_file_labels', false),
]);
} else {
$pv->volume_id = $volume->id;
$pv->delete();
}

if ($this->isAutomatedRequest()) {
return $pv;
}

if ($pv->import_annotations) {
$redirect = redirect()->route('pending-volume-annotation-labels', $pv->id);
} elseif ($pv->import_file_labels) {
$redirect = redirect()->route('pending-volume-file-labels', $pv->id);
} else {
$redirect = redirect()->route('volume', $volume->id);
}

return $redirect
->with('message', 'Volume created.')
->with('messageType', 'success');
}

/**
* Delete a pending volume
*
* @api {delete} pending-volumes/:id Discard a pending volume
* @apiGroup Volumes
* @apiName DestroyPendingVolume
* @apiPermission projectAdminAndPendingVolumeOwner
*
* @param Request $request]
*/
public function destroy(Request $request)
{
$pv = PendingVolume::findOrFail($request->route('id'));
$this->authorize('destroy', $pv);

$pv->delete();

if (!$this->isAutomatedRequest()) {
return $this->fuzzyRedirect('create-volume', ['project' => $pv->project_id]);
}
}
}
Loading