Skip to content

Commit f1c8122

Browse files
committed
Update the ImageMetadata validation rule
1 parent c2b1872 commit f1c8122

File tree

7 files changed

+140
-235
lines changed

7 files changed

+140
-235
lines changed

app/Rules/ImageMetadata.php

Lines changed: 30 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -3,53 +3,24 @@
33
namespace Biigle\Rules;
44

55
use Illuminate\Contracts\Validation\Rule;
6+
use Biigle\Services\MetadataParsing\VolumeMetadata;
67

78
class ImageMetadata implements Rule
89
{
9-
/**
10-
* Allowed columns for the metadata information to change image attributes.
11-
*
12-
* @var array
13-
*/
14-
const ALLOWED_ATTRIBUTES = [
15-
'lat',
16-
'lng',
17-
'taken_at',
18-
];
19-
20-
/**
21-
* Allowed columns for the metadata information to change image metadata.
22-
*
23-
* @var array
24-
*/
25-
const ALLOWED_METADATA = [
26-
'area',
27-
'distance_to_ground',
28-
'gps_altitude',
29-
'yaw',
30-
];
31-
3210
/**
3311
* All numeric metadata fields (keys) with description (values).
3412
*
3513
* @var array
3614
*/
3715
const NUMERIC_FIELDS = [
3816
'area' => 'area',
39-
'distance_to_ground' => 'distance to ground',
40-
'gps_altitude' => 'GPS altitude',
17+
'distanceToGround' => 'distance to ground',
18+
'gpsAltitude' => 'GPS altitude',
4119
'lat' => 'latitude',
4220
'lng' => 'longitude',
4321
'yaw' => 'yaw',
4422
];
4523

46-
/**
47-
* Array of volume file names.
48-
*
49-
* @var array
50-
*/
51-
protected $files;
52-
5324
/**
5425
* The validation error message.
5526
*
@@ -62,157 +33,84 @@ class ImageMetadata implements Rule
6233
*
6334
* @param array $files
6435
*/
65-
public function __construct($files)
36+
public function __construct(public array $files)
6637
{
67-
$this->files = $files;
6838
$this->message = "The :attribute is invalid.";
6939
}
7040

7141
/**
7242
* Determine if the validation rule passes.
73-
*
74-
* @param string $attribute
75-
* @param array $value
76-
* @return bool
7743
*/
78-
public function passes($attribute, $value)
44+
public function passes($attribute, $value): bool
7945
{
80-
if (!is_array($value)) {
81-
return false;
46+
if (!($value instanceof VolumeMetadata)) {
47+
throw new \Exception('No value of type '.VolumeMetadata::class.' given.');
8248
}
8349

50+
$fileMetadata = $value->getFiles();
8451
// This checks if any information is given at all.
85-
if (empty($value)) {
86-
$this->message = 'The metadata information is empty.';
87-
88-
return false;
89-
}
90-
91-
$columns = array_shift($value);
92-
93-
// This checks if any information is given beside the column description.
94-
if (empty($value)) {
52+
if ($fileMetadata->isEmpty()) {
9553
$this->message = 'The metadata information is empty.';
9654

9755
return false;
9856
}
9957

100-
if (!in_array('filename', $columns)) {
101-
$this->message = 'The filename column is required.';
102-
103-
return false;
104-
}
105-
106-
$colCount = count($columns);
58+
foreach ($fileMetadata as $file) {
10759

108-
if ($colCount === 1) {
109-
$this->message = 'No metadata columns given.';
110-
111-
return false;
112-
}
113-
114-
if ($colCount !== count(array_unique($columns))) {
115-
$this->message = 'Each column may occur only once.';
116-
117-
return false;
118-
}
119-
120-
$allowedColumns = array_merge(['filename'], self::ALLOWED_ATTRIBUTES, self::ALLOWED_METADATA);
121-
$diff = array_diff($columns, $allowedColumns);
122-
123-
if (count($diff) > 0) {
124-
$this->message = 'The columns array may contain only values of: '.implode(', ', $allowedColumns).'.';
125-
126-
return false;
127-
}
128-
129-
$lng = in_array('lng', $columns);
130-
$lat = in_array('lat', $columns);
131-
if ($lng && !$lat || !$lng && $lat) {
132-
$this->message = "If the 'lng' column is present, the 'lat' column must be present, too (and vice versa).";
133-
134-
return false;
135-
}
136-
137-
foreach ($value as $index => $row) {
138-
// +1 since index starts at 0.
139-
// +1 since column description row was removed above.
140-
$line = $index + 2;
141-
142-
if (count($row) !== $colCount) {
143-
$this->message = "Invalid column count in line {$line}.";
144-
145-
return false;
146-
}
147-
148-
$combined = array_combine($columns, $row);
149-
$combined = array_filter($combined);
150-
if (!array_key_exists('filename', $combined)) {
151-
$this->message = "Filename missing in line {$line}.";
60+
if (!in_array($file->name, $this->files)) {
61+
$this->message = "There is no file with filename {$file->name}.";
15262

15363
return false;
15464
}
15565

156-
$filename = $combined['filename'];
157-
158-
if (!in_array($filename, $this->files)) {
159-
$this->message = "There is no file with filename {$filename}.";
160-
161-
return false;
162-
}
163-
164-
if (array_key_exists('lng', $combined)) {
165-
$lng = $combined['lng'];
166-
if (!is_numeric($lng) || abs($lng) > 180) {
167-
$this->message = "'{$lng}' is no valid longitude for file {$filename}.";
66+
if (!is_null($file->lng)) {
67+
if (!is_numeric($file->lng) || abs($file->lng) > 180) {
68+
$this->message = "'{$file->lng}' is no valid longitude for file {$file->name}.";
16869

16970
return false;
17071
}
17172

172-
173-
if (!array_key_exists('lat', $combined)) {
174-
$this->message = "Missing latitude for file {$filename}.";
73+
if (is_null($file->lat)) {
74+
$this->message = "Missing latitude for file {$file->name}.";
17575

17676
return false;
17777
}
17878
}
17979

180-
if (array_key_exists('lat', $combined)) {
181-
$lat = $combined['lat'];
182-
if (!is_numeric($lat) || abs($lat) > 90) {
183-
$this->message = "'{$lat}' is no valid latitude for file {$filename}.";
80+
if (!is_null($file->lat)) {
81+
if (!is_numeric($file->lat) || abs($file->lat) > 90) {
82+
$this->message = "'{$file->lat}' is no valid latitude for file {$file->name}.";
18483

18584
return false;
18685
}
18786

188-
if (!array_key_exists('lng', $combined)) {
189-
$this->message = "Missing longitude for file {$filename}.";
87+
if (is_null($file->lng)) {
88+
$this->message = "Missing longitude for file {$file->name}.";
19089

19190
return false;
19291
}
19392
}
19493

19594
// Catch both a malformed date (false) and the zero date (negative integer).
196-
if (array_key_exists('taken_at', $combined)) {
197-
$date = $combined['taken_at'];
198-
if (!(strtotime($date) > 0)) {
199-
$this->message = "'{$date}' is no valid date for file {$filename}.";
95+
if (!is_null($file->takenAt)) {
96+
if (!(strtotime($file->takenAt) > 0)) {
97+
$this->message = "'{$file->takenAt}' is no valid date for file {$file->name}.";
20098

20199
return false;
202100
}
203101
}
204102

205103
foreach (self::NUMERIC_FIELDS as $key => $text) {
206-
if (array_key_exists($key, $combined) && !is_numeric($combined[$key])) {
207-
$this->message = "'{$combined[$key]}' is no valid {$text} for file {$filename}.";
104+
if (!is_null($file->$key) && !is_numeric($file->$key)) {
105+
$this->message = "'{$file->$key}' is no valid {$text} for file {$file->name}.";
208106

209107
return false;
210108
}
211109
}
212110

213-
if (array_key_exists('yaw', $combined)) {
214-
if ($combined['yaw'] < 0 || $combined['yaw'] > 360) {
215-
$this->message = "'{$combined['yaw']}' is no valid yaw for file {$filename}.";
111+
if (!is_null($file->yaw)) {
112+
if ($file->yaw < 0 || $file->yaw > 360) {
113+
$this->message = "'{$file->yaw}' is no valid yaw for file {$file->name}.";
216114

217115
return false;
218116
}

app/Services/MetadataParsing/FileMetadata.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,9 @@ public function __construct(public string $name)
88
{
99
//
1010
}
11+
12+
public function isEmpty(): bool
13+
{
14+
return true;
15+
}
1116
}

app/Services/MetadataParsing/ImageCsvParser.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ public function getMetadata(): VolumeMetadata
7171
return $column;
7272
}, $keys);
7373

74+
// This will remove duplicate columns and retain the "last" one.
7475
$keyMap = array_flip($keys);
76+
7577
$getValue = fn ($row, $key) => $row[$keyMap[$key] ?? null] ?? null;
7678
$maybeCast = fn ($value) => (is_null($value) || $value === '') ? null : floatval($value);
7779

app/Services/MetadataParsing/ImageMetadata.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,18 @@ public function __construct(
1717
{
1818
parent::__construct($name);
1919
}
20+
21+
/**
22+
* Determines if any metadata field other than the name is filled.
23+
*/
24+
public function isEmpty(): bool
25+
{
26+
return is_null($this->lat)
27+
&& is_null($this->lng)
28+
&& is_null($this->takenAt)
29+
&& is_null($this->area)
30+
&& is_null($this->distanceToGround)
31+
&& is_null($this->gpsAltitude)
32+
&& is_null($this->yaw);
33+
}
2034
}

app/Services/MetadataParsing/VolumeMetadata.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,18 @@ public function getFiles(): Collection
2828
{
2929
return $this->files->values();
3030
}
31+
32+
/**
33+
* Determine if there is any file metadata.
34+
*/
35+
public function isEmpty(): bool
36+
{
37+
foreach ($this->files as $file) {
38+
if (!$file->isEmpty()) {
39+
return false;
40+
}
41+
}
42+
43+
return true;
44+
}
3145
}

0 commit comments

Comments
 (0)