mirror of
https://github.com/ConnectedHumber/Air-Quality-Web
synced 2024-12-22 10:25:01 +00:00
Add performance counter system.
This commit is contained in:
parent
b92621aab6
commit
0006c2a921
15 changed files with 229 additions and 92 deletions
12
api.php
12
api.php
|
@ -12,16 +12,23 @@ $autoloader->addPrefix("AirQuality", "logic");
|
|||
$autoloader->addPrefix("SBRL", "lib/SBRL");
|
||||
$autoloader->register();
|
||||
|
||||
// 1.5: Performance counter
|
||||
$perfcounter = new \SBRL\PerformanceCounter();
|
||||
$perfcounter->start("total");
|
||||
|
||||
// 2: Settings
|
||||
$perfcounter->start("settings");
|
||||
$settings = new \SBRL\TomlConfig(
|
||||
"data/settings.toml",
|
||||
"settings.default.toml"
|
||||
);
|
||||
$perfcounter->end("settings");
|
||||
|
||||
|
||||
// 3: Dependency injection
|
||||
|
||||
$perfcounter->start("di");
|
||||
|
||||
$di_builder = new DI\ContainerBuilder();
|
||||
$di_builder->addDefinitions("di_config.php");
|
||||
if($settings->get("env.mode") == "production") {
|
||||
|
@ -37,6 +44,7 @@ if($settings->get("env.mode") == "production") {
|
|||
|
||||
$di_container = $di_builder->build();
|
||||
|
||||
$perfcounter->end("di");
|
||||
|
||||
// 4: Database
|
||||
|
||||
|
@ -68,6 +76,8 @@ if(!class_exists($handler_name)) {
|
|||
exit("Error: No action with the name '$action' could be found.");
|
||||
}
|
||||
|
||||
// FUTURE: A PSR-7 request/response system would be rather nice here.
|
||||
$handler = $di_container->get($handler_name);
|
||||
|
||||
$perfcounter->start("handle");
|
||||
// FUTURE: A PSR-7 request/response system would be rather nice here.
|
||||
$handler->handle();
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use Psr\Container\ContainerInterface;
|
||||
use SBRL\TomlConfig;
|
||||
|
||||
use AirQuality\Database;
|
||||
// use AirQuality\Database;
|
||||
|
||||
use AirQuality\Repositories\IDeviceRepository;
|
||||
use AirQuality\Repositories\MariaDBDeviceRepository;
|
||||
|
@ -12,15 +12,23 @@ use AirQuality\Repositories\MariaDBMeasurementDataRepository;
|
|||
use AirQuality\Repositories\IMeasurementTypeRepository;
|
||||
use AirQuality\Repositories\MariaDBMeasurementTypeRepository;
|
||||
|
||||
use SBRL\PerformanceCounter;
|
||||
|
||||
return [
|
||||
"settings.file_default" => "data/settings.toml",
|
||||
"settings.file_custom" => "settings.default.toml",
|
||||
|
||||
// These are created during initalisation, but we want them available via dependency injection too
|
||||
TomlConfig::class => function(ContainerInterface $c) {
|
||||
global $settings;
|
||||
return $settings;
|
||||
},
|
||||
PerformanceCounter::class => function(ContainerInterface $c) {
|
||||
global $perfcounter;
|
||||
return $perfcounter;
|
||||
},
|
||||
|
||||
// Interfaces that need mapping to their implementations
|
||||
IDeviceRepository::class => DI\autowire(MariaDBDeviceRepository::class),
|
||||
IMeasurementDataRepository::class => DI\autowire(MariaDBMeasurementDataRepository::class),
|
||||
IMeasurementTypeRepository::class => DI\autowire(MariaDBMeasurementTypeRepository::class)
|
||||
|
|
83
lib/SBRL/PerformanceCounter.php
Normal file
83
lib/SBRL/PerformanceCounter.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace SBRL;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Records performance information.
|
||||
*/
|
||||
class PerformanceCounter
|
||||
{
|
||||
/**
|
||||
* An associative array of performance information.
|
||||
* It should take the form key => [float] | float.
|
||||
* HACK: If the float is the only item in an array, then it hasn't been ended yet.
|
||||
* @var array
|
||||
*/
|
||||
private $data = [];
|
||||
|
||||
/**
|
||||
* An array of performance counters to hide when rendering.
|
||||
* Useful to keep some counters around for later, but keep them hidden so as to not clutter everything up.
|
||||
* @var string[]
|
||||
*/
|
||||
private $hidden = [];
|
||||
|
||||
/**
|
||||
* Creates a new performance counter instea
|
||||
*/
|
||||
function __construct() {
|
||||
// code...
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts recording the time taken for a given key.
|
||||
* Calling end($key) is optional
|
||||
* @param string $key The key to start recording the time taken for.
|
||||
*/
|
||||
public function start(string $key) {
|
||||
$this->data[$key] = [microtime(true)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends recording the time taken for a given key.
|
||||
* @param string $key The key to end counting for.
|
||||
* @return float The time taken, in milliseconds
|
||||
*/
|
||||
public function end(string $key) {
|
||||
if(empty($this->data[$key]))
|
||||
throw new Exception("Error: We can't end counting for $key because we haven't started yet");
|
||||
|
||||
$this->data[$key] = (microtime(true) - $this->data[$key][0]) * 1000;
|
||||
return $this->data[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the performance information as a short string.
|
||||
* Suitable for sending in a HTTP header, for example.
|
||||
* @return string The rendered performance information.
|
||||
*/
|
||||
public function render() {
|
||||
$result = "";
|
||||
$parts = [];
|
||||
foreach($this->data as $key => $value) {
|
||||
if(in_array($key, $this->hidden))
|
||||
continue;
|
||||
|
||||
if(is_array($value))
|
||||
$value = $this->end($key);
|
||||
|
||||
$render = round($value, 2) . "ms $key";
|
||||
if($key == "total") {
|
||||
$result .= "$render | ";
|
||||
continue;
|
||||
}
|
||||
$parts[] = $render;
|
||||
}
|
||||
|
||||
$result .= implode(", ", $parts);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -4,36 +4,40 @@ namespace AirQuality\Actions;
|
|||
|
||||
use \SBRL\TomlConfig;
|
||||
|
||||
use \AirQuality\PerfFormatter;
|
||||
|
||||
class Changelog implements IAction {
|
||||
/** @var TomlConfig */
|
||||
private $settings;
|
||||
/** @var \SBRL\PerformanceCounter */
|
||||
private $perfcounter;
|
||||
|
||||
/** @var \ParsedownExtra */
|
||||
private $parsedown_ext;
|
||||
|
||||
public function __construct(
|
||||
TomlConfig $in_settings,
|
||||
\ParsedownExtra $in_parsedown_ext) {
|
||||
\ParsedownExtra $in_parsedown_ext,
|
||||
\SBRL\PerformanceCounter $in_perfcounter) {
|
||||
$this->settings = $in_settings;
|
||||
$this->parsedown_ext = $in_parsedown_ext;
|
||||
$this->perfcounter = $in_perfcounter;
|
||||
}
|
||||
|
||||
public function handle() : bool {
|
||||
global $start_time;
|
||||
|
||||
$start_handle = microtime(true);
|
||||
|
||||
|
||||
// 1: Parse markdown
|
||||
$this->perfcounter->start("parse");
|
||||
$result = $this->parsedown_ext->text(file_get_contents(ROOT_DIR."Changelog.md"));
|
||||
$this->perfcounter->end("parse");
|
||||
|
||||
|
||||
// 2: Send response
|
||||
header("content-length: " . strlen($result));
|
||||
header("content-type: text/html");
|
||||
header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, null));
|
||||
header("x-time-taken: " . $this->perfcounter->render());
|
||||
echo($result);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -9,11 +9,13 @@ use \AirQuality\Repositories\IMeasurementTypeRepository;
|
|||
use \AirQuality\ApiResponseSender;
|
||||
|
||||
use \AirQuality\Validator;
|
||||
use \AirQuality\PerfFormatter;
|
||||
|
||||
|
||||
class DeviceData implements IAction {
|
||||
/** @var TomlConfig */
|
||||
private $settings;
|
||||
/** @var \SBRL\PerformanceCounter */
|
||||
private $perfcounter;
|
||||
|
||||
/** @var IMeasurementDataRepository */
|
||||
private $measurement_repo;
|
||||
|
@ -30,11 +32,13 @@ class DeviceData implements IAction {
|
|||
TomlConfig $in_settings,
|
||||
IMeasurementDataRepository $in_measurement_repo,
|
||||
IMeasurementTypeRepository $in_type_repo,
|
||||
ApiResponseSender $in_sender) {
|
||||
ApiResponseSender $in_sender,
|
||||
\SBRL\PerformanceCounter $in_perfcounter) {
|
||||
$this->settings = $in_settings;
|
||||
$this->measurement_repo = $in_measurement_repo;
|
||||
$this->type_repo = $in_type_repo;
|
||||
$this->sender = $in_sender;
|
||||
$this->perfcounter = $in_perfcounter;
|
||||
|
||||
$this->validator = new Validator($_GET);
|
||||
}
|
||||
|
@ -42,7 +46,6 @@ class DeviceData implements IAction {
|
|||
public function handle() : bool {
|
||||
global $start_time;
|
||||
|
||||
$start_handle = microtime(true);
|
||||
|
||||
// 1: Validate params
|
||||
$this->validator->is_numberish("device-id");
|
||||
|
@ -61,7 +64,7 @@ class DeviceData implements IAction {
|
|||
if(new \DateTime($_GET["start"]) > new \DateTime($_GET["end"])) {
|
||||
$this->sender->send_error_plain(
|
||||
400, "Error: The start date must be earlier than the end date.", [
|
||||
[ "x-time-taken", PerfFormatter::format_perf_data($start_time, $start_handle, null) ]
|
||||
[ "x-time-taken", $this->perfcounter->render() ]
|
||||
]
|
||||
);
|
||||
return false;
|
||||
|
@ -70,18 +73,19 @@ class DeviceData implements IAction {
|
|||
if(!empty($_GET["average-seconds"]) && intval($_GET["average-seconds"]) == 0) {
|
||||
$this->sender->send_error_plain(
|
||||
400, "Error: That average-seconds value is invalid (an integer greater than 0 required).", [
|
||||
[ "x-time-taken", PerfFormatter::format_perf_data($start_time, $start_handle, null) ]
|
||||
[ "x-time-taken", $this->perfcounter->render() ]
|
||||
]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->perfcounter->start("sql");
|
||||
$reading_type_id = $this->type_repo->get_id($_GET["reading-type"]);
|
||||
|
||||
if($reading_type_id == null) {
|
||||
$this->sender->send_error_plain(
|
||||
400, "Error: That reading type is invalid.", [
|
||||
[ "x-time-taken", PerfFormatter::format_perf_data($start_time, $start_handle, null) ]
|
||||
[ "x-time-taken", $this->perfcounter->render() ]
|
||||
]
|
||||
);
|
||||
return false;
|
||||
|
@ -95,19 +99,20 @@ class DeviceData implements IAction {
|
|||
new \DateTime($_GET["end"]),
|
||||
!empty($_GET["average-seconds"]) ? intval($_GET["average-seconds"]) : 1
|
||||
);
|
||||
$this->perfcounter->end("sql");
|
||||
|
||||
// 2.5: Validate data from database
|
||||
if(empty($data)) {
|
||||
http_response_code(404);
|
||||
header("content-type: text/plain");
|
||||
header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, null));
|
||||
header("x-time-taken: " . $this->perfcounter->render());
|
||||
echo("Error: No data has been recorded from the device id for that measurement type in the selected time scale or it doesn't exist.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 3: Serialise data
|
||||
$start_encode = microtime(true);
|
||||
$this->perfcounter->start("encode");
|
||||
$response_type = "application/octet-stream";
|
||||
$response_suggested_filename = "data-" . date(\DateTime::ATOM) . "";
|
||||
$response = null;
|
||||
|
@ -123,13 +128,14 @@ class DeviceData implements IAction {
|
|||
$response = ResponseEncoder::encode_csv($data);
|
||||
break;
|
||||
}
|
||||
$this->perfcounter->end("encode");
|
||||
|
||||
|
||||
// 4: Send response
|
||||
header("content-length: " . strlen($response));
|
||||
header("content-type: $response_type");
|
||||
header("content-disposition: inline; filename=$response_suggested_filename");
|
||||
header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, $start_encode));
|
||||
header("x-time-taken: " . $this->perfcounter->render());
|
||||
echo($response);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -7,11 +7,14 @@ use \AirQuality\Repositories\IMeasurementDataRepository;
|
|||
use \AirQuality\ApiResponseSender;
|
||||
|
||||
use \AirQuality\Validator;
|
||||
use \AirQuality\PerfFormatter;
|
||||
|
||||
|
||||
class DeviceDataBounds implements IAction {
|
||||
/** @var TomlConfig */
|
||||
private $settings;
|
||||
/** @var \SBRL\PerformanceCounter */
|
||||
private $perfcounter;
|
||||
|
||||
/** @var IMeasurementDataRepository */
|
||||
private $measurement_repo;
|
||||
|
||||
|
@ -24,10 +27,12 @@ class DeviceDataBounds implements IAction {
|
|||
public function __construct(
|
||||
TomlConfig $in_settings,
|
||||
IMeasurementDataRepository $in_measurement_repo,
|
||||
ApiResponseSender $in_sender) {
|
||||
ApiResponseSender $in_sender,
|
||||
\SBRL\PerformanceCounter $in_perfcounter) {
|
||||
$this->settings = $in_settings;
|
||||
$this->measurement_repo = $in_measurement_repo;
|
||||
$this->sender = $in_sender;
|
||||
$this->perfcounter = $in_perfcounter;
|
||||
|
||||
$this->validator = new Validator($_GET);
|
||||
}
|
||||
|
@ -35,7 +40,6 @@ class DeviceDataBounds implements IAction {
|
|||
public function handle() : bool {
|
||||
global $start_time;
|
||||
|
||||
$start_handle = microtime(true);
|
||||
|
||||
// 1: Validate params
|
||||
$this->validator->is_numberish("device-id");
|
||||
|
@ -43,29 +47,32 @@ class DeviceDataBounds implements IAction {
|
|||
|
||||
|
||||
// 2: Pull data from database
|
||||
$this->perfcounter->start("sql");
|
||||
$data = $this->measurement_repo->get_device_reading_bounds(
|
||||
intval($_GET["device-id"])
|
||||
);
|
||||
$this->perfcounter->end("sql");
|
||||
|
||||
// 2.5: Validate data from database
|
||||
if(empty($data)) {
|
||||
http_response_code(404);
|
||||
header("content-type: text/plain");
|
||||
header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, null));
|
||||
header("x-time-taken: " . $this->perfcounter->render());
|
||||
echo("Error: No data has been recorded from the device id or it doesn't exist.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 3: Serialise data
|
||||
$start_encode = microtime(true);
|
||||
$this->perfcounter->start("encode");
|
||||
$response = json_encode($data);
|
||||
$this->perfcounter->end("encode");
|
||||
|
||||
|
||||
// 4: Send response
|
||||
header("content-length: " . strlen($response));
|
||||
header("content-type: application/json");
|
||||
header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, $start_encode));
|
||||
header("x-time-taken: " . $this->perfcounter->render());
|
||||
echo($response);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use \AirQuality\Repositories\IMeasurementTypeRepository;
|
|||
use \AirQuality\ApiResponseSender;
|
||||
|
||||
use \AirQuality\Validator;
|
||||
use \AirQuality\PerfFormatter;
|
||||
|
||||
|
||||
/**
|
||||
* Action that retrieves recent data for a device.
|
||||
|
@ -17,6 +17,8 @@ use \AirQuality\PerfFormatter;
|
|||
class DeviceDataRecent implements IAction {
|
||||
/** @var TomlConfig */
|
||||
private $settings;
|
||||
/** @var \SBRL\PerformanceCounter */
|
||||
private $perfcounter;
|
||||
|
||||
/** @var IMeasurementDataRepository */
|
||||
private $measurement_repo;
|
||||
|
@ -33,11 +35,13 @@ class DeviceDataRecent implements IAction {
|
|||
TomlConfig $in_settings,
|
||||
IMeasurementDataRepository $in_measurement_repo,
|
||||
IMeasurementTypeRepository $in_type_repo,
|
||||
ApiResponseSender $in_sender) {
|
||||
ApiResponseSender $in_sender,
|
||||
\SBRL\PerformanceCounter $in_perfcounter) {
|
||||
$this->settings = $in_settings;
|
||||
$this->measurement_repo = $in_measurement_repo;
|
||||
$this->type_repo = $in_type_repo;
|
||||
$this->sender = $in_sender;
|
||||
$this->perfcounter = $in_perfcounter;
|
||||
|
||||
$this->validator = new Validator($_GET);
|
||||
}
|
||||
|
@ -45,7 +49,6 @@ class DeviceDataRecent implements IAction {
|
|||
public function handle() : bool {
|
||||
global $start_time;
|
||||
|
||||
$start_handle = microtime(true);
|
||||
|
||||
// 1: Validate params
|
||||
$this->validator->is_numberish("device-id");
|
||||
|
@ -69,24 +72,26 @@ class DeviceDataRecent implements IAction {
|
|||
if($reading_type_id == null) {
|
||||
$this->sender->send_error_plain(
|
||||
400, "Error: That reading type is invalid.", [
|
||||
[ "x-time-taken", PerfFormatter::format_perf_data($start_time, $start_handle, null) ]
|
||||
[ "x-time-taken", $this->perfcounter->render() ]
|
||||
]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2: Pull data from database
|
||||
$this->perfcounter->start("sql");
|
||||
$data = $this->measurement_repo->get_recent_readings(
|
||||
intval($_GET["device-id"]),
|
||||
$reading_type_id,
|
||||
$count
|
||||
);
|
||||
$this->perfcounter->end("sql");
|
||||
|
||||
// 2.5: Validate data from database
|
||||
if(empty($data)) {
|
||||
http_response_code(404);
|
||||
header("content-type: text/plain");
|
||||
header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, null));
|
||||
header("x-time-taken: " . $this->perfcounter->render());
|
||||
echo("Error: No data has been recorded from the device id for that measurement type in the selected time scale or it doesn't exist.");
|
||||
return false;
|
||||
}
|
||||
|
@ -96,7 +101,7 @@ class DeviceDataRecent implements IAction {
|
|||
|
||||
// FUTURE: Refactor this into an AbstractAction or something, but the careful not to shift too much work there
|
||||
// We might want to consider a HttpResponse container or something if we can find a decent PSR implementation
|
||||
$start_encode = microtime(true);
|
||||
$this->perfcounter->start("encode");
|
||||
$response_type = "application/octet-stream";
|
||||
$response_suggested_filename = "data-" . date(\DateTime::ATOM) . "";
|
||||
$response = null;
|
||||
|
@ -112,13 +117,14 @@ class DeviceDataRecent implements IAction {
|
|||
$response = ResponseEncoder::encode_csv($data);
|
||||
break;
|
||||
}
|
||||
$this->perfcounter->end("encode");
|
||||
|
||||
|
||||
// 4: Send response
|
||||
header("content-length: " . strlen($response));
|
||||
header("content-type: $response_type");
|
||||
header("content-disposition: inline; filename=$response_suggested_filename");
|
||||
header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, $start_encode));
|
||||
header("x-time-taken: " . $this->perfcounter->render());
|
||||
echo($response);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -4,14 +4,18 @@ namespace AirQuality\Actions;
|
|||
|
||||
use \SBRL\TomlConfig;
|
||||
use \AirQuality\Repositories\IDeviceRepository;
|
||||
use \AirQuality\Repositories\IMeasurementTypeRepository;
|
||||
use \AirQuality\ApiResponseSender;
|
||||
|
||||
use \AirQuality\Validator;
|
||||
use \AirQuality\PerfFormatter;
|
||||
|
||||
|
||||
class DeviceInfo implements IAction {
|
||||
/** @var TomlConfig */
|
||||
private $settings;
|
||||
/** @var \SBRL\PerformanceCounter */
|
||||
private $perfcounter;
|
||||
|
||||
/** @var IDeviceRepository */
|
||||
private $device_repo;
|
||||
|
||||
|
@ -27,10 +31,12 @@ class DeviceInfo implements IAction {
|
|||
public function __construct(
|
||||
TomlConfig $in_settings,
|
||||
IDeviceRepository $in_device_repo,
|
||||
ApiResponseSender $in_sender) {
|
||||
ApiResponseSender $in_sender,
|
||||
\SBRL\PerformanceCounter $in_perfcounter) {
|
||||
$this->settings = $in_settings;
|
||||
$this->device_repo = $in_device_repo;
|
||||
$this->sender = $in_sender;
|
||||
$this->perfcounter = $in_perfcounter;
|
||||
|
||||
$this->validator = new Validator($_GET);
|
||||
}
|
||||
|
@ -38,28 +44,30 @@ class DeviceInfo implements IAction {
|
|||
public function handle() : bool {
|
||||
global $start_time;
|
||||
|
||||
$start_handle = microtime(true);
|
||||
|
||||
// 1: Validate params
|
||||
$this->validator->is_numberish("device-id");
|
||||
$this->validator->run();
|
||||
|
||||
// 2: Pull data from database
|
||||
$this->perfcounter->start("sql");
|
||||
$data = $this->device_repo->get_device_info_ext(
|
||||
$_GET["device-id"]
|
||||
);
|
||||
$this->perfcounter->end("sql");
|
||||
|
||||
// 2.5: Validate data from database
|
||||
if(empty($data)) {
|
||||
$this->sender->send_error_plain(404, "Error: No data could be found for that device id.", [
|
||||
[ "x-time-taken: ", PerfFormatter::format_perf_data($start_time, $start_handle, null) ]
|
||||
[ "x-time-taken: ", $this->perfcounter->render() ]
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3: Serialise data
|
||||
$start_encode = microtime(true);
|
||||
$this->perfcounter->start("encode");
|
||||
$response = json_encode($data);
|
||||
$this->perfcounter->end("encode");
|
||||
|
||||
// 4: Send response
|
||||
|
||||
|
@ -70,7 +78,7 @@ class DeviceInfo implements IAction {
|
|||
|
||||
header("content-length: " . strlen($response));
|
||||
header("content-type: application/json");
|
||||
header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, $start_encode));
|
||||
header("x-time-taken: " . $this->perfcounter->render());
|
||||
echo($response);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -9,11 +9,13 @@ use \AirQuality\Repositories\IMeasurementTypeRepository;
|
|||
use \AirQuality\ApiResponseSender;
|
||||
|
||||
use \AirQuality\Validator;
|
||||
use \AirQuality\PerfFormatter;
|
||||
|
||||
|
||||
class FetchData implements IAction {
|
||||
/** @var TomlConfig */
|
||||
private $settings;
|
||||
/** @var \SBRL\PerformanceCounter */
|
||||
private $perfcounter;
|
||||
|
||||
/** @var IMeasurementDataRepository */
|
||||
private $measurement_repo;
|
||||
|
@ -30,11 +32,13 @@ class FetchData implements IAction {
|
|||
TomlConfig $in_settings,
|
||||
IMeasurementDataRepository $in_measurement_repo,
|
||||
IMeasurementTypeRepository $in_type_repo,
|
||||
ApiResponseSender $in_sender) {
|
||||
ApiResponseSender $in_sender,
|
||||
\SBRL\PerformanceCounter $in_perfcounter) {
|
||||
$this->settings = $in_settings;
|
||||
$this->measurement_repo = $in_measurement_repo;
|
||||
$this->type_repo = $in_type_repo;
|
||||
$this->sender = $in_sender;
|
||||
$this->perfcounter = $in_perfcounter;
|
||||
|
||||
$this->validator = new Validator($_GET);
|
||||
}
|
||||
|
@ -42,7 +46,6 @@ class FetchData implements IAction {
|
|||
public function handle() : bool {
|
||||
global $start_time;
|
||||
|
||||
$start_handle = microtime(true);
|
||||
|
||||
// 1: Validate params
|
||||
$this->validator->is_datetime("datetime");
|
||||
|
@ -58,7 +61,7 @@ class FetchData implements IAction {
|
|||
if($measurement_type_id == null) {
|
||||
$this->sender->send_error_plain(
|
||||
400, "Error: That reading type is invalid.", [
|
||||
[ "x-time-taken", PerfFormatter::format_perf_data($start_time, $start_handle, null) ]
|
||||
[ "x-time-taken", $this->perfcounter->render() ]
|
||||
]
|
||||
);
|
||||
return false;
|
||||
|
@ -66,24 +69,26 @@ class FetchData implements IAction {
|
|||
|
||||
|
||||
// 2: Pull data from database
|
||||
$this->perfcounter->start("sql");
|
||||
$data = $this->measurement_repo->get_readings_by_date(
|
||||
new \DateTime($_GET["datetime"]),
|
||||
$measurement_type_id
|
||||
);
|
||||
$this->perfcounter->end("sql");
|
||||
|
||||
|
||||
// 2.5: Validate data from database
|
||||
if(empty($data)) {
|
||||
$this->sender->send_error_plain(404,
|
||||
"Error: No data could be found for that timestamp.",
|
||||
[ PerfFormatter::format_perf_data($start_time, $start_handle, null) ]
|
||||
[ [ "x-time-taken", $this->perfcounter->render() ] ]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 3: Serialise data
|
||||
$start_encode = microtime(true);
|
||||
$this->perfcounter->start("encode");
|
||||
$response_type = "application/octet-stream";
|
||||
$response_suggested_filename = "data-" . date(\DateTime::ATOM) . "";
|
||||
$response = null;
|
||||
|
@ -99,6 +104,7 @@ class FetchData implements IAction {
|
|||
$response = ResponseEncoder::encode_csv($data);
|
||||
break;
|
||||
}
|
||||
$this->perfcounter->end("encode");
|
||||
|
||||
|
||||
// 4: Send response
|
||||
|
@ -111,7 +117,7 @@ class FetchData implements IAction {
|
|||
header("content-type: $response_type");
|
||||
header("content-length: " . strlen($response));
|
||||
header("content-disposition: inline; filename=$response_suggested_filename");
|
||||
header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, $start_encode));
|
||||
header("x-time-taken: " . $this->perfcounter->render());
|
||||
echo($response);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -4,25 +4,27 @@ namespace AirQuality\Actions;
|
|||
|
||||
use \SBRL\TomlConfig;
|
||||
|
||||
use \AirQuality\PerfFormatter;
|
||||
|
||||
class Index implements IAction {
|
||||
/** @var TomlConfig */
|
||||
private $settings;
|
||||
/** @var \SBRL\PerformanceCounter */
|
||||
private $perfcounter;
|
||||
|
||||
/** @var \ParsedownExtra */
|
||||
private $parsedown_ext;
|
||||
|
||||
public function __construct(
|
||||
TomlConfig $in_settings) {
|
||||
TomlConfig $in_settings,
|
||||
\SBRL\PerformanceCounter $in_perfcounter) {
|
||||
$this->settings = $in_settings;
|
||||
$this->perfcounter = $in_perfcounter;
|
||||
}
|
||||
|
||||
public function handle() : bool {
|
||||
global $start_time;
|
||||
|
||||
$start_handle = microtime(true);
|
||||
|
||||
$this->perfcounter->start("handle");
|
||||
|
||||
// 1: Parse markdown
|
||||
$result = "Welcome to the Air Quality Web HTTP API!
|
||||
|
@ -38,7 +40,7 @@ Note that if you have deployed your own version of the air quality web interface
|
|||
// 2: Send response
|
||||
header("content-length: " . strlen($result));
|
||||
header("content-type: text/plain");
|
||||
header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, null));
|
||||
header("x-time-taken: " . $this->perfcounter->render());
|
||||
echo($result);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ use \SBRL\ResponseEncoder;
|
|||
use \AirQuality\Repositories\IDeviceRepository;
|
||||
use \AirQuality\ApiResponseSender;
|
||||
|
||||
use \AirQuality\PerfFormatter;
|
||||
|
||||
class ListDevices implements IAction {
|
||||
/** @var TomlConfig */
|
||||
private $settings;
|
||||
/** @var IDeviceRepository */
|
||||
private $device_repo;
|
||||
/** @var \SBRL\PerformanceCounter */
|
||||
private $perfcounter;
|
||||
|
||||
/** @var ApiResponseSender */
|
||||
private $sender;
|
||||
|
@ -21,16 +22,17 @@ class ListDevices implements IAction {
|
|||
public function __construct(
|
||||
TomlConfig $in_settings,
|
||||
IDeviceRepository $in_device_repo,
|
||||
ApiResponseSender $in_sender) {
|
||||
ApiResponseSender $in_sender,
|
||||
\SBRL\PerformanceCounter $in_perfcounter) {
|
||||
$this->settings = $in_settings;
|
||||
$this->device_repo = $in_device_repo;
|
||||
$this->sender = $in_sender;
|
||||
$this->perfcounter = $in_perfcounter;
|
||||
}
|
||||
|
||||
public function handle() : bool {
|
||||
global $start_time;
|
||||
|
||||
$start_handle = microtime(true);
|
||||
|
||||
// 1: Parse & validate parameters
|
||||
$only_with_location = !empty($_GET["only-with-location"]);
|
||||
|
@ -45,21 +47,23 @@ class ListDevices implements IAction {
|
|||
|
||||
|
||||
// 2: Pull data from database
|
||||
$this->perfcounter->start("sql");
|
||||
$data = $this->device_repo->get_all_devices($only_with_location);
|
||||
$this->perfcounter->end("sql");
|
||||
|
||||
|
||||
// 2.5: Validate data from database
|
||||
if(empty($data)) {
|
||||
$this->sender->send_error_plain(404,
|
||||
"Error: No devices are currently present in the system.",
|
||||
[ "x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, null) ]
|
||||
[ "x-time-taken: ", $this->perfcounter->render() ]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 3: Serialise data
|
||||
$start_encode = microtime(true);
|
||||
$this->perfcounter->start("encode");
|
||||
$response = null;
|
||||
$response_type = "application/octet-stream";
|
||||
$response_suggested_filename = "data-" . date(\DateTime::ATOM) . "";
|
||||
|
@ -75,6 +79,7 @@ class ListDevices implements IAction {
|
|||
$response = ResponseEncoder::encode_csv($data);
|
||||
break;
|
||||
}
|
||||
$this->perfcounter->end("encode");
|
||||
|
||||
// 4: Send response
|
||||
|
||||
|
@ -84,7 +89,7 @@ class ListDevices implements IAction {
|
|||
header("content-length: " . strlen($response));
|
||||
header("content-type: $response_type");
|
||||
header("content-disposition: inline; filename=$response_suggested_filename");
|
||||
header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, $start_encode));
|
||||
header("x-time-taken: " . $this->perfcounter->render());
|
||||
echo($response);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -7,11 +7,13 @@ use \SBRL\ResponseEncoder;
|
|||
use \AirQuality\Repositories\IMeasurementTypeRepository;
|
||||
use \AirQuality\ApiResponseSender;
|
||||
|
||||
use \AirQuality\PerfFormatter;
|
||||
|
||||
class ListReadingTypes implements IAction {
|
||||
/** @var TomlConfig */
|
||||
private $settings;
|
||||
/** @var \SBRL\PerformanceCounter */
|
||||
private $perfcounter;
|
||||
|
||||
/** @var IMeasurementTypeRepository */
|
||||
private $types_repo;
|
||||
|
||||
|
@ -21,16 +23,17 @@ class ListReadingTypes implements IAction {
|
|||
public function __construct(
|
||||
TomlConfig $in_settings,
|
||||
IMeasurementTypeRepository $in_types_repo,
|
||||
ApiResponseSender $in_sender) {
|
||||
ApiResponseSender $in_sender,
|
||||
\SBRL\PerformanceCounter $in_perfcounter) {
|
||||
$this->settings = $in_settings;
|
||||
$this->types_repo = $in_types_repo;
|
||||
$this->sender = $in_sender;
|
||||
$this->perfcounter = $in_perfcounter;
|
||||
}
|
||||
|
||||
public function handle() : bool {
|
||||
global $start_time;
|
||||
|
||||
$start_handle = microtime(true);
|
||||
|
||||
// 1: Parse & validate parameters
|
||||
$device_id = !empty($_GET["device-id"]) ? intval($_GET["device-id"]) : null;
|
||||
|
@ -45,10 +48,13 @@ class ListReadingTypes implements IAction {
|
|||
|
||||
// 1: Pull data from database
|
||||
$data = null;
|
||||
|
||||
$this->perfcounter->start("sql");
|
||||
if(!is_int($device_id))
|
||||
$data = $this->types_repo->get_all_types();
|
||||
else
|
||||
$data = $this->types_repo->get_types_by_device($device_id);
|
||||
$this->perfcounter->end("sql");
|
||||
|
||||
// 1.5: Validate data from database
|
||||
if(empty($data)) {
|
||||
|
@ -56,7 +62,7 @@ class ListReadingTypes implements IAction {
|
|||
}
|
||||
|
||||
// 3: Serialise data
|
||||
$start_encode = microtime(true);
|
||||
$this->perfcounter->start("encode");
|
||||
$response = null;
|
||||
$response_type = "application/octet-stream";
|
||||
$response_suggested_filename = "data-" . date(\DateTime::ATOM) . "";
|
||||
|
@ -72,6 +78,7 @@ class ListReadingTypes implements IAction {
|
|||
$response = ResponseEncoder::encode_csv($data);
|
||||
break;
|
||||
}
|
||||
$this->perfcounter->end("encode");
|
||||
|
||||
// 4: Send response
|
||||
|
||||
|
@ -81,7 +88,7 @@ class ListReadingTypes implements IAction {
|
|||
header("content-length: " . strlen($response));
|
||||
header("content-type: $response_type");
|
||||
header("content-disposition: inline; filename=$response_suggested_filename");
|
||||
header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, $start_encode));
|
||||
header("x-time-taken: " . $this->perfcounter->render());
|
||||
echo($response);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -4,23 +4,24 @@ namespace AirQuality\Actions;
|
|||
|
||||
use \SBRL\TomlConfig;
|
||||
|
||||
use \AirQuality\PerfFormatter;
|
||||
|
||||
class Version implements IAction {
|
||||
/** @var TomlConfig */
|
||||
private $settings;
|
||||
|
||||
/** @var \SBRL\PerformanceCounter */
|
||||
private $perfcounter;
|
||||
|
||||
public function __construct(
|
||||
TomlConfig $in_settings) {
|
||||
TomlConfig $in_settings,
|
||||
\SBRL\PerformanceCounter $in_perfcounter) {
|
||||
$this->settings = $in_settings;
|
||||
$this->perfcounter = $in_perfcounter;
|
||||
}
|
||||
|
||||
public function handle() : bool {
|
||||
global $start_time;
|
||||
|
||||
$start_handle = microtime(true);
|
||||
|
||||
|
||||
// 1: Parse markdown
|
||||
$result = file_get_contents(ROOT_DIR."version");
|
||||
|
@ -29,7 +30,7 @@ class Version implements IAction {
|
|||
// 2: Send response
|
||||
header("content-length: " . strlen($result));
|
||||
header("content-type: text/plain");
|
||||
header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, null));
|
||||
header("x-time-taken: " . $this->perfcounter->render());
|
||||
echo($result);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,16 @@ use Psr\Container\ContainerInterface;
|
|||
*/
|
||||
class Database
|
||||
{
|
||||
// Whether the database connection should be read only or not.
|
||||
// Defaults to true, as we don't need to write _anything_ to the database.
|
||||
/** @var TomlConfig */
|
||||
private $settings;
|
||||
/** @var \SBRL\PerformanceCounter */
|
||||
private $perfcounter;
|
||||
|
||||
/**
|
||||
* Whether the database connection should be read only or not.
|
||||
* Defaults to true, as we don't need to write _anything_ to the database.
|
||||
* @var bool
|
||||
*/
|
||||
private const read_only = true;
|
||||
private $connection;
|
||||
|
||||
|
@ -23,13 +31,15 @@ class Database
|
|||
\PDO::ATTR_AUTOCOMMIT => false
|
||||
];
|
||||
|
||||
function __construct(TomlConfig $in_settings) {
|
||||
function __construct(TomlConfig $in_settings, \SBRL\PerformanceCounter $in_perfcounter) {
|
||||
$this->settings = $in_settings;
|
||||
$this->perfcounter = $in_perfcounter;
|
||||
|
||||
$this->connect(); // Connect automagically
|
||||
}
|
||||
|
||||
public function connect() {
|
||||
$this->perfcounter->start("dbconnect");
|
||||
$this->connection = new \PDO(
|
||||
$this->get_connection_string(),
|
||||
$this->settings->get("database.username"),
|
||||
|
@ -39,6 +49,7 @@ class Database
|
|||
// Make this connection read-only
|
||||
if(self::read_only)
|
||||
$this->connection->query("SET SESSION TRANSACTION READ ONLY;");
|
||||
$this->perfcounter->end("dbconnect");
|
||||
}
|
||||
|
||||
public function query($sql, $variables = []) {
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace AirQuality;
|
||||
|
||||
/**
|
||||
* Formats performance data to be sent in a HTTP header.
|
||||
*/
|
||||
class PerfFormatter {
|
||||
function __construct() {
|
||||
|
||||
}
|
||||
|
||||
public static function format_perf_data($start_init, $start_handle, $start_encode) {
|
||||
$now = microtime(true);
|
||||
|
||||
$time_total = round(($now - $start_init) * 1000, 2);
|
||||
$time_setup = round(($start_handle - $start_init) * 1000, 2);
|
||||
$time_handle = round((($start_encode ?? $now) - $start_handle) * 1000, 2);
|
||||
$time_encode = round(($now - $start_encode) * 1000, 2);
|
||||
|
||||
$result = "{$time_total}ms total | {$time_setup}ms setup, {$time_handle}ms handle";
|
||||
if(!empty($start_encode))
|
||||
$result .= ", {$time_encode}ms encode";
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue