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

0.10.2 importer fix #543

Merged
merged 3 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 10 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# Changelog
All notable changes to this project will be documented in this file.

## 0.10.2
### Changed
- Date Range _importFrom_ returns a JSON string
- Added tests to _ApiDataImporterTest_ to check if the same columns can be used in multiple attributes
- Row error now also sends the column value, instead of just sending the column name
- Dropdowns in the _Data Importer_ are now sorted alphabetically
- The ErrorList component of the Data Importer in the Frontend now preserves text inside `{{...}}`

## 0.10.1
### Added
- Option to display attributes in _Data Model Editor_ in groups
Expand All @@ -10,8 +18,8 @@ All notable changes to this project will be documented in this file.
- selects the first choice (if there is **only one choice** in the dropdown)*
- selects the exact match (**case insensitive**; e.g. "apple" + `Tab` will select the available choice "apple", but also "Apple")*
- nothing and focuses the next attribute (default)
- * Selected elements will be marked with a blue (Tab) badge
- Pressing `Delete` inside _Single Choice Dropdowns_ will clear the element
- * Selected elements will be marked with a blue (Tab) badge
- Pressing `Delete` inside _Single Choice Dropdowns_ will clear the element
- Importer now automatically removes BOM if present
- Better readable format for error message on validation
- Renamed _fromImport_ to _parseImport_ on the attribute classses. The base class now by default imports the passed string, removing redundancies on the string-based classes.
Expand Down
8 changes: 4 additions & 4 deletions app/AttributeTypes/DaterangeAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ public static function parseImport(int|float|bool|string $data) : mixed {
throw InvalidDataException::requireBefore($start, $end);
}

return [
"start" => $start,
"end" => $end,
];
return json_encode([
$start,
$end,
]);
}

public static function unserialize(mixed $data) : mixed {
Expand Down
29 changes: 16 additions & 13 deletions app/Import/EntityImporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class EntityImporter {

private $metadata;
private array $attributesMap;
private array $attributeAttributeMap = [];
private array $attributeIdToAttributeValue = [];
private int $entityTypeId;
private string $nameColumn;
private ?string $parentColumn = null;
Expand Down Expand Up @@ -147,7 +147,7 @@ private function verifyEntityType($entityTypeId): bool {
private function verifyAttributeMapping($headers): bool {
$nameErrors = [];
$indexErrors = [];
foreach($this->attributesMap as $attribute => $column) {
foreach($this->attributesMap as $attributeId => $column) {
$column = trim($column);
if($column == "") {
continue;
Expand All @@ -157,27 +157,26 @@ private function verifyAttributeMapping($headers): bool {
array_push($nameErrors, $column);
}

$attr = Attribute::find($attribute);
$attr = Attribute::find($attributeId);
if(!$attr) {
array_push($indexErrors, $attribute);
array_push($indexErrors, $attributeId);
} else {
$this->attributeAttributeMap[$column] = $attr;
$this->attributeIdToAttributeValue[$attributeId] = $attr;
}
}

$valid = true;
if(count($indexErrors) > 0) {
$this->resolver->conflict(__("entity-importer.attribute-id-does-not-exist", ["attributes" => implode(", ", $indexErrors)]));
$valid = false;
}

if(count($nameErrors) > 0) {
$this->resolver->conflict(__("entity-importer.attribute-column-does-not-exist", ["columns" => implode(", ", $nameErrors)]));
$valid = false;
}

if(count($indexErrors) > 0 || count($nameErrors) > 0) {
return false;
} else {
return true;
}
return $valid;
}

private function validateName($row, $rowIndex): bool {
Expand Down Expand Up @@ -233,18 +232,22 @@ private function validateLocation($row, $rowIndex): bool {

private function validateAttributesInRow($row, $index): bool {
$errors = [];
foreach($this->attributeAttributeMap as $column => $attribute) {
foreach($this->attributeIdToAttributeValue as $attributeId => $attribute) {
try {
$column = $this->attributesMap[$attributeId];
$datatype = $attribute->datatype;
$attrClass = AttributeBase::getMatchingClass($datatype);
$attrClass::fromImport($row[$column]);
} catch(Exception $e) {
array_push($errors, $column);
array_push($errors, ["column" => $column, "value" => $row[$column]]);
}
}

if(count($errors) > 0) {
$this->rowConflict($index, "entity-importer.attribute-could-not-be-imported", ["attribute" => implode(", ", $errors)]);
$errorStrings = array_map(function ($error) {
return "{{" . $error['column'] . "}}" . " => " . "{{" . $error['value'] . "}}";
}, $errors);
$this->rowConflict($index, "entity-importer.attribute-could-not-be-imported", ["attributeErrors" => implode(", ", $errorStrings)]);
}
return count($errors) == 0;
}
Expand Down
10 changes: 10 additions & 0 deletions database/seeders/EntityAttributesTableSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,16 @@ public function run()
'position' => 6,
'depends_on' => NULL,
),
23 =>
array (
'id' => 26,
'entity_type_id' => 3, // Fundstelle
'attribute_id' => 19, // Aufbewahrung [string]
'created_at' => '2017-12-20 17:00:43',
'updated_at' => '2017-12-20 17:01:58',
'position' => 3,
'depends_on' => NULL,
),
));
}
}
4 changes: 3 additions & 1 deletion lang/de/entity-importer.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@

<?php

return [
'empty' => 'Keine Zeilen für den Import gefunden',
'file-not-found' => 'Datei konnte nicht gelesen werden.',
'missing-data' => 'Benötigte Spalte fehlt: :column',
'invalid-data' => 'Ungültige Daten: [:column] => :value',
'attribute-could-not-be-imported' => 'Attribut konnte nicht importiert werden: :attributeErrors',
'attribute-id-does-not-exist' => 'Die Attribut-ID existiert nicht: :attributes',
'attribute-column-does-not-exist' => 'Die Attribut-Spalten existieren nicht: :columns',
'name-column-does-not-exist' => 'Die Spalte für den Namen existiert nicht: :column',
'parent-column-does-non-exist' => 'Die Spalte für die Eltern-Entität existiert nicht: :column',
'parent-entity-does-not-exist' => 'Die Eltern-Entität existiert nicht: :entity',
Expand Down
2 changes: 1 addition & 1 deletion lang/en/entity-importer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
'file-not-found' => 'File could not be read.',
'missing-data' => 'Required column is missing: :column',
'invalid-data' => 'Invalid data: [:column] => :value',
'attribute-could-not-be-imported' => 'Attribute could not be imported: :attribute',
'attribute-could-not-be-imported' => 'Attribute could not be imported: :attributeErrors',
'attribute-id-does-not-exist' => 'The attribute id does not exist: :attributes',
'attribute-column-does-not-exist' => 'The attribute columns do not exist: :columns',
'name-column-does-not-exist' => 'The column for the name does not exist: :column',
Expand Down
19 changes: 10 additions & 9 deletions resources/js/components/attribute/Daterange.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

<template>
<date-picker
:id="name"
Expand Down Expand Up @@ -46,6 +45,9 @@
},
value: {
type: Array,
validator: arr => {
return !arr || arr.length === 2;
},
required: true,
},
},
Expand All @@ -56,15 +58,15 @@
disabled,
value,
} = toRefs(props);
// FETCH

// FUNCTIONS
const strToDate = str => {
return new Date(str);
// FETCH
const fixValue = _ => {
return value.value?.map(dt => new Date(dt));
};

const resetFieldState = _ => {
v.resetField({
value: value.value?.map(dt => strToDate(dt)),
value: fixValue(),
});
};
const undirtyField = _ => {
Expand All @@ -78,7 +80,7 @@
return new Date(date.getTime() - (date.getTimezoneOffset()*60*1000));
});
v.handleChange(correctValue);
}
};

// DATA
const {
Expand All @@ -87,7 +89,7 @@
meta,
resetField,
} = useField(`daterange_${name.value}`, yup.array(), {
initialValue: value.value?.map(dt => strToDate(dt)),
initialValue: fixValue(),
});
const state = reactive({

Expand All @@ -99,7 +101,6 @@
resetField,
});


watch(_ => value, (newValue, oldValue) => {
resetFieldState();
});
Expand Down
31 changes: 23 additions & 8 deletions resources/js/components/error/ErrorList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,29 +35,44 @@
required: true
}
},
setup(props){
setup(props) {
const header = computed(_ => {
return props.value.split(props.headerSeparator)[0].trim();
});

const items = computed(_ => {
// Replace text inside double curly braces with placeholders
const preservationRegex = RegExp('{{(.*?)}}', 'g');
const variables = {};
let counter = 1;
const preservationMatches = props.value.replace(preservationRegex, (match, p1 = '') => {
const key = `$${counter++}`;
variables[key] = p1;
return key;
});

const headerParts = props.value.split(props.headerSeparator);
headerParts.shift();
const [header, ...body] = preservationMatches.split(props.headerSeparator);

const parts = headerParts.join(props.separator);
return parts.split(props.separator);
const joinedBody = body.join(props.headerSeparator).trim();
let lines = joinedBody.split(props.separator);
lines = lines.map((line, idx) => {
let result = line;
for(const [key, value] of Object.entries(variables)) {
result = result.replace(key, value);
}
return result;
});
return lines;
});

const hasItems = computed(_ => {
return items.value.length > 0 && items.value[0].trim() !== '';
return items.value.length > 0 && items.value[0] && items.value[0].trim() !== '';
});


return {
hasItems,
header,
items
items,
};
}
};
Expand Down
Loading
Loading