/** * Map a virtual field called default_value onto the relevant default value fields, depending on the data type. */ protected function preSubmit() { if (isset($this->submission['fields']['default_value']['value'])) { $attr = ORM::factory('survey_attribute', $this->submission['fields']['survey_attribute_id']['value']); switch ($attr->data_type) { case 'T': $this->submission['fields']['default_text_value']['value'] = $this->submission['fields']['default_value']['value']; break; case 'F': $this->submission['fields']['default_float_value']['value'] = $this->submission['fields']['default_value']['value']; break; case 'I': case 'L': $this->submission['fields']['default_int_value']['value'] = $this->submission['fields']['default_value']['value']; break; case 'D': case 'V': $vagueDate = vague_date::string_to_vague_date($this->submission['fields']['default_value']['value']); $this->submission['fields']['default_date_start_value']['value'] = $vagueDate[0]; $this->submission['fields']['default_date_end_value']['value'] = $vagueDate[1]; $this->submission['fields']['default_date_type_value']['value'] = $vagueDate[2]; } } return parent::presubmit(); }
/** * Validates that a specific date string can be correctly parsed into a vague date. * * @param string SDate */ public static function vague_date($sDate) { if (vague_date::string_to_vague_date($sDate) != false) { return true; } return false; }
public function Index() { $start = ''; $end = ''; $type = ''; $err = ''; $log = ''; if (array_key_exists('date', $_POST)) { $str = $_POST['date']; $log .= "Have string in postdata<br />"; $log .= "Parsing for string " . $str . "<br />"; $arr = vague_date::string_to_vague_date($str); if ($arr != false) { $log .= "Parsed correctly<br />"; $start = $arr['start']; $end = $arr['end']; $type = $arr['type']; } else { $err = 'Unable to parse date'; $log .= $err; } } else { $log .= "No postdata<br />"; } ?> <form method='post'><input name='date' id='date' value=''/><br /><input type='submit' value='Parse' /></form> <?php echo $err; ?> <ul> <li>Start: <?php echo $start; ?> <li> <li>End: <?php echo $end; ?> <li> <li>Type: <?php echo $type; ?> </li> </ul> <?php echo $log; ?> <?php }
/** * Fills the temporary table called occdelta, which contains details of each proposed record to * verify. This is only done when calling verify directly, since occdelta is prepared by the * scheduled_tasks process when auto-checks are being run on the schedule. */ private function prepareOccdelta($db, $sample, $occurrences) { $website_id = $this->website_id; $srid = kohana::config('sref_notations.internal_srid'); $last_sref = ''; $last_sref_system = ''; foreach ($occurrences as $occurrence) { $record = array_merge($sample, $occurrence); $survey_id = $record['sample:survey_id']; $sref = $record['sample:entered_sref']; $sref_system = $record['sample:entered_sref_system']; $taxa_taxon_list_id = $record['occurrence:taxa_taxon_list_id']; if (isset($record['sample:geom'])) { $geom = $record['sample:geom']; } else { // avoid recalculating the geom if we don't have to as this is relatively expensive if ($sref !== $last_sref || $sref_system !== $last_sref_system) { $geom = spatial_ref::sref_to_internal_wkt($sref, $sref_system); } $last_sref = $sref; $last_sref_system = $sref_system; } $date = $record['sample:date']; $vd = vague_date::string_to_vague_date($date); $date_start = $vd[0]; $date_end = $vd[1]; $date_type = $vd[2]; $db->query("insert into occdelta (website_id, survey_id, date_start, date_end, date_type, public_entered_sref, entered_sref_system, public_geom, taxa_taxon_list_id)\n values ({$website_id}, {$survey_id}, '{$date_start}', '{$date_end}', '{$date_type}', '{$sref}', '{$sref_system}', st_geomfromtext('{$geom}', {$srid}), {$taxa_taxon_list_id});"); } // patch in some extra details about the taxon required for each cache entry $db->query("update occdelta o set taxon_meaning_id=ttl.taxon_meaning_id, taxon=ttl.taxon, taxa_taxon_list_external_key=ttl.external_key " . "from list_taxa_taxon_lists ttl where ttl.id=o.taxa_taxon_list_id"); }
protected function createAttributeRecord($attrId, $valueId, $value, $attrDef) { // There are particular circumstances when $value is actually an array: when a attribute is multi value, // AND has yet to be created, AND is passed in as multiple ***Attr:<n>[] POST variables. This should only happen when // the attribute has yet to be created, as after this point the $valueID is filled in and that specific attribute POST variable // is no longer multivalue - only one value is stored per attribute value record, though more than one record may exist // for a given attribute. There may be others with th same <n> without a $valueID. // If attrId = fk_* (e.g. when importing data) then the value is a term whose id needs to be looked up. if (is_array($value)) { if (is_null($valueId)) { $retVal = true; foreach ($value as $singlevalue) { // recurse over array. $retVal = $this->createAttributeRecord($attrId, $valueId, $singlevalue, $attrDef) && $retVal; } return $retVal; } else { $this->errors['general'] = 'INTERNAL ERROR: multiple values passed in for ' . $this->object_name . ' ' . $valueId . ' ' . print_r($value, true); return false; } } $fk = false; if (substr($attrId, 0, 3) == 'fk_') { // value is a term that needs looking up $fk = true; $attrId = substr($attrId, 3); $value = trim($value); } // Create a attribute value, loading the existing value id if it exists, or search for the existing record // if not multivalue but no id supplied and not a new record // @todo: Optimise attribute saving by using query builder rather than ORM if ($this->existing && !is_null($valueId) && !$attrDef->multi_value == 'f') { $attrValueModel = ORM::factory($this->object_name . '_attribute_value')->where(array($this->object_name . '_attribute_id' => $attrId, $this->object_name . '_id' => $this->id))->find(); } if (!isset($attrValueModel) || !$attrValueModel->loaded) { $attrValueModel = ORM::factory($this->object_name . '_attribute_value', $valueId); } $oldValues = array_merge($attrValueModel->as_array()); $dataType = $attrDef->data_type; $vf = null; $fieldPrefix = array_key_exists('field_prefix', $this->submission) ? $this->submission['field_prefix'] . ':' : ''; // For attribute value errors, we need to report e.g smpAttr:attrId[:attrValId] as the error key name, not // the table and field name as normal. $fieldId = $fieldPrefix . $this->attrs_field_prefix . ':' . $attrId; if ($attrValueModel->id) { $fieldId .= ':' . $attrValueModel->id; } switch ($dataType) { case 'T': $vf = 'text_value'; break; case 'F': $vf = 'float_value'; break; case 'D': case 'V': // Date if (!empty($value)) { $vd = vague_date::string_to_vague_date($value); if ($vd) { $attrValueModel->date_start_value = $vd[0]; $attrValueModel->date_end_value = $vd[1]; $attrValueModel->date_type_value = $vd[2]; kohana::log('debug', "Accepted value {$value} for attribute {$fieldId}"); kohana::log('debug', " date_start_value=" . $attrValueModel->date_start_value); kohana::log('debug', " date_end_value=" . $attrValueModel->date_end_value); kohana::log('debug', " date_type_value=" . $attrValueModel->date_type_value); } else { $this->errors[$fieldId] = "Invalid value {$value} for attribute " . $attrDef->caption; kohana::log('debug', "Could not accept value {$value} into date fields for attribute {$fieldId}."); return false; } } else { $attrValueModel->date_start_value = null; $attrValueModel->date_end_value = null; $attrValueModel->date_type_value = null; } break; case 'G': $vf = 'geom_value'; break; case 'B': // Boolean $vf = 'int_value'; if (!empty($value)) { $lower = strtolower($value); if ($lower == 'false' || $lower == 'f' || $lower == 'no' || $lower == 'n' || $lower == 'off') { $value = 0; } elseif ($lower == 'true' || $lower == 't' || $lower == 'yes' || $lower == 'y' || $lower == 'on') { $value = 1; } } break; case 'L': // Lookup list $vf = 'int_value'; if (!empty($value) && $fk) { // value must be looked up $r = $this->fkLookup(array('fkTable' => 'lookup_term', 'fkSearchField' => 'term', 'fkSearchValue' => $value, 'fkSearchFilterField' => 'termlist_id', 'fkSearchFilterValue' => $attrDef->termlist_id)); if ($r) { $value = $r; } else { $this->errors[$fieldId] = "Invalid value {$value} for attribute " . $attrDef->caption; kohana::log('debug', "Could not accept value {$value} into field {$vf} for attribute {$fieldId}."); return false; } } break; default: // Integer $vf = 'int_value'; break; } if ($vf != null) { $attrValueModel->{$vf} = $value; // Test that ORM accepted the new value - it will reject if the wrong data type for example. // Use a string compare to get a proper test but with type tolerance. // A wkt geometry gets translated to a proper geom so this will look different - just check it is not empty. // A float may loose precision or trailing 0 - just check for small percentage difference if (strcmp($attrValueModel->{$vf}, $value) === 0 || $dataType === 'G' && !empty($attrValueModel->{$vf})) { kohana::log('debug', "Accepted value {$value} into field {$vf} for attribute {$fieldId}."); } else { if ($dataType === 'F' && abs($attrValueModel->{$vf} - $value) < 1.0E-5 * $attrValueModel->{$vf}) { kohana::log('alert', "Lost precision accepting value {$value} into field {$vf} for attribute {$fieldId}. Value=" . $attrValueModel->{$vf}); } else { $this->errors[$fieldId] = "Invalid value {$value} for attribute " . $attrDef->caption; kohana::log('debug', "Could not accept value {$value} into field {$vf} for attribute {$fieldId}."); return false; } } } // set metadata $exactMatches = array_intersect_assoc($oldValues, $attrValueModel->as_array()); // which fields do we have in the submission? $fieldsWithValuesInSubmission = array_intersect_key($oldValues, $attrValueModel->as_array()); // Hook to the owning entity (the sample, location, taxa_taxon_list or occurrence) $thisFk = $this->object_name . '_id'; $attrValueModel->{$thisFk} = $this->id; // and hook to the attribute $attrFk = $this->object_name . '_attribute_id'; $attrValueModel->{$attrFk} = $attrId; // we'll update metadata only if at least one of the fields have changed $wantToUpdateAttrMetadata = count($exactMatches) !== count($fieldsWithValuesInSubmission); if (!$wantToUpdateAttrMetadata) { $attrValueModel->wantToUpdateMetadata = false; } try { $v = $attrValueModel->validate(new Validation($attrValueModel->as_array()), true); } catch (Exception $e) { $v = false; $this->errors[$fieldId] = $e->getMessage(); error::log_error('Exception during validation', $e); } if (!$v) { foreach ($attrValueModel->errors as $key => $value) { // concatenate the errors if more than one per field. $this->errors[$fieldId] = array_key_exists($fieldId, $this->errors) ? $this->errors[$fieldId] . ' ' . $value : $value; } return false; } $attrValueModel->save(); if ($wantToUpdateAttrMetadata && !$this->wantToUpdateMetadata) { // we didn't update the parent's metadata. But a custom attribute value has changed, so it makes sense to update it now. $this->wantToUpdateMetadata = true; $this->set_metadata(); $this->validate(new Validation($this->as_array()), true); } $this->nestedChildModelIds[] = $attrValueModel->get_submitted_ids(); return true; }
public function testConvertYearFrom_To_VagueDate() { $vd = vague_date::string_to_vague_date('after 2001'); $this->assertEquals('2001-01-01', $vd[0]); $this->assertEquals(null, $vd[1]); $this->assertEquals('Y-', $vd[2]); $vd = vague_date::string_to_vague_date('2001-'); $this->assertEquals('2001-01-01', $vd[0]); $this->assertEquals(null, $vd[1]); $this->assertEquals('Y-', $vd[2]); }
private function getFilterClause($field, $datatype, &$operator, &$value) { if ($datatype == 'text') { // quote text values and replace * wildcards with SQL friendly ones. $value = str_replace('*', '%', $value); //by default add a wildcard to the end if (substr($value, -1) !== '%') { $value .= '%'; } $operator = 'ILIKE'; } else { $operator = '='; } if ($datatype !== 'text' && $datatype !== 'date') { //strip spaces so the user can be more flexible about spaces they enter //Note: Don't use this for text filter as spacing might be important for the search. //For dates, spacing will be handled by the vague date engine $value = str_replace(" ", "", $value); //apart from text and date values we handle the case where the user enters a range e.g. 1-3 // @todo: is there an i18n consideration here with the .? if (preg_match('/(?P<from>\\d+(\\.\\d+)?)(-|to)(?<to>\\d+(\\.\\d+)?)/', $value, $matches)) { return "{$field} BETWEEN " . $matches['from'] . " AND " . $matches['to']; } // support <, <=, >, >= operators if (preg_match('/(?P<op>(>|<|>=|<=))(?P<val>\\d+(\\.\\d+)?)/', $value, $matches)) { return "{$field} " . $matches['op'] . " " . $matches['val']; } } if ($datatype == 'text') { // ensure value is escaped for apostrophes $value = pg_escape_string($value); // quote text and date values $value = "'" . $value . "'"; } if ($datatype != 'date') { return "{$field} {$operator} {$value}"; } else { $vaguedate = vague_date::string_to_vague_date($value); return "({$field} >= '" . $vaguedate[0] . "' AND {$field} <= '" . $vaguedate[1] . "')"; } }
/** * Before submission, map vague dates to their underlying database fields. */ protected function preSubmit() { if (array_key_exists('date', $this->submission['fields'])) { $vague_date = vague_date::string_to_vague_date($this->submission['fields']['date']['value']); $this->submission['fields']['date_start']['value'] = $vague_date[0]; $this->submission['fields']['date_end']['value'] = $vague_date[1]; $this->submission['fields']['date_type']['value'] = $vague_date[2]; } // Allow a sample to be submitted with a spatial ref and system but no Geom. If so we // can work out the Geom if (array_key_exists('entered_sref', $this->submission['fields']) && array_key_exists('entered_sref_system', $this->submission['fields']) && !(array_key_exists('geom', $this->submission['fields']) && $this->submission['fields']['geom']['value']) && $this->submission['fields']['entered_sref']['value'] && $this->submission['fields']['entered_sref_system']['value']) { try { $this->submission['fields']['geom']['value'] = spatial_ref::sref_to_internal_wkt($this->submission['fields']['entered_sref']['value'], $this->submission['fields']['entered_sref_system']['value']); } catch (Exception $e) { $this->errors['entered_sref'] = $e->getMessage(); } } return parent::presubmit(); }