mirror of https://github.com/MISP/MISP
- brought back the quick organisation overview as it's a much missed feature - added treemap for tags - brought attribute histogram into statistics page - more coming in the futurepull/1651/head
parent
1d63edc1cf
commit
c76d358535
|
@ -33,7 +33,9 @@ class ACLComponent extends Component {
|
|||
'add_threatconnect' => array('perm_add'),
|
||||
'attributeReplace' => array('perm_add'),
|
||||
'attributeStatistics' => array('*'),
|
||||
'bro' => array('*'),
|
||||
'checkComposites' => array('perm_admin'),
|
||||
'checkOrphanedAttributes' => array(),
|
||||
'delete' => array('perm_add'),
|
||||
'deleteSelected' => array('perm_add'),
|
||||
'describeTypes' => array('perm_add'),
|
||||
|
@ -133,6 +135,7 @@ class ACLComponent extends Component {
|
|||
'edit' => array(),
|
||||
'enable' => array(),
|
||||
'fetchFromFeed' => array(),
|
||||
'fetchSelectedFromFreetextIndex' => array(),
|
||||
'getEvent' => array(),
|
||||
'index' => array(),
|
||||
'previewEvent' => array(),
|
||||
|
@ -338,6 +341,7 @@ class ACLComponent extends Component {
|
|||
'request_API' => array('*'),
|
||||
'routeafterlogin' => array('*'),
|
||||
'statistics' => array('*'),
|
||||
'tagStatisticsGraph' => array('*'),
|
||||
'terms' => array('*'),
|
||||
'updateLoginTime' => array('*'),
|
||||
'verifyCertificate' => array(),
|
||||
|
|
|
@ -809,10 +809,6 @@ class UsersController extends AppController {
|
|||
}
|
||||
}
|
||||
|
||||
public function attributehistogram() {
|
||||
//all code is called via JS
|
||||
}
|
||||
|
||||
public function histogram($selected = null) {
|
||||
if (!$this->request->is('ajax')) throw new MethodNotAllowedException('This function can only be accessed via AJAX.');
|
||||
if ($selected == '[]') $selected = null;
|
||||
|
@ -1045,7 +1041,21 @@ class UsersController extends AppController {
|
|||
}
|
||||
|
||||
// shows some statistics about the instance
|
||||
public function statistics() {
|
||||
public function statistics($page = 'data') {
|
||||
$this->set('page', $page);
|
||||
$this->set('pages', array('data' => 'Usage data', 'orgs' => 'Organisations', 'tags' => 'Tags', 'attributehistogram' => 'Attribute histogram'));
|
||||
if ($page == 'data') {
|
||||
$this->__statisticsData($this->params['named']);
|
||||
} else if ($page == 'orgs') {
|
||||
$this->__statisticsOrgs($this->params['named']);
|
||||
} else if ($page == 'tags') {
|
||||
$this->__statisticsTags($this->params['named']);
|
||||
} else if ($page == 'attributehistogram') {
|
||||
$this->render('statistics_histogram');
|
||||
}
|
||||
}
|
||||
|
||||
private function __statisticsData($params = array()) {
|
||||
// set all of the data up for the heatmaps
|
||||
$orgs = $this->User->Organisation->find('all', array('fields' => array('DISTINCT (name) AS name'), 'recursive' => -1));
|
||||
$this->loadModel('Log');
|
||||
|
@ -1060,27 +1070,27 @@ class UsersController extends AppController {
|
|||
$this_month = strtotime('first day of this month');
|
||||
$stats[0] = $this->User->Event->find('count', null);
|
||||
$stats[1] = $this->User->Event->find('count', array('conditions' => array('Event.timestamp >' => $this_month)));
|
||||
|
||||
|
||||
$stats[2] = $this->User->Event->Attribute->find('count', array('conditions' => array('Attribute.deleted' => 0)));
|
||||
$stats[3] = $this->User->Event->Attribute->find('count', array('conditions' => array('Attribute.timestamp >' => $this_month, 'Attribute.deleted' => 0)));
|
||||
|
||||
|
||||
$this->loadModel('Correlation');
|
||||
$this->Correlation->recursive = -1;
|
||||
$stats[4] = $this->Correlation->find('count', null);
|
||||
$stats[4] = $stats[4] / 2;
|
||||
|
||||
|
||||
$stats[5] = $this->User->Event->ShadowAttribute->find('count', null);
|
||||
|
||||
|
||||
$stats[6] = $this->User->find('count', null);
|
||||
$stats[7] = count($orgs);
|
||||
|
||||
|
||||
$this->loadModel('Thread');
|
||||
$stats[8] = $this->Thread->find('count', array('conditions' => array('Thread.post_count >' => 0)));
|
||||
$stats[9] = $this->Thread->find('count', array('conditions' => array('Thread.date_created >' => date("Y-m-d H:i:s",$this_month), 'Thread.post_count >' => 0)));
|
||||
|
||||
|
||||
$stats[10] = $this->Thread->Post->find('count', null);
|
||||
$stats[11] = $this->Thread->Post->find('count', array('conditions' => array('Post.date_created >' => date("Y-m-d H:i:s",$this_month))));
|
||||
|
||||
|
||||
$this->set('stats', $stats);
|
||||
$this->set('orgs', $orgs);
|
||||
$this->set('start', strtotime(date('Y-m-d H:i:s') . ' -5 months'));
|
||||
|
@ -1088,6 +1098,108 @@ class UsersController extends AppController {
|
|||
$this->set('startDateCal', $year . ', ' . $month . ', 01');
|
||||
$range = '[5, 10, 50, 100]';
|
||||
$this->set('range', $range);
|
||||
$this->render('statistics_data');
|
||||
}
|
||||
|
||||
private function __statisticsOrgs($params = array()) {
|
||||
$this->loadModel('Organisation');
|
||||
$conditions = array();
|
||||
if (!isset($params['scope']) || $params['scope'] == 'local') {
|
||||
$params['scope'] = 'local';
|
||||
$conditions['Organisation.local'] = 1;
|
||||
} elseif ($params['scope'] == 'external') {
|
||||
$conditions['Organisation.local'] = 0;
|
||||
}
|
||||
$orgs = array();
|
||||
$orgs = $this->Organisation->find('all', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => $conditions,
|
||||
'fields' => array('id', 'name', 'description', 'local', 'contacts', 'type', 'sector', 'nationality'),
|
||||
));
|
||||
$orgs = Set::combine($orgs, '{n}.Organisation.id', '{n}.Organisation');
|
||||
$users = $this->User->find('all', array(
|
||||
'group' => 'User.org_id',
|
||||
'conditions' => array('User.org_id' => array_keys($orgs)),
|
||||
'recursive' => -1,
|
||||
'fields' => array('org_id', 'count(*)')
|
||||
));
|
||||
foreach ($users as $user) {
|
||||
$orgs[$user['User']['org_id']]['userCount'] = $user[0]['count(*)'];
|
||||
}
|
||||
unset($users);
|
||||
$events = $this->User->Event->find('all', array(
|
||||
'group' => 'Event.orgc_id',
|
||||
'conditions' => array('Event.orgc_id' => array_keys($orgs)),
|
||||
'recursive' => -1,
|
||||
'fields' => array('Event.orgc_id', 'count(*)')
|
||||
));
|
||||
foreach ($events as $event) {
|
||||
$orgs[$event['Event']['orgc_id']]['eventCount'] = $event[0]['count(*)'];
|
||||
}
|
||||
unset($events);
|
||||
$orgs = Set::combine($orgs, '{n}.name', '{n}');
|
||||
// f*** php
|
||||
uksort($orgs, 'strcasecmp');
|
||||
foreach ($orgs as $k => $value) {
|
||||
if (file_exists(APP . 'webroot' . DS . 'img' . DS . 'orgs' . DS . $k . '.png')) {
|
||||
$orgs[$k]['logo'] = true;
|
||||
}
|
||||
}
|
||||
$this->set('scope', $params['scope']);
|
||||
$this->set('orgs', $orgs);
|
||||
$this->render('statistics_orgs');
|
||||
}
|
||||
|
||||
public function tagStatisticsGraph() {
|
||||
$top_ten_tags = array();
|
||||
$trending_tags = array();
|
||||
$all_tags = array();
|
||||
$this->loadModel('EventTag');
|
||||
$tags = $this->EventTag->getSortedTagList();
|
||||
$this->loadModel('Taxonomy');
|
||||
$taxonomies = $this->Taxonomy->find('list', array(
|
||||
'conditions' => array('enabled' => true),
|
||||
'fields' => array('Taxonomy.namespace')
|
||||
));
|
||||
$flatData = array();
|
||||
foreach ($tags as $key => $value) {
|
||||
$name = explode(':', $value['name']);
|
||||
$tags[$key]['taxonomy'] = 'custom';
|
||||
if (count($name) > 1) {
|
||||
if (in_array($name[0], $taxonomies)) {
|
||||
$tags[$key]['taxonomy'] = $name[0];
|
||||
}
|
||||
}
|
||||
$flatData[$tags[$key]['taxonomy']][$value['name']] = array('name' => $value['name'], 'size' => $value['eventCount']);
|
||||
}
|
||||
$treemap = array(
|
||||
'name' => 'tags',
|
||||
'children' => array()
|
||||
);
|
||||
|
||||
foreach ($flatData as $key => $value) {
|
||||
$newElement = array(
|
||||
'name' => $key,
|
||||
'children' => array()
|
||||
);
|
||||
foreach ($value as $tag) {
|
||||
$newElement['children'][] = array('name' => $tag['name'], 'size' => $tag['size']);
|
||||
}
|
||||
$treemap['children'][] = $newElement;
|
||||
}
|
||||
$taxonomyColourCodes = array();
|
||||
$taxonomies = array_merge(array('custom'), $taxonomies);
|
||||
$this->set('taxonomyColourCodes', $taxonomyColourCodes);
|
||||
$this->set('taxonomies', $taxonomies);
|
||||
$this->set('flatData', $flatData);
|
||||
$this->set('treemap', $treemap);
|
||||
$this->set('tags', $tags);
|
||||
$this->layout = 'treemap';
|
||||
$this->render('ajax/tag_statistics_graph');
|
||||
}
|
||||
|
||||
private function __statisticsTags($params = array()) {
|
||||
$this->render('statistics_tags');
|
||||
}
|
||||
|
||||
public function verifyGPG() {
|
||||
|
|
|
@ -63,4 +63,30 @@ class EventTag extends AppModel {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSortedTagList($context = false) {
|
||||
$conditions = array();
|
||||
$tag_counts = $this->find('all', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('tag_id', 'count(*)'),
|
||||
'group' => array('tag_id'),
|
||||
'conditions' => $conditions,
|
||||
'contain' => array('Tag.name')
|
||||
));
|
||||
$temp = array();
|
||||
$tags = array();
|
||||
foreach ($tag_counts as $tag_count) {
|
||||
$temp[$tag_count['Tag']['name']] = array(
|
||||
'tag_id' => $tag_count['Tag']['id'],
|
||||
'eventCount' => $tag_count[0]['count(*)'],
|
||||
'name' => $tag_count['Tag']['name'],
|
||||
);
|
||||
$tags[$tag_count['Tag']['name']] = $tag_count[0]['count(*)'];
|
||||
}
|
||||
arsort($tags);
|
||||
foreach ($tags as $k => $v) {
|
||||
$tags[$k] = $temp[$k];
|
||||
}
|
||||
return $tags;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<div class="btn-toolbar">
|
||||
<div class="btn-group">
|
||||
<?php
|
||||
foreach ($pages as $key => $value):
|
||||
?>
|
||||
<a class="btn <?php echo $page == $key ? 'btn-primary' : 'btn-inverse'?> qet" href="<?php echo $baseurl . '/users/statistics/' . h($key); ?>"><?php echo h($value); ?></a>
|
||||
<?php
|
||||
endforeach;
|
||||
?>
|
||||
</div>
|
||||
</div>
|
|
@ -88,7 +88,6 @@
|
|||
<li><a href="<?php echo $baseurl;?>/pages/display/doc/quickstart">User Guide</a></li>
|
||||
<li><a href="<?php echo $baseurl;?>/users/terms">Terms & Conditions</a></li>
|
||||
<li><a href="<?php echo $baseurl;?>/users/statistics">Statistics</a></li>
|
||||
<li><a href="<?php echo $baseurl;?>/users/attributehistogram">Attribute Histogram</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
|
|
@ -155,7 +155,6 @@
|
|||
<li id='liuserGuide'><a href="<?php echo $baseurl;?>/pages/display/doc/general">User Guide</a></li>
|
||||
<li id='literms'><a href="<?php echo $baseurl;?>/users/terms">Terms & Conditions</a></li>
|
||||
<li id='listatistics'><a href="<?php echo $baseurl;?>/users/statistics">Statistics</a></li>
|
||||
<li id='limembers'><a href="<?php echo $baseurl;?>/users/attributehistogram">Attribute Histogram</a></li>
|
||||
<?php
|
||||
break;
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
echo $this->Html->css('treemap');
|
||||
echo $content_for_layout;
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
echo $this->Html->script('d3');
|
||||
?>
|
||||
<div id="treemapSettings">
|
||||
<div class="row">
|
||||
<?php
|
||||
foreach ($taxonomies as $k => $taxonomy):
|
||||
?>
|
||||
<div class="span2" style="cursor: pointer;">
|
||||
<span id="<?php echo $taxonomy . '-colour'?>" class="attributehistogram-legend-box" style="display: block;float: left;margin: 4px 6px 0 0;background-color:white;"> </span>
|
||||
<span class="treemap-selector bold" data-treemap-selector="<?php echo h($taxonomy); ?>"><?php echo h($taxonomy); ?></span>
|
||||
</div>
|
||||
<?php
|
||||
if (($k+1) % 12 == 0) {
|
||||
echo '</div><div class="row">';
|
||||
}
|
||||
endforeach;
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<div id="treemapGraph"></div>
|
||||
<script>
|
||||
var root = <?php echo json_encode($treemap); ?>;
|
||||
var flatData = <?php echo json_encode($flatData); ?>;
|
||||
var taxonomies = <?php echo json_encode($taxonomies); ?>;
|
||||
var hiddenTaxonomies = [];
|
||||
</script>
|
||||
<?php echo $this->Html->script('treemap'); ?>
|
|
@ -1,14 +0,0 @@
|
|||
<div class="users index">
|
||||
<div id = "histogram"></div>
|
||||
<?php //echo $this->element('histogram');?>
|
||||
<br /><br />
|
||||
</div>
|
||||
<?php
|
||||
echo $this->element('side_menu', array('menuList' => 'globalActions', 'menuItem' => 'members'));
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
// tooltips
|
||||
$(document).ready(function () {
|
||||
updateHistogram('');
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
echo $this->Html->script('d3');
|
||||
echo $this->Html->script('cal-heatmap');
|
||||
echo $this->Html->css('cal-heatmap');
|
||||
?>
|
||||
<div class = "index">
|
||||
<h2>Statistics</h2>
|
||||
<?php
|
||||
echo $this->element('Users/statisticsMenu');
|
||||
?>
|
||||
<p>Some statistics about this instance. The changes since the beginning of this month are noted in brackets wherever applicable</p>
|
||||
<div style="width:250px;">
|
||||
<dl>
|
||||
<dt>Events</dt>
|
||||
<dd><?php echo h($stats[0]);
|
||||
if ($stats[1]) echo ' <span style="color:green">(+' . h($stats[1]) . ')</span> ';
|
||||
else echo ' <span style="color:red">(0)</span> ';?>
|
||||
</dd>
|
||||
<dt><?php echo 'Attributes'; ?></dt>
|
||||
<dd><?php echo h($stats[2]);
|
||||
if ($stats[1]) echo ' <span style="color:green">(+' . h($stats[3]) . ')</span> ';
|
||||
else echo ' <span style="color:red">(0)</span> ';?>
|
||||
</dd>
|
||||
<dt><?php echo 'Correlations found'; ?></dt>
|
||||
<dd><?php echo h($stats[4]); ?> </dd>
|
||||
<dt><?php echo 'Proposals active'; ?></dt>
|
||||
<dd><?php echo h($stats[5]); ?> </dd>
|
||||
<dt><?php echo 'Users'; ?></dt>
|
||||
<dd><?php echo h($stats[6]); ?> </dd>
|
||||
<dt><?php echo 'Organisations'; ?></dt>
|
||||
<dd><?php echo h($stats[7]); ?> </dd>
|
||||
<dt><?php echo 'Discussion threads'; ?></dt>
|
||||
<dd><?php echo h($stats[8]);
|
||||
if ($stats[9]) echo ' <span style="color:green">(+' . h($stats[9]) . ')</span> ';
|
||||
else echo ' <span style="color:red">(0)</span> ';?>
|
||||
</dd>
|
||||
<dt><?php echo 'Discussion posts'; ?></dt>
|
||||
<dd><?php echo h($stats[10]);
|
||||
if ($stats[11]) echo ' <span style="color:green">(+' . h($stats[11]) . ')</span> ';
|
||||
else echo ' <span style="color:red">(0)</span> ';?>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<br />
|
||||
<h3>Activity Heatmap</h3>
|
||||
<p>A heatmap showing user activity for each day during this month and the 4 months that preceded it. Use the buttons below to only show the heatmap of a specific organisation.</p>
|
||||
<div id="orgs">
|
||||
<select onchange="updateCalendar(this.options[this.selectedIndex].value);">
|
||||
<option value="all">All organisations</option>
|
||||
<?php
|
||||
foreach ($orgs as $org):
|
||||
?>
|
||||
<option value="<?php echo h($org['Organisation']['name']); ?>"><?php echo h($org['Organisation']['name']); ?></option>
|
||||
<?php
|
||||
endforeach;
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<table>
|
||||
<tr>
|
||||
<td style="vertical-align:top;">
|
||||
<div style="margin-right:5px;margin-top:40px;"><button id="goLeft" class="btn" onClick="goLeft()"><span class="icon-arrow-left"></span></button></div>
|
||||
</td>
|
||||
<td>
|
||||
<div id="cal-heatmap"></div>
|
||||
</td>
|
||||
<td style="vertical-align:top;">
|
||||
<div style="margin-left:5px;margin-top:40px;"><button id="goRight" class="btn" onClick="goRight()"><span class="icon-arrow-right"></span></button></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var cal = new CalHeatMap();
|
||||
var orgSelected = "all";
|
||||
cal.init({
|
||||
range: 5,
|
||||
domain:"month",
|
||||
subDomain:"x_day",
|
||||
start: new Date(<?php echo $startDateCal; ?>),
|
||||
data: "<?php echo Configure::read('MISP.baseurl'); ?>/logs/returnDates.json",
|
||||
highlight: "now",
|
||||
domainDynamicDimension: false,
|
||||
cellSize: 20,
|
||||
cellPadding: 1,
|
||||
domainGutter: 10,
|
||||
legend: <?php echo $range;?>,
|
||||
legendCellSize: 15,
|
||||
});
|
||||
|
||||
function updateCalendar(org) {
|
||||
if (org == "all") {
|
||||
cal.update("<?php echo Configure::read('MISP.baseurl'); ?>/logs/returnDates/all.json");
|
||||
orgSelected = "all";
|
||||
} else {
|
||||
cal.update("<?php echo Configure::read('MISP.baseurl'); ?>/logs/returnDates/"+org+".json");
|
||||
orgSelected = org;
|
||||
}
|
||||
}
|
||||
|
||||
function goRight() {
|
||||
cal.options.data = "<?php echo Configure::read('MISP.baseurl'); ?>/logs/returnDates/"+orgSelected+".json";
|
||||
cal.next();
|
||||
}
|
||||
|
||||
function goLeft() {
|
||||
cal.options.data = "<?php echo Configure::read('MISP.baseurl'); ?>/logs/returnDates/"+orgSelected+".json";
|
||||
cal.previous();
|
||||
}
|
||||
</script>
|
||||
<?php
|
||||
if (preg_match('/(?i)msie [2-9]/',$_SERVER['HTTP_USER_AGENT']) && !strpos($_SERVER['HTTP_USER_AGENT'], 'Opera')) {
|
||||
if (preg_match('%(?i)Trident/(.*?).0%', $_SERVER['HTTP_USER_AGENT'], $matches) && isset($matches[1]) && $matches[1] > 5) {
|
||||
?>
|
||||
<br /><br /><p style="color:red;font-size:11px;">The above graph will not work correctly in Compatibility mode. Please make sure that it is disabled in your Internet Explorer settings.</p>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<br /><br /><p style="color:red;font-size:11px;">The above graph will not work correctly on Internet Explorer 9.0 and earlier. Please download Chrome, Firefox or upgrade to a newer version of Internet Explorer.</p>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
echo $this->element('side_menu', array('menuList' => 'globalActions', 'menuItem' => 'statistics'));
|
||||
?>
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
echo $this->Html->script('d3');
|
||||
echo $this->Html->script('cal-heatmap');
|
||||
echo $this->Html->css('cal-heatmap');
|
||||
?>
|
||||
<div class = "index">
|
||||
<h2>Statistics</h2>
|
||||
<?php echo $this->element('Users/statisticsMenu'); ?>
|
||||
<div id = "histogram"></div>
|
||||
</div>
|
||||
<?php
|
||||
echo $this->element('side_menu', array('menuList' => 'globalActions', 'menuItem' => 'statistics'));
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
updateHistogram('');
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
echo $this->Html->script('d3');
|
||||
echo $this->Html->script('cal-heatmap');
|
||||
echo $this->Html->css('cal-heatmap');
|
||||
?>
|
||||
<div class = "index">
|
||||
<h2>Statistics</h2>
|
||||
<?php
|
||||
echo $this->element('Users/statisticsMenu');
|
||||
$types = array(
|
||||
'local' => array('selected' => false, 'text' => 'Local organisations'),
|
||||
'external' => array('selected' => false, 'text' => 'Known remote organisations'),
|
||||
'all' => array('selected' => false, 'text' => 'All organisations')
|
||||
);
|
||||
$types[$scope]['selected'] = true;
|
||||
?>
|
||||
<p>Quick overview over the organisations residing on or known by this instance.</p>
|
||||
<div class="tabMenuFixedContainer" style="display:inline-block;">
|
||||
<?php
|
||||
foreach ($types as $key => $value):
|
||||
?>
|
||||
<span class="tabMenuFixed tabMenuFixedCenter tabMenuSides useCursorPointer <?php if ($value['selected']) echo 'tabMenuActive'; ?>" onclick="window.location='/users/statistics/orgs/scope:<?php echo h($key);?>'"><?php echo h($value['text']); ?></span>
|
||||
<?php
|
||||
endforeach;
|
||||
?>
|
||||
</div>
|
||||
<table class="table table-striped table-hover table-condensed" style="width:50%;">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Logo</th>
|
||||
<th>Users</th>
|
||||
<th>Events</th>
|
||||
<th>Nationality</th>
|
||||
<th>Type</th>
|
||||
<th>Sector</th>
|
||||
</tr>
|
||||
<?php
|
||||
foreach ($orgs as $data):
|
||||
?>
|
||||
<tr class="org_row" data-orgid="<?php echo h($data['id']); ?>">
|
||||
<td class="short"><?php echo h($data['name']); ?></td>
|
||||
<td class="short">
|
||||
<?php
|
||||
if (isset($data['logo'])):
|
||||
?>
|
||||
<img src="<?php echo $baseurl; ?>/img/orgs/<?php echo h($data['name']); ?>.png" height="24" width="24" />
|
||||
<?php
|
||||
else:
|
||||
echo ' ';
|
||||
endif;
|
||||
|
||||
?>
|
||||
</td>
|
||||
<td class="short"><span class="<?php echo isset($data['userCount']) ? 'blue bold' : 'grey'; ?>"><?php echo isset($data['userCount']) ? h($data['userCount']) : '0';?></span></td>
|
||||
<td class="short"><span class="<?php echo isset($data['eventCount']) ? 'blue bold' : 'grey'; ?>"><?php echo isset($data['eventCount']) ? h($data['eventCount']) : '0';?></span></td>
|
||||
<td class="shortish"><?php echo isset($data['nationality']) && $data['nationality'] !== 'Not specified' ? h($data['nationality']) : ' '; ?></td>
|
||||
<td class="shortish"><?php echo isset($data['type']) ? h($data['type']) : ' '; ?></td>
|
||||
<td class="shortish"><?php echo isset($data['sector']) ? h($data['sector']) : ' '; ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
endforeach;
|
||||
?>
|
||||
</table>
|
||||
</div>
|
||||
<?php
|
||||
echo $this->element('side_menu', array('menuList' => 'globalActions', 'menuItem' => 'statistics'));
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
$('.org_row').click(function() {
|
||||
window.location = "<?php echo $baseurl; ?>/organisations/view/" + $(this).data('orgid');
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
echo $this->Html->script('d3');
|
||||
echo $this->Html->script('cal-heatmap');
|
||||
echo $this->Html->css('cal-heatmap');
|
||||
?>
|
||||
<div class = "index">
|
||||
<h2>Statistics</h2>
|
||||
<?php
|
||||
echo $this->element('Users/statisticsMenu');
|
||||
?>
|
||||
<p>A treemap of the currently used event tags. Click on any of the taxonomies to hide it and click it again to show it.</p>
|
||||
<div id="treemapdiv" class="treemapdiv"></div>
|
||||
</div>
|
||||
<?php
|
||||
echo $this->element('side_menu', array('menuList' => 'globalActions', 'menuItem' => 'statistics'));
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
loadTagTreemap();
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,35 @@
|
|||
@CHARSET "UTF-8";
|
||||
|
||||
.treemapdiv {
|
||||
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#treemapSettings {
|
||||
margin-top:10px;
|
||||
margin-bottom:10px;
|
||||
border: 1px solid black;
|
||||
width: 960px;
|
||||
}
|
||||
|
||||
#taxonomydt {
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
#taxonomydd {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#treemapGraph {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
width: 960px;
|
||||
}
|
||||
|
||||
.node {
|
||||
border: solid 1px white;
|
||||
font: 10px sans-serif;
|
||||
line-height: 12px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
text-indent: 2px;
|
||||
}
|
|
@ -2638,3 +2638,21 @@ function checkOrphanedAttributes() {
|
|||
url: "/attributes/checkOrphanedAttributes/",
|
||||
});
|
||||
}
|
||||
|
||||
function loadTagTreemap() {
|
||||
$.ajax({
|
||||
async:true,
|
||||
beforeSend: function (XMLHttpRequest) {
|
||||
$(".loading").show();
|
||||
},
|
||||
success:function (data, textStatus) {
|
||||
$(".treemapdiv").html(data);
|
||||
},
|
||||
complete:function() {
|
||||
$(".loading").hide();
|
||||
},
|
||||
type:"get",
|
||||
cache: false,
|
||||
url: "/users/tagStatisticsGraph",
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
var margin = {top: 0, right: 0, bottom: 0, left: 0},
|
||||
width = 960 - margin.left - margin.right,
|
||||
height = 500 - margin.top - margin.bottom;
|
||||
|
||||
var color = d3.scale.category20c();
|
||||
|
||||
var treemap = d3.layout.treemap()
|
||||
.size([width, height])
|
||||
.sticky(true)
|
||||
.value(function(d) { return d.size; });
|
||||
|
||||
var div = d3.select("#treemapGraph").append("div")
|
||||
.style("position", "relative")
|
||||
.style("width", (width + margin.left + margin.right) + "px")
|
||||
.style("height", (height + margin.top + margin.bottom) + "px")
|
||||
.style("left", margin.left + "px")
|
||||
.style("top", margin.top + "px");
|
||||
|
||||
var node = div.datum(root).selectAll(".node")
|
||||
.data(treemap.nodes)
|
||||
.enter().append("div")
|
||||
.attr("class", "node")
|
||||
.attr("title", function(d) {return d.name + ': ' + d.size})
|
||||
.attr("id", function(d) { return d.name + '-node'})
|
||||
.call(position)
|
||||
.style("background", function(d) { return d.children ? color(d.name) : null; })
|
||||
.text(function(d) { return d.children ? null : d.name; });
|
||||
|
||||
taxonomies.forEach(function(taxonomy) {
|
||||
$("#" + taxonomy + "-colour").css("background-color", $("#" + taxonomy + "-node").css('background-color'));
|
||||
});
|
||||
|
||||
d3.selectAll("input").on("change", function change() {
|
||||
var value = this.value === "count" ? function() { return 1; } : function(d) { return d.size; };
|
||||
node
|
||||
.data(treemap.value(value).nodes)
|
||||
.transition()
|
||||
.duration(1500)
|
||||
.call(position);
|
||||
});
|
||||
|
||||
function position() {
|
||||
this.style("left", function(d) { return d.x + "px"; })
|
||||
.style("top", function(d) { return d.y + "px"; })
|
||||
.style("width", function(d) { return Math.max(0, d.dx - 1) + "px"; })
|
||||
.style("height", function(d) { return Math.max(0, d.dy - 1) + "px"; });
|
||||
}
|
||||
|
||||
function updateTaxonomies() {
|
||||
var value = function(d) {
|
||||
tagTaxonomy = d.name.split(':')[0];
|
||||
if (taxonomies.indexOf(tagTaxonomy) == -1) {
|
||||
tagTaxonomy = 'custom';
|
||||
}
|
||||
if ($.inArray(tagTaxonomy, hiddenTaxonomies) > -1) {
|
||||
return 0;
|
||||
} else {
|
||||
return flatData[tagTaxonomy][d.name]['size'];
|
||||
}
|
||||
};
|
||||
node
|
||||
.data(treemap.value(value).nodes)
|
||||
.transition()
|
||||
.duration(1500)
|
||||
.call(position);
|
||||
}
|
||||
|
||||
$('.treemap-selector').click(function() {
|
||||
var taxonomy = $( this ).data("treemap-selector");
|
||||
var index = hiddenTaxonomies.indexOf(taxonomy);
|
||||
if ($( this ).hasClass("bold")) {
|
||||
$( this ).removeClass("bold");
|
||||
if (index < 0) {
|
||||
hiddenTaxonomies.push(taxonomy);
|
||||
}
|
||||
} else {
|
||||
$( this ).addClass("bold");
|
||||
if (index > -1) {
|
||||
hiddenTaxonomies.splice(index, 1);
|
||||
}
|
||||
}
|
||||
updateTaxonomies();
|
||||
});
|
Loading…
Reference in New Issue