Implement detail page for experiments

This commit is contained in:
Eike Foken
2011-09-14 05:44:20 +02:00
parent 92e45e8033
commit a4f41fd392
6 changed files with 473 additions and 133 deletions

View File

@@ -1,6 +1,6 @@
<?php <?php defined('BASEPATH') || exit('No direct script access allowed');
/* /*
* Copyright (c) 2011 Karsten Heiken <karsten@disposed.de> * Copyright (c) 2011 Karsten Heiken, Eike Foken
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -38,6 +38,7 @@ class Experiments extends CI_Controller {
$this->load->model('parameter'); $this->load->model('parameter');
$this->load->model('program'); $this->load->model('program');
$this->load->model('experiment'); $this->load->model('experiment');
$this->load->model('job');
} }
/** /**
@@ -131,6 +132,64 @@ class Experiments extends CI_Controller {
$this->load->view('experiments/new', $data); $this->load->view('experiments/new', $data);
} }
/**
* Copies an experiment.
*
* @param string $experimentId
*/
public function copy($experimentId = '') {
$experiment = $this->experiment->getByID($experimentId);
if (isset($experiment['project_id'])) {
redirect('experiments/create/' . $experiment['project_id'] . '/' . $experimentId);
} else {
show_404();
}
}
/**
* Shows detailed informations about a specific experiment.
*
* @param string $experimentId
*/
public function detail($experimentId = '') {
$experiment = $this->experiment->getByID($experimentId);
if (empty($experimentId) || !isset($experiment['id'])){
show_404();
}
$this->load->helper('typography');
$data['experiment'] = $experiment;
$data['job'] = $this->job->getByExperimentId($experiment['id']);
$data['project'] = $this->project->getById($experiment['project_id']);
$this->load->view('experiments/detail', $data);
}
/**
* Allows users to delete an experiment.
*
* @param string $experimentId
*/
public function delete($experimentId = '') {
// TODO: Check user rights
$experiment = $this->experiment->getByID($experimentId);
if (empty($experimentId) || !isset($experiment['id'])){
show_404();
}
$this->load->helper('file');
$experimentPath = FCPATH . 'uploads/' . $experiment['project_id'] . '/' . $experiment['id'] . '/';
if (delete_files($experimentPath, true)) {
rmdir($experimentPath);
}
$this->experiment->delete($experimentId);
redirect('projects/detail/' . $experiment['project_id'], 303);
}
} }
/* End of file experiments.php */ /* End of file experiments.php */

View File

@@ -112,5 +112,37 @@ if ( ! function_exists('prettyTime'))
} }
} }
/**
* Parses any english textual datetime description into a relative date string.
*
* @author Eike Foken <kontakt@eikefoken.de>
* @param string $date
* @param boolean $show_seconds
* @return string
*/
if (!function_exists('relative_time')) {
function relative_time($date, $show_seconds = false) {
$diff = time() - strtotime($date);
if ($diff < 120 && !$show_seconds) {
$output = _('just now');
} else if ($diff < 60 && $show_seconds) {
$output = sprintf(ngettext('%d second ago', '%d seconds ago', $diff), $diff);
} else if (($diff = round($diff / 60)) < 60) {
$output = sprintf(ngettext('%d minute ago', '%d minutes ago', $diff), $diff);
} else if (($diff = round($diff / 60)) < 24) {
$output = sprintf(ngettext('%d hour ago', '%d hours ago', $diff), $diff);
} else if (($diff = round($diff / 24)) < 7) {
$output = sprintf(ngettext('%d day ago', '%d days ago', $diff), $diff);
} else if (($diff = round($diff / 7)) < 4) {
$output = sprintf(ngettext('%d week ago', '%d weeks ago', $diff), $diff);
} else {
$output = _('on') . ' ' . strftime('%B %Y', strtotime($date));
}
return $output;
}
}
/* End of file MY_date_helper.php */ /* End of file MY_date_helper.php */
/* Location: ./application/helpers/MY_date_helper.php */ /* Location: ./application/helpers/MY_date_helper.php */

View File

@@ -1,6 +1,6 @@
<?php defined('BASEPATH') || exit('No direct script access allowed'); <?php defined('BASEPATH') || exit('No direct script access allowed');
/* /*
* Copyright (c) 2011 Karsten Heiken <karsten@disposed.de> * Copyright (c) 2011 Karsten Heiken, Eike Foken
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,14 +25,15 @@
* Experiments are used to store different variations of the same project. * Experiments are used to store different variations of the same project.
* *
* @author Karsten Heiken <karsten@disposed.de> * @author Karsten Heiken <karsten@disposed.de>
* @author Eike Foken <kontakt@eikefoken.de>
*/ */
class Experiment extends CI_Model { class Experiment extends CI_Model {
/** /**
* Create a new experiment. * Creates a new experiment.
* *
* @param array $data The data of the new experiment * @param array $data The data of the new experiment
* @return boolean Was the insert successful? * @return boolean Returns TRUE if the insert was successful.
*/ */
public function create($data) { public function create($data) {
if (!isset($data['project_id'])) { if (!isset($data['project_id'])) {
@@ -51,13 +52,26 @@ class Experiment extends CI_Model {
} }
/** /**
* Deletes a experiment. * Updates an experiment.
*
* @param array $data
* @param string $experimentId
* @return boolean Returns TRUE if the update was successful.
*/
public function update($data, $experimentId) {
$this->db->update('experiments', $data, array('id' => $experimentId));
return $this->db->affected_rows() == 1;
}
/**
* Deletes an experiment.
* *
* @param string $experimentId The experiments ID to delete * @param string $experimentId The experiments ID to delete
* @return boolean Was the deletion successful? * @return boolean Returns TRUE if the deletion was successful.
*/ */
public function delete($experimentId) { public function delete($experimentId) {
return $this->db->delete('experiments', array('id' => $experimentId)); $this->db->delete('experiments', array('id' => $experimentId));
return $this->db->affected_rows() == 1;
} }
/** /**
@@ -99,14 +113,15 @@ class Experiment extends CI_Model {
} }
/** /**
* Gets an experiment by ID. * Gets an experiment by ID (kept for backwards compatibility).
* *
* @deprecated 14-09-2011
* @see Experiment::getById()
* @param string $experimentId The experiment to get * @param string $experimentId The experiment to get
* @return array The experiment * @return array The experiment
*/ */
public function get($experimentId) { public function get($experimentId) {
$query = $this->db->get_where('experiments', array('id' => $experimentId)); return $this->getById($experimentId);
return $query->row_array();
} }
/** /**
@@ -143,3 +158,6 @@ class Experiment extends CI_Model {
return $results; return $results;
} }
} }
/* End of file group.php */
/* Location: ./application/controllers/group.php */

View File

@@ -66,6 +66,22 @@ class Job extends CI_Model {
return $this->db->where('id', $job_id)->update('jobs', $data); return $this->db->where('id', $job_id)->update('jobs', $data);
} }
/**
* Gets a specific job.
*
* @param string $experimentId
* @return array The job data
*/
public function getByExperimentId($experimentId) {
$this->db->select('jobs.*, users.username, users.firstname, users.lastname');
$this->db->join('users', 'jobs.started_by = users.id', 'left');
$this->db->where('experiment_id', $experimentId);
$query = $this->db->get('jobs');
return $query->row_array();
}
/** /**
* Gets a list of recent jobs. * Gets a list of recent jobs.
* *

View File

@@ -0,0 +1,70 @@
<?php $this->load->view('header');?>
<div id="content">
<div class="title">
<h2><?=anchor('projects', _('Projects'));?> &raquo; <?=anchor('projects/detail/' . $project['id'], $project['name']);?> &raquo; <?=$experiment['name'];?></h2>
</div>
<div class="box">
<h3><?=_('Description');?></h3>
<div class="editInPlace"><?=auto_typography($experiment['description']);?></div>
<p></p>
<h3>Actions</h3>
<p>
<?php
if (isset($job['id'])):
?>
<a class="button disabled job_start"><?=_('Start job');?></a>
<?php
else:
?>
<a href="<?=site_url('jobs/start/' . $experiment['id']);?>" class="button job_start"><?=_('Start job');?></a>
<?php
endif;
?>
<a href="<?=site_url('experiments/copy/' . $experiment['id']);?>" class="button left copy"><?=_('Copy experiment');?>
</a><a href="javascript:deleteConfirm('<?=site_url('experiments/delete/' . $experiment['id']);?>');" class="button right delete"><?=_('Delete experiment');?></a>
</p>
</div>
<?php
if (isset($job['id'])):
?>
<div class="box">
<h3><?=_('Job details');?></h3>
<p>
<strong><?=_('Date started');?>:</strong> <?=relative_time($job['started_at']);?><br />
<strong><?=_('Starter');?>:</strong> <?=anchor('users/profile/' . urldecode($job['username']), $job['firstname'] . ' ' . $job['lastname']);?><br />
<strong><?=_('Server');?>:</strong> <?=(!empty($job['server'])) ? anchor('admin/servers/detail/' . urldecode($job['server']), $job['server']) : _('Not yet picked');?>
<div class="progress_bar" style="width: 300px;">
<strong><?=$job['progress']?>%</strong>
<span style="width: <?=$job['progress']?>%;">&nbsp;</span>
</div>
</p>
<?php
if ($job['finished_at'] != '0000-00-00 00:00:00'):
?>
<p><a href="<?=site_url('results/show/' . $experiment['id']);?>" class="button results"><?=_('Show results');?></a></p>
<?php
endif;
?>
</div>
<?php
endif;
?>
</div>
<script>
$('.editInPlace').editInPlace({
url: BASE_URL + 'ajax/update_experiment/' + '<?=$experiment['id']?>',
saving_image: SITE_URL + 'assets/images/ajax-loader.gif',
update_value: 'description',
value_required: true
});
</script>
<?php $this->load->view('footer');?>

View File

@@ -392,6 +392,23 @@ a.button.locked {
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
} }
a.button.disabled {
color: #6e6e6e;
background-color: #fff;
/*border: 1px solid rgba(0, 0, 0, 0.05);*/
cursor: default;
opacity: 0.4;
filter: alpha(opacity=40);
}
a.button.disabled:hover, a.button.disabled:active {
color: inherit;
border: 1px solid #dcdcdc;
-moz-box-shadow: inherit;
-webkit-box-shadow: inherit;
box-shadow: inherit;
}
a.button:hover { a.button:hover {
color: #333; color: #333;
border-color: #999; border-color: #999;
@@ -485,6 +502,21 @@ a.cancel {
padding-left: 30px; padding-left: 30px;
} }
a.copy {
background: url(../images/icons/document-copy.png) 10px center no-repeat #f3f3f3;
padding-left: 30px;
}
a.job_start {
background: url(../images/icons/server--arrow.png) 10px center no-repeat #f3f3f3;
padding-left: 30px;
}
a.results {
background: url(../images/icons/blue-folder-open-document-text.png) 10px center no-repeat #f3f3f3;
padding-left: 30px;
}
a.up { a.up {
background: url(../images/button-sprite.png) 13px -131px no-repeat #f3f3f3; background: url(../images/button-sprite.png) 13px -131px no-repeat #f3f3f3;
width: 18px; width: 18px;
@@ -595,6 +627,119 @@ html ul.tabs li.active a:hover {
padding: 0; padding: 0;
} }
/* Progress bar */
div.progress_bar {
position: relative;
margin: 0 0 15px 0;
padding: 1px;
width: 250px;
height: 20px;
border: 1px solid #999;
background: #fff;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
}
div.progress_bar > span {
display: block;
height: 100%;
background-color: #5b86ac;
background-image: -webkit-gradient(
linear,
left bottom,
left top,
from(#5b86ac),
to(#6891b7)
);
background-image: -moz-linear-gradient(
center bottom,
#5b86ac,
#6891b7
);
background-image: -ms-linear-gradient(
center bottom,
#5b86ac,
#6891b7
);
background-image: -o-linear-gradient(
center bottom,
#5b86ac,
#6891b7
);
position: relative;
overflow: hidden;
}
div.progress_bar > span:after {
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-image: -webkit-gradient(
linear,
0 0,
100% 100%,
color-stop(.25, rgba(255, 255, 255, .08)),
color-stop(.25, transparent),
color-stop(.5, transparent),
color-stop(.5, rgba(255, 255, 255, .08)),
color-stop(.75, rgba(255, 255, 255, .08)),
color-stop(.75, transparent),
to(transparent)
);
background-image: -moz-linear-gradient(
-45deg,
rgba(255, 255, 255, .08) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, .08) 50%,
rgba(255, 255, 255, .08) 75%,
transparent 75%,
transparent
);
background-image: -ms-linear-gradient(
-45deg,
rgba(255, 255, 255, .08) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, .08) 50%,
rgba(255, 255, 255, .08) 75%,
transparent 75%,
transparent
);
background-image: -o-linear-gradient(
-45deg,
rgba(255, 255, 255, .08) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, .08) 50%,
rgba(255, 255, 255, .08) 75%,
transparent 75%,
transparent
);
z-index: 1;
-webkit-background-size: 50px 50px;
-moz-background-size: 50px 50px;
-webkit-animation: move 2s linear infinite;
overflow: hidden;
}
div.progress_bar strong {
color: #111;
position: absolute;
top: 0;
left: 0;
z-index: 2;
width: 100%;
display: block;
line-height: 24px;
text-align: center;
}
/* JTip styles */ /* JTip styles */
.form_info a, .form_info a,