2019-03-08 16:19:07 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
App::uses('AppModel', 'Model');
|
|
|
|
|
|
|
|
class DecayingModel extends AppModel
|
|
|
|
{
|
|
|
|
public $actsAs = array('Containable');
|
|
|
|
|
|
|
|
public $hasMany = array(
|
2019-04-03 10:05:45 +02:00
|
|
|
'DecayingModelMapping' => array(
|
|
|
|
'className' => 'DecayingModelMapping',
|
|
|
|
'foreignKey' => 'model_id',
|
|
|
|
'dependent' => true
|
|
|
|
)
|
2019-03-08 16:19:07 +01:00
|
|
|
);
|
|
|
|
|
2019-07-25 15:45:33 +02:00
|
|
|
private $__registered_model_classes = array();
|
|
|
|
|
2019-03-08 16:19:07 +01:00
|
|
|
public function afterFind($results, $primary = false) {
|
|
|
|
foreach ($results as $k => $v) {
|
2019-08-16 15:10:59 +02:00
|
|
|
$results[$k]['DecayingModel']['isDefault'] = $this->isDefaultModel($v);
|
2019-03-08 16:19:07 +01:00
|
|
|
if (!empty($v['DecayingModel']['parameters'])) {
|
|
|
|
$decoded = json_decode($v['DecayingModel']['parameters'], true);
|
|
|
|
if ($decoded === null) {
|
|
|
|
$decoded = array();
|
|
|
|
}
|
|
|
|
$results[$k]['DecayingModel']['parameters'] = $decoded;
|
|
|
|
}
|
2019-04-03 11:51:29 +02:00
|
|
|
if (!empty($v['DecayingModel']['attribute_types'])) {
|
|
|
|
$decoded = json_decode($v['DecayingModel']['attribute_types'], true);
|
|
|
|
if ($decoded === null) {
|
|
|
|
$decoded = array();
|
|
|
|
}
|
|
|
|
$results[$k]['DecayingModel']['attribute_types'] = $decoded;
|
2019-04-04 09:37:51 +02:00
|
|
|
} else {
|
|
|
|
$results[$k]['DecayingModel']['attribute_types'] = array();
|
2019-04-03 11:51:29 +02:00
|
|
|
}
|
|
|
|
if (!empty($v['DecayingModel']['ref'])) {
|
|
|
|
$decoded = json_decode($v['DecayingModel']['ref'], true);
|
|
|
|
if ($decoded === null) {
|
|
|
|
$decoded = array();
|
|
|
|
}
|
|
|
|
$results[$k]['DecayingModel']['ref'] = $decoded;
|
|
|
|
}
|
2019-03-08 16:19:07 +01:00
|
|
|
}
|
|
|
|
return $results;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function beforeValidate($options = array()) {
|
2019-04-03 10:05:45 +02:00
|
|
|
parent::beforeValidate();
|
2019-08-16 10:33:58 +02:00
|
|
|
|
|
|
|
if (
|
|
|
|
isset($this->data['DecayingModel']['parameters']) &&
|
|
|
|
!empty($this->data['DecayingModel']['parameters']) &&
|
|
|
|
!is_array($this->data['DecayingModel']['parameters'])
|
|
|
|
) {
|
2019-03-08 16:19:07 +01:00
|
|
|
$encoded = json_decode($this->data['DecayingModel']['parameters'], true);
|
|
|
|
if ($encoded !== null) {
|
2019-07-09 09:49:08 +02:00
|
|
|
$validation = $this->__validateParameters($encoded);
|
|
|
|
if ($validation !== false) {
|
|
|
|
$this->data['DecayingModel']['parameters'] = json_encode($encoded);
|
|
|
|
return true;
|
|
|
|
}
|
2019-03-08 16:19:07 +01:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2019-04-03 11:51:29 +02:00
|
|
|
if (!empty($this->data['DecayingModel']['attribute_types']) && !is_array($this->data['DecayingModel']['attribute_types'])) {
|
|
|
|
$encoded = json_decode($this->data['DecayingModel']['attribute_types'], true);
|
|
|
|
if ($encoded !== null) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2019-03-08 16:19:07 +01:00
|
|
|
}
|
|
|
|
|
2019-04-03 10:05:45 +02:00
|
|
|
public function beforeSave($options = array()) {
|
|
|
|
if (isset($this->data['DecayingModel']['parameters']) && is_array($this->data['DecayingModel']['parameters'])) {
|
|
|
|
$this->data['DecayingModel']['parameters'] = json_encode($this->data['DecayingModel']['parameters']);
|
|
|
|
}
|
2019-07-09 08:49:49 +02:00
|
|
|
if (isset($this->data['DecayingModel']['parameters']['base_score_config']) && is_array($this->data['DecayingModel']['parameters']['base_score_config'])) {
|
|
|
|
$this->data['DecayingModel']['parameters']['base_score_config'] = json_encode($this->data['DecayingModel']['parameters']['base_score_config']);
|
|
|
|
}
|
2019-08-16 15:42:01 +02:00
|
|
|
if (isset($this->data['DecayingModel']['parameters']['settings']) && is_array($this->data['DecayingModel']['parameters']['settings'])) {
|
|
|
|
$this->data['DecayingModel']['parameters']['settings'] = json_encode($this->data['DecayingModel']['parameters']['settings']);
|
|
|
|
}
|
2019-04-03 11:51:29 +02:00
|
|
|
if (isset($this->data['DecayingModel']['attribute_types']) && is_array($this->data['DecayingModel']['attribute_types'])) {
|
|
|
|
$this->data['DecayingModel']['attribute_types'] = json_encode($this->data['DecayingModel']['attribute_types']);
|
|
|
|
}
|
|
|
|
if (isset($this->data['DecayingModel']['ref']) && is_array($this->data['DecayingModel']['ref'])) {
|
|
|
|
$this->data['DecayingModel']['ref'] = json_encode($this->data['DecayingModel']['ref']);
|
|
|
|
}
|
2019-04-03 10:05:45 +02:00
|
|
|
if (!isset($this->data['DecayingModel']['org_id'])) {
|
|
|
|
$this->data['DecayingModel']['org_id'] = Configure::read('MISP.host_org_id');
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-07-09 09:49:08 +02:00
|
|
|
/*
|
|
|
|
* may be done at some point but we still want to be generic
|
|
|
|
* so enforcing hardcoded tests here may not be the best solution
|
|
|
|
* For now, limit the number of digits for the parameters
|
|
|
|
*/
|
|
|
|
private function __validateParameters(&$parameters)
|
2019-07-09 08:49:49 +02:00
|
|
|
{
|
2019-07-09 09:49:08 +02:00
|
|
|
foreach ($parameters as $name => $value) {
|
|
|
|
if (is_array($value)) {
|
|
|
|
$this->__validateParameters($parameters[$name]);
|
|
|
|
} else if (is_numeric($value)) {
|
|
|
|
$parameters[$name] = round($value, 4);
|
2019-08-13 14:21:58 +02:00
|
|
|
} else if (!empty($value)) {
|
|
|
|
$parameters[$name] = $value;
|
2019-07-18 15:08:13 +02:00
|
|
|
} else {
|
|
|
|
$parameters[$name] = 0;
|
2019-07-09 09:49:08 +02:00
|
|
|
}
|
|
|
|
}
|
2019-07-09 08:49:49 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-04-03 10:05:45 +02:00
|
|
|
private function __load_models($force = false)
|
|
|
|
{
|
|
|
|
$dir = new Folder(APP . 'files' . DS . 'misp-decaying-models' . DS . 'models');
|
|
|
|
$files = $dir->find('.*\.json');
|
|
|
|
$models = array();
|
|
|
|
foreach ($files as $file) {
|
|
|
|
$file = new File($dir->pwd() . DS . $file);
|
|
|
|
$models[] = json_decode($file->read(), true);
|
|
|
|
$file->close();
|
|
|
|
}
|
|
|
|
return $models;
|
|
|
|
}
|
|
|
|
|
2019-04-03 11:51:29 +02:00
|
|
|
public function update($force=false)
|
2019-04-03 10:05:45 +02:00
|
|
|
{
|
|
|
|
$new_models = $this->__load_models($force);
|
|
|
|
$temp = $this->find('all', array(
|
|
|
|
'recursive' => -1
|
|
|
|
));
|
|
|
|
$existing_models = array();
|
|
|
|
foreach ($temp as $k => $model) {
|
|
|
|
$existing_models[$model['DecayingModel']['uuid']] = $model['DecayingModel'];
|
|
|
|
}
|
|
|
|
foreach ($new_models as $k => $new_model) {
|
|
|
|
if (isset($existing_models[$new_model['uuid']])) {
|
|
|
|
$existing_model = $existing_models[$new_model['uuid']];
|
|
|
|
if ($force || $new_model['version'] > $existing_model['version']) {
|
|
|
|
$new_model['id'] = $existing_model['id'];
|
2019-04-04 11:17:11 +02:00
|
|
|
$new_model['model_id'] = $existing_model['id'];
|
2019-04-03 10:05:45 +02:00
|
|
|
$this->save($new_model);
|
2019-04-04 11:17:11 +02:00
|
|
|
$this->DecayingModelMapping->resetMappingForModel($new_model);
|
2019-04-03 10:05:45 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$this->create();
|
|
|
|
$this->save($new_model);
|
2019-04-04 09:37:51 +02:00
|
|
|
$new_model['id'] = $this->Model->id;
|
2019-04-04 11:17:11 +02:00
|
|
|
$new_model['model_id'] = $this->Model->id;
|
|
|
|
$this->DecayingModelMapping->resetMappingForModel($new_model);
|
2019-04-03 10:05:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-16 10:33:58 +02:00
|
|
|
public function isDefaultModel($decaying_model)
|
|
|
|
{
|
|
|
|
return !is_null($decaying_model['DecayingModel']['uuid']);
|
|
|
|
}
|
|
|
|
|
2019-08-16 11:04:47 +02:00
|
|
|
public function fetchAllowedModels($user, $full=true)
|
2019-07-25 15:45:33 +02:00
|
|
|
{
|
2019-03-08 16:19:07 +01:00
|
|
|
$conditions = array();
|
|
|
|
if (!$user['Role']['perm_site_admin']) {
|
2019-08-16 11:04:47 +02:00
|
|
|
$conditions['OR'] = array(
|
|
|
|
'org_id' => $user['Organisation']['id'],
|
|
|
|
'all_orgs' => 1
|
|
|
|
);
|
2019-03-08 16:19:07 +01:00
|
|
|
}
|
2019-07-23 11:07:38 +02:00
|
|
|
$decayingModels = $this->find('all', array(
|
2019-03-08 16:19:07 +01:00
|
|
|
'conditions' => $conditions,
|
2019-08-16 11:04:47 +02:00
|
|
|
'include' => $full ? 'DecayingModelMapping' :''
|
2019-03-08 16:19:07 +01:00
|
|
|
));
|
2019-08-16 15:10:59 +02:00
|
|
|
foreach ($decayingModels as $i => $decayingModel) { // includes both model default mapping and user mappings
|
|
|
|
if ($full) {
|
2019-08-16 11:04:47 +02:00
|
|
|
$decayingModels[$i]['DecayingModel']['attribute_types'] = $decayingModels[$i]['DecayingModel']['attribute_types'] + Hash::extract($decayingModels[$i]['DecayingModelMapping'], '{n}.attribute_type');
|
|
|
|
unset($decayingModels[$i]['DecayingModelMapping']);
|
|
|
|
}
|
2019-07-23 11:07:38 +02:00
|
|
|
}
|
2019-03-08 16:19:07 +01:00
|
|
|
|
2019-07-23 11:07:38 +02:00
|
|
|
return $decayingModels;
|
2019-03-08 16:19:07 +01:00
|
|
|
}
|
|
|
|
|
2019-08-16 10:52:00 +02:00
|
|
|
// Method that fetches decayingModel
|
|
|
|
// very flexible, it's basically a replacement for find, with the addition that it restricts access based on user
|
|
|
|
// - full attach Attribute types associated to the requested model
|
|
|
|
public function fetchModel($user, $id, $full=true, $conditions = array())
|
2019-07-25 15:45:33 +02:00
|
|
|
{
|
2019-08-16 10:52:00 +02:00
|
|
|
$conditions['id'] = $id;
|
2019-07-25 15:45:33 +02:00
|
|
|
$searchOptions = array(
|
|
|
|
'conditions' => $conditions,
|
|
|
|
);
|
|
|
|
if (!$full) {
|
|
|
|
$searchOptions['recursive'] = -1;
|
|
|
|
}
|
|
|
|
$decayingModel = $this->find('first', $searchOptions);
|
2019-03-08 16:19:07 +01:00
|
|
|
|
|
|
|
// if not found return false
|
|
|
|
if (empty($decayingModel)) {
|
2019-08-16 10:33:58 +02:00
|
|
|
throw new MethodNotAllowedException(__('No Decaying Model with the provided ID exists, or you are not authorised to view it.'));
|
2019-03-08 16:19:07 +01:00
|
|
|
}
|
2019-08-16 10:52:00 +02:00
|
|
|
if (
|
|
|
|
!$user['Role']['perm_site_admin'] && // if the user is a site admin, return the model without question
|
2019-08-16 15:42:01 +02:00
|
|
|
!($user['Organisation']['id'] == $decayingModel['DecayingModel']['org_id'] || $decayingModel['DecayingModel']['all_orgs'])
|
2019-08-16 10:52:00 +02:00
|
|
|
) {
|
|
|
|
throw new MethodNotAllowedException(__('No Decaying Model with the provided ID exists, or you are not authorised to view it.'));
|
|
|
|
}
|
2019-03-08 16:19:07 +01:00
|
|
|
|
2019-04-04 09:37:51 +02:00
|
|
|
if ($full) {
|
|
|
|
$decayingModel['DecayingModel']['attribute_types'] = $this->DecayingModelMapping->getAssociatedTypes($user, $decayingModel['DecayingModel']['id']);
|
|
|
|
}
|
2019-08-16 10:52:00 +02:00
|
|
|
return $decayingModel;
|
2019-03-08 16:19:07 +01:00
|
|
|
}
|
|
|
|
|
2019-06-25 11:46:23 +02:00
|
|
|
// filter out taxonomies and entries not having a numerical value
|
|
|
|
public function listTaxonomiesWithNumericalValue()
|
|
|
|
{
|
|
|
|
$this->Taxonomy = ClassRegistry::init('Taxonomy');
|
2019-06-25 16:57:44 +02:00
|
|
|
$this->Tag = ClassRegistry::init('Tag');
|
2019-06-25 11:46:23 +02:00
|
|
|
$taxonomies = $this->Taxonomy->listTaxonomies(array('full' => true, 'enabled' => true));
|
|
|
|
$start_count = count($taxonomies);
|
|
|
|
foreach ($taxonomies as $namespace => $taxonomy) {
|
|
|
|
if(!empty($taxonomy['TaxonomyPredicate'])) {
|
2019-06-25 16:57:44 +02:00
|
|
|
$tags = $this->Tag->getTagsForNamespace($taxonomy['namespace'], false);
|
2019-06-25 11:46:23 +02:00
|
|
|
foreach($taxonomy['TaxonomyPredicate'] as $p => $predicate) {
|
|
|
|
if(!empty($predicate['TaxonomyEntry'])) {
|
|
|
|
foreach ($predicate['TaxonomyEntry'] as $e => $entry) {
|
|
|
|
if (!is_numeric($entry['numerical_value'])) {
|
|
|
|
unset($taxonomies[$namespace]['TaxonomyPredicate'][$p]['TaxonomyEntry'][$e]);
|
2019-06-25 16:57:44 +02:00
|
|
|
} else {
|
|
|
|
$tag_name = sprintf('%s:%s="%s"', $taxonomy['namespace'], $predicate['value'], $entry['value']);
|
|
|
|
$taxonomies[$namespace]['TaxonomyPredicate'][$p]['TaxonomyEntry'][$e]['Tag'] = $tags[strtoupper($tag_name)]['Tag'];
|
|
|
|
$taxonomies[$namespace]['TaxonomyPredicate'][$p]['TaxonomyEntry'][$e]['Tag']['numerical_value'] = $entry['numerical_value'];
|
2019-06-25 11:46:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (empty($taxonomies[$namespace]['TaxonomyPredicate'][$p]['TaxonomyEntry'])) {
|
|
|
|
unset($taxonomies[$namespace]['TaxonomyPredicate'][$p]);
|
2019-06-25 16:57:44 +02:00
|
|
|
} else {
|
|
|
|
$taxonomies[$namespace]['TaxonomyPredicate'][$p]['TaxonomyEntry'] = array_values($taxonomies[$namespace]['TaxonomyPredicate'][$p]['TaxonomyEntry']);
|
2019-06-25 11:46:23 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
unset($taxonomies[$namespace]['TaxonomyPredicate'][$p]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (empty($taxonomies[$namespace]['TaxonomyPredicate'])) {
|
|
|
|
unset($taxonomies[$namespace]);
|
2019-06-25 16:57:44 +02:00
|
|
|
} else {
|
|
|
|
$taxonomies[$namespace]['TaxonomyPredicate'] = array_values($taxonomies[$namespace]['TaxonomyPredicate']);
|
2019-06-25 11:46:23 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
unset($taxonomies[$namespace]);
|
|
|
|
}
|
|
|
|
}
|
2019-06-25 16:57:44 +02:00
|
|
|
|
2019-06-25 11:46:23 +02:00
|
|
|
return array(
|
|
|
|
'taxonomies' => $taxonomies,
|
|
|
|
'not_having_numerical_value' => $start_count - count($taxonomies)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-08-13 14:21:58 +02:00
|
|
|
private function __include_formula_file_and_return_instance($filename='Polynomial.php')
|
2019-07-17 16:14:24 +02:00
|
|
|
{
|
2019-08-14 16:33:49 +02:00
|
|
|
// FIXME: fetch everything, check if it exists, use the value from the list
|
2019-08-13 14:21:58 +02:00
|
|
|
$filename_no_extension = str_replace('.php', '', $filename);
|
2019-08-13 15:54:03 +02:00
|
|
|
$filename = preg_replace('/[^a-zA-Z0-9_]+/', '-', $filename_no_extension) . '.php'; // sanitization & TO BE REVIEWED
|
2019-07-25 09:28:30 +02:00
|
|
|
$full_path = APP . 'Model/DecayingModelsFormulas/' . $filename;
|
2019-08-13 14:21:58 +02:00
|
|
|
$expected_classname = $filename_no_extension;
|
2019-07-25 09:28:30 +02:00
|
|
|
if (is_file($full_path)) {
|
2019-08-16 10:52:00 +02:00
|
|
|
include_once $full_path;
|
2019-07-25 09:28:30 +02:00
|
|
|
$model_class = ClassRegistry::init($expected_classname);
|
2019-08-13 14:21:58 +02:00
|
|
|
if ($model_class->checkLoading() === 'BONFIRE LIT') {
|
2019-07-25 09:28:30 +02:00
|
|
|
return $model_class;
|
2019-07-18 09:58:11 +02:00
|
|
|
}
|
2019-07-17 16:14:24 +02:00
|
|
|
}
|
2019-07-25 09:28:30 +02:00
|
|
|
return false;
|
2019-07-16 09:31:49 +02:00
|
|
|
}
|
|
|
|
|
2019-08-13 14:21:58 +02:00
|
|
|
public function listAvailableFormulas()
|
|
|
|
{
|
2019-08-14 16:33:49 +02:00
|
|
|
// FIXME: Use cakephp function
|
2019-08-13 14:21:58 +02:00
|
|
|
$path = APP . 'Model/DecayingModelsFormulas/';
|
|
|
|
$formula_files = array_diff(scandir($path), array('..', '.', 'Base.php'));
|
|
|
|
$available_formulas = array();
|
|
|
|
foreach ($formula_files as $formula_file) {
|
|
|
|
$model_class = $this->__include_formula_file_and_return_instance($formula_file);
|
|
|
|
if ($model_class === false) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-08-13 15:27:11 +02:00
|
|
|
$available_formulas[get_class($model_class)] = get_parent_class($model_class) == 'Polynomial' || get_class($model_class) == 'Polynomial' ? 'Polynomial' : get_class($model_class);
|
2019-08-13 14:21:58 +02:00
|
|
|
}
|
|
|
|
return $available_formulas;
|
|
|
|
}
|
|
|
|
|
2019-07-25 09:28:30 +02:00
|
|
|
public function getModelClass($model)
|
2019-07-16 09:31:49 +02:00
|
|
|
{
|
2019-08-13 09:38:45 +02:00
|
|
|
$formula_name = $model['DecayingModel']['formula'] === '' ? 'polynomial' : $model['DecayingModel']['formula'];
|
2019-07-25 09:28:30 +02:00
|
|
|
$expected_filename = Inflector::humanize($formula_name) . '.php';
|
2019-07-25 15:45:33 +02:00
|
|
|
if (!isset($this->__registered_model_classes[$formula_name])) {
|
|
|
|
$model_class = $this->__include_formula_file_and_return_instance($expected_filename);
|
|
|
|
if ($model_class === false) {
|
|
|
|
throw new NotFoundException(sprintf(__('The class for `%s` was not found or not loaded correctly'), $formula_name));
|
|
|
|
}
|
|
|
|
$this->__registered_model_classes[$formula_name] = $model_class;
|
2019-07-16 09:31:49 +02:00
|
|
|
}
|
2019-07-25 15:45:33 +02:00
|
|
|
return $this->__registered_model_classes[$formula_name];
|
2019-07-16 09:31:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// returns timestamp set to the rounded hour
|
|
|
|
public function round_timestamp_to_hour($time)
|
|
|
|
{
|
|
|
|
$offset = $time % 3600;
|
|
|
|
return $time - $offset;
|
|
|
|
}
|
|
|
|
|
2019-08-14 10:48:13 +02:00
|
|
|
public function getScoreOvertime($user, $model_id, $attribute_id, $model_overrides)
|
2019-07-16 09:31:49 +02:00
|
|
|
{
|
|
|
|
$this->Attribute = ClassRegistry::init('Attribute');
|
|
|
|
$attribute = $this->Attribute->fetchAttributesSimple($user, array(
|
2019-07-17 16:14:24 +02:00
|
|
|
'conditions' => array('id' => $attribute_id),
|
|
|
|
'contain' => array('AttributeTag' => array('Tag'))
|
2019-07-16 09:31:49 +02:00
|
|
|
));
|
|
|
|
if (empty($attribute)) {
|
|
|
|
throw new NotFoundException(__('Attribute not found'));
|
|
|
|
} else {
|
|
|
|
$attribute = $attribute[0];
|
2019-07-17 16:14:24 +02:00
|
|
|
$tagConditions = array('EventTag.event_id' => $attribute['Attribute']['event_id']);
|
|
|
|
$temp = $this->Attribute->Event->EventTag->find('all', array(
|
|
|
|
'recursive' => -1,
|
|
|
|
'contain' => array('Tag'),
|
|
|
|
'conditions' => $tagConditions
|
|
|
|
));
|
|
|
|
foreach ($temp as $tag) {
|
|
|
|
$tag['EventTag']['Tag'] = $tag['Tag'];
|
|
|
|
unset($tag['Tag']);
|
2019-07-25 15:45:33 +02:00
|
|
|
$attribute['Attribute']['EventTag'][] = $tag['EventTag'];
|
2019-07-17 16:14:24 +02:00
|
|
|
}
|
2019-07-25 15:45:33 +02:00
|
|
|
$attribute['Attribute']['AttributeTag'] = $attribute['AttributeTag'];
|
|
|
|
unset($attribute['AttributeTag']);
|
2019-07-16 09:31:49 +02:00
|
|
|
}
|
2019-08-16 10:52:00 +02:00
|
|
|
$model = $this->fetchModel($user, $model_id, true);
|
2019-07-16 09:31:49 +02:00
|
|
|
if ($model === false) {
|
|
|
|
throw new NotFoundException(__('Model not found'));
|
|
|
|
}
|
2019-08-14 10:48:13 +02:00
|
|
|
if (!empty($model_overrides)) {
|
|
|
|
$this->overrideModelParameters($model, $model_overrides);
|
|
|
|
}
|
2019-07-25 09:28:30 +02:00
|
|
|
$this->Computation = $this->getModelClass($model);
|
2019-07-16 09:31:49 +02:00
|
|
|
$this->Sighting = ClassRegistry::init('Sighting');
|
|
|
|
$sightings = $this->Sighting->listSightings($user, $attribute_id, 'attribute', false, 0, false);
|
|
|
|
if (empty($sightings)) {
|
|
|
|
$sightings = array(array('Sighting' => array('date_sighting' => $attribute['Attribute']['timestamp']))); // simulate a sighting nonetheless
|
|
|
|
}
|
|
|
|
// get start time
|
|
|
|
$start_time = $attribute['Attribute']['timestamp'];
|
|
|
|
// $start_time = $attribute['Attribute']['first_seen'] < $start_time ? $attribute['Attribute']['first_seen'] : $start_time;
|
|
|
|
$start_time = $sightings[0]['Sighting']['date_sighting'] < $start_time ? $sightings[0]['Sighting']['date_sighting'] : $start_time;
|
|
|
|
$start_time = intval($start_time);
|
|
|
|
$start_time = $this->round_timestamp_to_hour($start_time);
|
|
|
|
// get end time
|
|
|
|
$end_time = $sightings[count($sightings)-1]['Sighting']['date_sighting'] + $model['DecayingModel']['parameters']['tau']*24*60*60;
|
|
|
|
$end_time = $this->round_timestamp_to_hour($end_time);
|
2019-08-12 16:34:26 +02:00
|
|
|
$base_score_config = $this->Computation->computeBasescore($model, $attribute['Attribute']);
|
2019-07-17 16:14:24 +02:00
|
|
|
$base_score = $base_score_config['base_score'];
|
2019-07-16 09:31:49 +02:00
|
|
|
|
|
|
|
// generate time span from oldest timestamp to last decay, resolution is hours
|
|
|
|
$score_overtime = array();
|
|
|
|
$rounded_sightings = array();
|
|
|
|
$sighting_index = 0;
|
|
|
|
for ($t=$start_time; $t < $end_time; $t+=3600) {
|
|
|
|
// fetch closest sighting to the current time
|
2019-07-25 15:45:33 +02:00
|
|
|
$sighting_index = $this->getClosestSighting($sightings, $t, $sighting_index);
|
2019-07-16 09:31:49 +02:00
|
|
|
$last_sighting = $this->round_timestamp_to_hour($sightings[$sighting_index]['Sighting']['date_sighting']);
|
|
|
|
$sightings[$sighting_index]['Sighting']['rounded_timestamp'] = $last_sighting;
|
2019-07-17 16:14:24 +02:00
|
|
|
$elapsed_time = $t - $last_sighting;
|
2019-08-13 15:27:11 +02:00
|
|
|
$score_overtime[$t] = $this->Computation->computeScore($model, $attribute['Attribute'], $base_score, $elapsed_time);
|
2019-07-16 09:31:49 +02:00
|
|
|
}
|
|
|
|
$csv = 'date,value' . PHP_EOL;
|
|
|
|
foreach ($score_overtime as $t => $v) {
|
|
|
|
$csv .= (new DateTime())->setTimestamp($t)->format('Y-m-d H:i:s') . ',' . $v . PHP_EOL;
|
|
|
|
}
|
|
|
|
return array(
|
|
|
|
'csv' => $csv,
|
2019-07-17 16:14:24 +02:00
|
|
|
'sightings' => $sightings,
|
|
|
|
'base_score_config' => $base_score_config,
|
|
|
|
'last_sighting' => $sightings[count($sightings)-1],
|
2019-08-14 10:48:13 +02:00
|
|
|
'current_score' => $this->Computation->computeCurrentScore($user, $model, $attribute['Attribute'], $base_score, $sightings[count($sightings)-1]['Sighting']['date_sighting']),
|
|
|
|
'Model' => $model['DecayingModel']
|
2019-07-16 09:31:49 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-07-25 15:45:33 +02:00
|
|
|
public function getClosestSighting($sightings, $time, $previous_index)
|
2019-07-16 09:31:49 +02:00
|
|
|
{
|
|
|
|
if (count($sightings) <= $previous_index+1) {
|
|
|
|
return $previous_index;
|
|
|
|
}
|
|
|
|
$max_time = $time + 3600;
|
|
|
|
$next_index = $previous_index+1;
|
|
|
|
$next_sighting = $sightings[$next_index]['Sighting']['date_sighting'];
|
|
|
|
while ($next_sighting <= $max_time) {
|
|
|
|
$next_index++;
|
|
|
|
if ($next_index >= count($sightings)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
$next_sighting = $sightings[$next_index]['Sighting']['date_sighting'];
|
|
|
|
}
|
|
|
|
return $next_index-1;
|
|
|
|
}
|
|
|
|
|
2019-08-14 10:48:13 +02:00
|
|
|
public function overrideModelParameters(&$model, $model_overrides)
|
|
|
|
{
|
|
|
|
$allowed_overrides = array('threshold' => 1);
|
|
|
|
foreach ($model_overrides as $parameter => $value) {
|
|
|
|
if (isset($allowed_overrides[$parameter])) {
|
|
|
|
$model['DecayingModel']['parameters'][$parameter] = $value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function attachScoresToAttribute($user, &$attribute, $model_id=false, $model_overrides=array())
|
2019-07-25 15:45:33 +02:00
|
|
|
{
|
|
|
|
if ($model_id !== false) {
|
2019-08-16 10:52:00 +02:00
|
|
|
$model = $this->fetchModel($user, $model_id, false);
|
2019-07-25 15:45:33 +02:00
|
|
|
if ($model !== false) {
|
2019-08-14 10:48:13 +02:00
|
|
|
if (!empty($model_overrides)) {
|
|
|
|
$this->overrideModelParameters($model, $model_overrides);
|
|
|
|
}
|
2019-07-25 15:45:33 +02:00
|
|
|
$score = $this->getScore($attribute, $model, $user);
|
2019-07-25 16:13:42 +02:00
|
|
|
$decayed = $this->isDecayed($attribute, $model, $score);
|
2019-08-12 16:34:26 +02:00
|
|
|
$attribute['decay_score'][] = array('DecayingModel' => $model['DecayingModel'], 'score' => $score, 'decayed' => $decayed);
|
2019-07-25 15:45:33 +02:00
|
|
|
} else {
|
|
|
|
throw new NotFoundException(__('Model not found or you are not authorized to view it'));
|
|
|
|
}
|
|
|
|
} else { // get score for all activated models
|
2019-08-12 16:34:26 +02:00
|
|
|
$associated_model_ids = $this->DecayingModelMapping->getAssociatedModels($user, $attribute['type'], true);
|
|
|
|
$associated_model_ids = array_values($associated_model_ids[$attribute['type']]);
|
2019-07-25 15:45:33 +02:00
|
|
|
if (!empty($associated_model_ids)) {
|
|
|
|
foreach ($associated_model_ids as $model_id) {
|
2019-08-16 10:52:00 +02:00
|
|
|
$model = $this->fetchModel($user, $model_id, false);
|
2019-07-25 15:45:33 +02:00
|
|
|
if ($model !== false && $model['DecayingModel']['enabled']) {
|
|
|
|
$score = $this->getScore($attribute, $model, $user);
|
2019-07-25 16:13:42 +02:00
|
|
|
$decayed = $this->isDecayed($attribute, $model, $score);
|
2019-08-12 16:34:26 +02:00
|
|
|
$attribute['decay_score'][] = array('DecayingModel' => $model['DecayingModel'], 'score' => $score, 'decayed' => $decayed);
|
2019-07-25 15:45:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getScore($attribute, $model, $user=false)
|
|
|
|
{
|
|
|
|
if (is_numeric($attribute) && $user !== false) {
|
|
|
|
$this->Attribute = ClassRegistry::init('Attribute');
|
|
|
|
$attribute = $this->Attribute->fetchAttributesSimple($user, array(
|
|
|
|
'conditions' => array('id' => $attribute),
|
|
|
|
'contain' => array('AttributeTag' => array('Tag'))
|
|
|
|
));
|
|
|
|
}
|
|
|
|
if (is_numeric($model) && $user !== false) {
|
2019-08-16 10:52:00 +02:00
|
|
|
$model = $this->fetchModel($user, $model);
|
2019-07-25 15:45:33 +02:00
|
|
|
}
|
|
|
|
$this->Computation = $this->getModelClass($model);
|
|
|
|
return $this->Computation->computeCurrentScore($user, $model, $attribute);
|
|
|
|
}
|
|
|
|
|
2019-07-25 16:13:42 +02:00
|
|
|
public function isDecayed($attribute, $model, $score=false, $user=false)
|
|
|
|
{
|
|
|
|
if ($score === false) {
|
|
|
|
$score = $this->getScore($attribute, $model, $user);
|
|
|
|
}
|
|
|
|
$this->Computation = $this->getModelClass($model);
|
|
|
|
return $this->Computation->isDecayed($model, $attribute, $score);
|
|
|
|
}
|
|
|
|
|
2019-03-08 16:19:07 +01:00
|
|
|
}
|