Commit a76ab4ad authored by Matthew Slater's avatar Matthew Slater

refactored account status object

parent dc68ca5f
......@@ -138,7 +138,6 @@ if (isset($active_node)) {
// Get the balances and times.
$name = $_POST['acc_name'];
$history = $ledgerRequester->print()->getHistory($name);
cc_log($history);
if (count($history) > 2) {
setResponse($history, 'green');
}
......
......@@ -371,7 +371,6 @@ class Node {
*/
function renderChart() : array {
$map = $this->getRequester('ledger')->accounts(TRUE, FALSE);
$id = "balance_map_$this->name";
return [
'<div id = "'.$id.'" style="width:24%"></div>',
......
......@@ -338,7 +338,7 @@ paths:
schema:
type: string
default: true
- name: deep
- name: tree
in: query
description: true to show whole ledger tree, false to show current node only
required: false
......
......@@ -96,10 +96,13 @@ function arg(int $index) {
if (!isset($args)) {
$args = array_values(explode('/', $uri));
}
// Ensure $arg[0] is always empty
if ($index > 0) {
return $args[$index] ?? NULL;
}
//$arg[1] is always the endpoint
elseif ($index == -1) {
//Provide additional args after the endpoint
return array_slice($args, 2);
}
else {
......
<?php
class AccountStatus {
private $account;
private $data;
public $gross_in;
public $gross_out;
public $balance;
public $trades;
public $entries;
public $partners_local;
//public $partnersGlobal;
//public $balancePending;
function __construct(LedgerAccount $account, array $tx_data = []) {
$this->account = $account;
$this->data = $tx_data;
$this->balance = $this->getFormatted('balance');
$this->gross_in = $this->getFormatted('gross_in');
$this->gross_out = $this->getFormatted('gross_out');
$this->trades = $this->getFormatted('trades');
$this->entries = $this->getFormatted('entries');
$this->partners_local = $this->getFormatted('partners_local');
}
function __get($name) {
if (isset($this->account->{$val})) {
return $this->account->{$val};
}
trigger_error("Requested bad property '$name' from AccountStatus", E_USER_WARNING);
}
function getFormatted($field) {
global $config;
return round($this->data[$field]??0, $config['decimal_places']);
}
function getType() {
return get_class($this->account);
}
}
......@@ -5,6 +5,10 @@
*/
class LedgerAccount {
//Also possible: partners_global
const TRADE_STATS = ['entries', 'partners_local', 'trades', 'gross_in', 'gross_out', 'balance', 'volume'];
/**
* The name which would be written in the ledger
* @var string
......@@ -148,6 +152,7 @@ class LedgerAccount {
* Balances keyed by timestamp, oldest first
*/
function getHistory($samples = 0) : array {
cc_log($this->localName);
$points[date("Y-m-d H:i:s", $this->created)] = 0;
$points += $this->_getHistory();
if ($samples === 0){
......@@ -201,7 +206,7 @@ class LedgerAccount {
* @return array
* Two groups of stats, with keys 'completed' and 'pending'.
*/
function getInfo() : array {
function getTradeStats() : array {
$query = "SELECT uid2, income, expenditure, diff, volume, state, is_primary as isPrimary "
. "FROM transaction_index "
. "WHERE uid1 = '$this->localName'";
......@@ -244,95 +249,94 @@ class LedgerAccount {
return [$this->relative, $stats];
}
/**
* @param bool $details
* @return array
*
*/
protected static function getAllActiveAccounts(bool $details = TRUE) : array {
$accounts = [];
$all_active_policies = PolicyRequester::create()->filter(['status' => TRUE], $details?'full':'nameonly');
if ($details) {
foreach ($all_active_policies as $policy) {
$accounts[$policy->name] = static::create($policy->name, TRUE);//from policyrequester
}
}
else {
$accounts = $all_active_policies;
}
return $accounts;
}
/**
*
* @param bool $details
* @return array
* the policies or Sccountstatuses
*
*/
static function getAllActiveAccountInfo(bool $details = TRUE) : array {
require './AccountStatus.php';
$all_accounts = static::getAllActiveAccounts($details);
static function getAllTradeStats(bool $details = FALSE) : array {
$all_account_names = PolicyRequester::create()->filter(['status' => TRUE], 'nameonly');
$results = [];
foreach (static::TRADE_STATS as $stat) {
$default[$stat] = 0;
}
if ($details) {
// NB this is only the balances of accounts which have traded.
$all_balances = static::getAllBalances();
$results = [];
foreach ($all_accounts as $account) {
$name = $account->localName;
$data = $all_balances[$name]['completed'] ?? [];
$results[$name] = new AccountStatus($account, $data);
$all_balances = static::_getAllTradeStats();
foreach ($all_account_names as $name) {
$results[$name] = $all_balances[$name]['completed'] ?? (object)$default;
}
}
else {
$results = $all_accounts;
$results = $all_account_names;
}
return $results;
}
/**
* @return array
*
* @todo move to static function LedgerAccount::getAllBalances
*/
private static function getAllBalances() : array {
private static function _getAllTradeStats() : array {
$balances = [];
$result = Db::query("SELECT uid1, uid2, income, expenditure, diff, volume, state, is_primary FROM transaction_index WHERE income > 0");
while ($row = $result->fetch_object()) {
$balances[$row->uid1]['pending']['gross_in'][] = $row->income;
$balances[$row->uid2]['pending']['gross_out'][] = $row->income;
$balances[$row->uid1]['pending']->gross_in[] = $row->income;
$balances[$row->uid2]['pending']->gross_out[] = $row->income;
if ($row->is_primary) {
$balances[$row->uid1]['pending']['partners_local'][] = $row->uid2;
$balances[$row->uid2]['pending']['partners_local'][] = $row->uid1;
$balances[$row->uid1]['pending']['trades'][] = 1;
$balances[$row->uid2]['pending']['trades'][] = 1;
$balances[$row->uid1]['pending']->partners_local[] = $row->uid2;
$balances[$row->uid2]['pending']->partners_local[] = $row->uid1;
$balances[$row->uid1]['pending']->trades[] = 1;
$balances[$row->uid2]['pending']->trades[] = 1;
}
if ($row->state == 'completed') {
$balances[$row->uid1]['completed']['gross_in'][] = $row->income;
$balances[$row->uid2]['completed']['gross_out'][] = $row->income;
$balances[$row->uid1]['completed']->gross_in[] = $row->income;
$balances[$row->uid2]['completed']->gross_out[] = $row->income;
if ($row->is_primary) {
$balances[$row->uid1]['completed']['partners_local'][] = $row->uid2;
$balances[$row->uid2]['completed']['partners_local'][] = $row->uid1;
$balances[$row->uid1]['completed']['trades'][] = 1;
$balances[$row->uid2]['completed']['trades'][] = 1;
$balances[$row->uid1]['completed']->partners_local[] = $row->uid2;
$balances[$row->uid2]['completed']->partners_local[] = $row->uid1;
$balances[$row->uid1]['completed']->trades[] = 1;
$balances[$row->uid2]['completed']->trades[] = 1;
}
}
}
foreach ($balances as &$states) {
foreach ($states as &$data) {
$data['entries'] = count($data['partners']);
$data['partners_local'] = count(array_unique($data['partners_local']));
$data['trades'] = count($data['trades']);
$data['gross_in'] = array_sum((array)$data['gross_in']);
$data['gross_out'] = array_sum((array)$data['gross_out']);
$data['balance'] = $data['gross_in'] - $data['gross_out'];
$data['volume'] = $data['gross_in'] + $data['gross_out'];
foreach (static::TRADE_STATS as $stat) {
switch ($stat) {
case 'entries':
$val = count($data->partners_local);
break;
case 'partners_local':
$val = count(array_unique($data->partners_local));
break;
case 'trades':
$val = count($data->trades);
break;
case 'gross_in':
$val = $this->getFormatted(array_sum($data->gross_in));
break;
case 'gross_out':
$val = $this->getFormatted(array_sum($data->gross_out));
break;
case 'balance':
$val = $this->getFormatted($data->gross_in - $data->gross_out);
break;
case 'volume':
$val = $this->getFormatted($data->gross_in + $data->gross_out);
break;
}
$data[$stat] = $val;
}
}
}
return $balances;
}
private function getFormatted($num) {
global $config;
return round($this->data[$field]??0, $config['decimal_places']);
}
/**
* Resolve a path to an account on the current node.
* @return stdClass
......
......@@ -208,7 +208,7 @@ class Transaction implements TransactionInterface, JsonSerializable {
}
foreach ($this->sum() as $localName => $info) {
$account = LedgerAccount::create($localName, FALSE);
$ledgerAccountInfo = $account->getInfo();
$ledgerAccountInfo = $account->getTradeStats();
$projected = $ledgerAccountInfo['pending']['balance'] + $info->diff;
if ($projected > $this->payee->max) {
cc_violation(MaxLimitViolation::create($localName, $this->payee->max, $projected));
......
......@@ -55,27 +55,28 @@ abstract class LedgerAccountRemote extends LedgerAccount {
/**
* {@inheritDoc}
*/
function getInfo() : array {
function getTradeStats() : array {
global $orientation;
if ($orientation->downstreamAccount) {
// Branchward nodes may not grant permission
return LedgerRequester::create($orientation->downstreamAccount)
->getStats($this->givenPath);
}
return parent::getInfo();
return parent::getTradeStats();
}
/**
* {@inheritDoc}
*/
static function getAllActiveAccountInfo(bool $details = TRUE) : array {
static function getAllTradeStats(bool $details = TRUE) : array {
global $orientation;
$all_accounts = parent::getAllActiveAccountInfo($details);
$all_accounts = parent::getAllTradeStats($details);
// This function is only ever called from index.php where the ledger has
// been oriented to root.
$map = $orientation->getDownstreamRequester()->accounts($details, TRUE);
$downstreamAccountName = $orientation->downstreamAccount->localName;
if ($details) {
$all_accounts[$downstreamAccountName]->parents = $map;
}
......
......@@ -26,12 +26,6 @@ ledger_journal(join("\n", $headers));
authenticateUpstream($headers, $_SERVER['REQUEST_URI']);
if ($method == 'GET') {
//Pretty clunky this needs explaining if not refactoring.
$args = arg(-1);
}
// READ FUNCTIONS
if ($endpoint == '') {
cc_ledger_response(200, $orientation->handshake());
......@@ -61,24 +55,29 @@ if ($method == 'GET') {
}
elseif($endpoint == 'accounts') {
parse_str($_SERVER['QUERY_STRING'], $params);
if ($params['deep'] and $orientation->orientToRoot()) {
$all = LedgerAccountRemote::getAllActiveAccountInfo($params['details']??1);
if ($params['tree'] and $orientation->orientToRoot()) {
$class = 'LedgerAccountRemote';
}
else {
$all = LedgerAccount::getAllActiveAccountInfo($params['details']??1);
$class = 'LedgerAccount';
}
cc_response(200, $all);
cc_response(200, $class::getAllTradeStats((bool)$params['details']));
}
elseif ($endpoint == 'account' or $endpoint == 'history') {
$given_path = implode('/',arg(-1));
$account = LedgerAccount::create($given_path, FALSE);
elseif ($endpoint == 'account') {
$params = arg(-1);
// This account could be remote.
if ($endpoint == 'account') {
cc_response(200, $account->getInfo());
}
elseif ($endpoint == 'history') {
cc_response(200, $account->getHistory(arg(3)));
$account = LedgerAccount::create(implode('/', $params), FALSE);
cc_response(200, $account->getTradeStats());
}
elseif ($endpoint == 'history') {
$params = arg(-1);
$samples = 0;
if (is_numeric(end($params))) {
$samples = array_pop($params);
}
$account = LedgerAccount::create(implode('/', $params), FALSE);
cc_response(200, $account->getHistory($samples));
}
}
// Create a new transaction
......
......@@ -77,7 +77,7 @@ class LedgerRequester extends BaseRequester {
*/
function buildValidateTransaction(Transaction $transaction) {
global $orientation;
if ($this->downstreamAccount->localName == $orientation->upstreamAccount->localName) {
if ($orientation->upstreamAccount and $this->downstreamAccount->localName == $orientation->upstreamAccount->localName) {
// This happened frequently during development causing a lot of timeouts and memory errors.
cc_failure(MiscFailure::create('Attempted to request upstream.'));
}
......@@ -153,11 +153,11 @@ class LedgerRequester extends BaseRequester {
* @param bool $deep
* @return array
*/
function accounts(bool $details = TRUE, bool $deep = FALSE) : array {
function accounts(bool $details = TRUE, bool $tree = FALSE) : array {
list($code, $result) = $this
->accept(200)
->addQueryParam('details', $details)
->addQueryParam('deep', $deep)
->addQueryParam('tree', $tree)
->request('accounts');
return (array)$result;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment