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

fix: sample attribute lat lon regex #2036

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## Unreleased

- Fix #831: Check if longitude and lagitude match the WGS84 format for sample attributes
- Feat #2066: Update file attribute values form layout and add expand button for long values
- Feat #2102: Delete outdated apidocs files
- Feat #1667: Add log entry when minting DOI
Expand Down
2 changes: 2 additions & 0 deletions data/dev/attribute.csv
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ id,attribute_name,definition,model,structured_comment_name,value_syntax,allowed_
347,last_modified,the date the file was last modified,modified,last_modified,date,"",1,"",""
356,Description,"description of the sample (may include many attributes as human readable text, but these should also be added as independent attribute:value pairs).","",description,text,"",1,"",""
376,estimated genome size,"An estimate of the size of the genome of the species being studied, in basepairs (Gb, Mb or Kb)","",est_genome_size,text,"",1,"",""
391,geographic location (latitude),"The geographical latitudinal origin of the sample, the value should be reported in decimal degrees and in WGS84 system","",latitude,number,"","","",""
392,geographic location (longitude),"The geographical longitudinal origin of the sample, the value should be reported in decimal degrees and in WGS84 system","",longitude,number,"","","",""
448,alternative accession-biosample,"","",alt_acc_biosample,"","","","",""
455,keyword,"","",keywords,"","","","",""
472,camera parameters,"","",camera_parameters,text,"",m,"",""
Expand Down
140 changes: 93 additions & 47 deletions protected/controllers/AdminSampleController.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public function accessRules()
{
return array(
array('allow', // admin only
'actions'=>array('admin','delete','index','view','create','update'),
'actions'=>array('admin','delete','index','view','create','update', 'checkAttribute'),
'roles'=>array('admin'),
),
array('allow', 'actions' => array('create1', 'choose'), 'users' => array('@')),
Expand Down Expand Up @@ -193,50 +193,47 @@ public function actionChoose() {
public function actionUpdate($id)
{
$model = $this->loadModel($id);
//$old_code= $model->code;
// Uncomment the following line if AJAX validation is needed
// $this->performAjaxValidation($model);

if (isset($_POST['Sample'])) {
$model->attributes = $_POST['Sample'];
$model->name = $_POST['Sample']['name'];

if (strpos($_POST['Sample']['species_id'], ":") !== false) {
$array = explode(":", $_POST['Sample']['species_id']);
$tax_id = $array[0];
if (!empty($tax_id)) {
$species = $this->findSpeciesRecord($tax_id, $model);
$this->updateSampleAttributes($model);
if (!$model->hasErrors()) {
$this->redirect(array('view', 'id' => $model->id));
}
} else {
$model->addError('error', 'Taxon ID is empty!');
}
} else {
if ($sampleAttribute = Yii::$app->request->post('Sample')) {
$hasErrors = false;
$model->name = $sampleAttribute['name'];

if (!strpos($sampleAttribute['species_id'], ':')) {
$model->addError('error', 'The input format is wrong, should be tax_id:common_name');
$hasErrors = true;
}
}

$species = Species::model()->findByPk($model->species_id);
$array = explode(':', $sampleAttribute['species_id']);
$tax_id = $array[0];

$model->species_id = $species->tax_id . ":";
$has_common_name = false;
if ($species->common_name != null) {
$has_common_name = true;
$model->species_id .= $species->common_name;
}
if (!$tax_id) {
$model->addError('error', 'Taxon ID is empty!');
$hasErrors = true;
}

if (!$this->findSpeciesRecord($tax_id, $model)) {
$model->addError('error', 'The species does not exist');
$hasErrors = true;
}

if (!$hasErrors && $model->save()) {
$this->updateSampleAttributes($model);
}

if ($species->scientific_name != null) {
if ($has_common_name) {
$model->species_id .= ",";
if (!$model->hasErrors()) {
$this->redirect(array('view', 'id' => $model->id));
}
$model->species_id .= $species->scientific_name;
}
$this->render('update', array(
'model' => $model,
'species' => $species,
));

$species = Species::model()->findByPk($model->species_id);

$model->species_id = sprintf('%s: %s', $species->tax_id, $species->common_name ?: '');
$model->species_id .= sprintf('%s%s', $species->common_name ? ', ' : '', $species->scientific_name ?: '');

$this->render('update', array(
'model' => $model,
'species' => $species,
));
}

/**
Expand Down Expand Up @@ -315,6 +312,47 @@ protected function performAjaxValidation($model)
}
}

public function actionCheckAttribute()
{
if (!Yii::app()->request->isAjaxRequest) {
throw new CHttpException(400,'Invalid request. An Error occurred');
}

$errorMessage = [];
$attributesList = $_POST['attr'];

foreach (explode('",', $attributesList) as $attributes) {
$attributes = str_replace('"', '', $attributes);
$attributeData = explode('=', $attributes);
if (count($attributeData) === 2) {
$attributeData[0] = trim($attributeData[0]);
if ($attributeData[0] !== 'longitude' && $attributeData[0] !== 'latitude') {
continue;
}

$errorMessage = $this->checkWrongLatLongAttribute($attributeData[0], $attributeData[1]);
}
}

echo CJSON::encode(['messages' => $errorMessage]);
Yii::app()->end();
}

private function checkWrongLatLongAttribute(string $attribute, string $attributeValue): array
{
$errorMessage = [];
if (preg_match('/^[^0-9]*$/', $attributeValue)) {
$errorMessage[] = sprintf('Attribute value for %s doesn\'t match WGS84 decimal format.
For geographic location (country, sea, region) use another attribute name', $attribute);
} else if ($attribute === 'latitude' && !preg_match('/^[-+]?(?:90(?:\.0+)?|[1-8]?\d(?:\.\d+)?)$/', $attributeValue)) {
$errorMessage[] = sprintf('Attribute value for %s doesn\'t match WGS84 decimal format', $attribute);
} else if ($attribute === 'longitude' && !preg_match('/^[-+]?(?:180(?:\.0+)?|1[0-7]\d(?:\.\d+)?|\d{1,2}(?:\.\d+)?)$/', $attributeValue)) {
$errorMessage[] = sprintf('Attribute value for %s doesn\'t match WGS84 decimal format', $attribute);
}

return $errorMessage;
}

/**
* Upate sample attribute
*
Expand All @@ -332,20 +370,28 @@ private function updateSampleAttributes($model)
$sampleAttribute->sample_id = $model->id;
$attributes = str_replace('"', '', $attributes);
$attributeData = explode('=', $attributes);
if (count($attributeData) == 2) {
if (count($attributeData) === 2) {
// Get attribute model
$attribute = Attributes::model()->findByAttributes(array('structured_comment_name' => trim($attributeData[0])));
if (!$attribute) {
$model->addError('error', 'Attribute name for the input ' . $attributeData[0] . "=" . $attributeData[1] . ' is not valid - please select a valid attribute name!');
} else {
// Let's save the new sample attribute
$sampleAttribute->value = trim($attributeData[1]);
$sampleAttribute->attribute_id = $attribute->id;
if (!$sampleAttribute->save(true)) {
foreach ($sampleAttribute->getErrors() as $errors) {
foreach ($errors as $errorMessage) {
$model->addError('error', $errorMessage);
}

continue;
}

if ($attributeData[0] === 'longitude' || $attributeData[0] === 'latitude') {
if ($this->checkWrongLatLongAttribute($attributeData[0], $attributeData[1])) {
continue;
}
}

// Let's save the new sample attribute
$sampleAttribute->value = trim($attributeData[1]);
$sampleAttribute->attribute_id = $attribute->id;
if (!$sampleAttribute->save()) {
foreach ($sampleAttribute->getErrors() as $errors) {
foreach ($errors as $errorMessage) {
$model->addError('error', $errorMessage);
}
}
}
Expand Down
84 changes: 81 additions & 3 deletions protected/views/adminSample/_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
<p class="note">Fields with <span class="required">*</span> are required.</p>

<?php if ($model->hasErrors()) : ?>
<div class="alert alert-danger">
<div id="sample_error" class="alert alert-danger">
<?php echo $form->errorSummary($model); ?>
</div>
<?php endif; ?>

<div id="ajax-error" class='alert alert-danger' style="display: none;">An error occurred</div>

<div class="form-group">
<?php echo $form->labelEx($model, 'species_id', array('class' => 'control-label')); ?>
<?php
Expand Down Expand Up @@ -65,10 +67,86 @@

<div class="pull-right btns-row">
<a href="/adminSample/admin" class="btn background-btn-o">Cancel</a>
<?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save', array('class' => 'btn background-btn')); ?>
<button id='checkAttribute' type='button' class="btn btn-primary"><?php echo $model->isNewRecord ? 'Create' : 'Save'?></button>

</div>

<div class='modal fade' id='confirmation_sample_modal' role='dialog'>
<div class='modal-dialog modal-lg'>
<div class='modal-content'>
<div class='modal-header'>
<button type='button' class='close' data-dismiss='modal'>&times;</button>
<h4 class='modal-title'>Important</h4>
</div>
<div class='modal-body'>
<div id='check-attribute-warning' class='alert alert-danger' style='display: none;'>
</div>
<div id="check-attribute-confirmation" class="mt-4">
</div>
</div>
<div class='modal-footer'>
<a id="hideModal" class='btn background-btn-o'>Cancel</a>
<?php echo CHtml::submitButton('Confirm', array('id' => 'submit_confirmation', 'class' => 'btn background-btn')); ?>
</div>
</div>

</div>
</div>
<?php $this->endWidget(); ?>
</div>

</div>
</div>
<script>
$(document).ready(function() {
$('#hideModal').click(function(e) {
$('#confirmation_sample_modal').modal('hide');
})

$('#checkAttribute').click(function (e) {
e.preventDefault()

let myWarning = $('#check-attribute-warning')[0];
let myConfirmation = $('#check-attribute-confirmation')[0];
myWarning.innerHTML = '';
myWarning.style.display = 'none'
myConfirmation.innerHTML = '';
$('#submit_confirmation')[0].style.display = ''
$('#ajax-error')[0].style.display = 'none';

$.ajax({
url: "<?php echo Yii::app()->createUrl('adminSample/checkAttribute') ?> ",
type: 'POST',
data: {
attr: $('.form').find("textarea[name='Sample[attributesList]']").val()
},
dataType: 'json',
success: function(response) {
$('#confirmation_sample_modal').modal('show');
let messages = response.messages

if (!messages.length) {
let el = document.createElement('div');
el.textContent= 'Are you sure you want to continue?'
el.className = 'mt-4'
myConfirmation.appendChild(el);

return;
}

myWarning.style.display = 'block'
$('#submit_confirmation')[0].style.display = 'none'

messages.forEach((message) => {
let el = document.createElement('li');
el.textContent = message;

myWarning.appendChild(el);
})
},
error: function(xhr, status, error) {
$('#ajax-error')[0].style.display = 'block';
}
});
});
});
</script>
Loading