WIP server side.

This commit is contained in:
Ahmed Bouhuolia
2020-01-22 02:09:45 +02:00
parent de905d7e7c
commit 488709088b
123 changed files with 14885 additions and 771 deletions

View File

@@ -0,0 +1,76 @@
export default class BudgetEntriesSet {
constructor() {
this.accounts = {};
this.totalSummary = {}
this.orderSize = null;
}
setZeroPlaceholder() {
if (!this.orderSize) { return; }
Object.values(this.accounts).forEach((account) => {
for (let i = 0; i <= this.orderSize.length; i++) {
if (typeof account[i] === 'undefined') {
account[i] = { amount: 0 };
}
}
});
}
static from(accounts, configs) {
const collection = new this(configs);
accounts.forEach((entry) => {
if (typeof this.accounts[entry.accountId] === 'undefined') {
collection.accounts[entry.accountId] = {};
}
if (entry.order) {
collection.accounts[entry.accountId][entry.order] = entry;
}
});
return collection;
}
toArray() {
const output = [];
Object.key(this.accounts).forEach((accountId) => {
const entries = this.accounts[accountId];
output.push({
account_id: accountId,
entries: [
...Object.key(entries).map((order) => {
const entry = entries[order];
return {
order,
amount: entry.amount,
};
}),
],
});
});
}
calcTotalSummary() {
const totalSummary = {};
for (let i = 0; i < this.orderSize.length; i++) {
Object.value(this.accounts).forEach((account) => {
if (typeof totalSummary[i] !== 'undefined') {
totalSummary[i] = { amount: 0, order: i };
}
totalSummary[i].amount += account[i].amount;
});
}
this.totalSummary = totalSummary;
}
toArrayTotalSummary() {
return Object.values(this.totalSummary);
}
}

View File

View File

@@ -0,0 +1,279 @@
export default {
METADATA_GROUP: 'default',
KEY_COLUMN: 'key',
VALUE_COLUMN: 'value',
TYPE_COLUMN: 'type',
extraColumns: [],
metadata: [],
shouldReload: true,
extraMetadataQuery: () => {},
/**
* Set the value column key to query from.
* @param {String} name -
*/
setKeyColumnName(name) {
this.KEY_COLUMN = name;
},
/**
* Set the key column name to query from.
* @param {String} name -
*/
setValueColumnName(name) {
this.VALUE_COLUMN = name;
},
/**
* Set extra columns to be added to the rows.
* @param {Array} columns -
*/
setExtraColumns(columns) {
this.extraColumns = columns;
},
/**
* Metadata database query.
* @param {Object} query -
* @param {String} groupName -
*/
whereQuery(query, key) {
const groupName = this.METADATA_GROUP;
if (groupName) {
query.where('group', groupName);
}
if (key) {
if (Array.isArray(key)) {
query.whereIn('key', key);
} else {
query.where('key', key);
}
}
},
/**
* Loads the metadata from the storage.
* @param {String|Array} key -
* @param {Boolean} force -
*/
async load(force = false) {
if (this.shouldReload || force) {
const metadataCollection = await this.query((query) => {
this.whereQuery(query);
this.extraMetadataQuery(query);
}).fetchAll();
this.shouldReload = false;
this.metadata = [];
const metadataArray = this.mapMetadataCollection(metadataCollection);
metadataArray.forEach((metadata) => { this.metadata.push(metadata); });
}
},
/**
* Fetches all the metadata that associate with the current group.
*/
async allMeta(force = false) {
await this.load(force);
return this.metadata;
},
/**
* Find the given metadata key.
* @param {String} key -
* @return {object} - Metadata object.
*/
findMeta(key) {
return this.metadata.find((meta) => meta.key === key);
},
/**
* Fetch the metadata of the current group.
* @param {*} key -
*/
async getMeta(key, defaultValue, force = false) {
await this.load(force);
const metadata = this.findMeta(key);
return metadata ? metadata.value : defaultValue || false;
},
/**
* Markes the metadata to should be deleted.
* @param {String} key -
*/
async removeMeta(key) {
await this.load();
const metadata = this.findMeta(key);
if (metadata) {
metadata.markAsDeleted = true;
}
this.shouldReload = true;
/**
* Remove all meta data of the given group.
* @param {*} group
*/
removeAllMeta(group = 'default') {
this.metdata.map((meta) => ({
...(meta.group !== group) ? { markAsDeleted: true } : {},
...meta,
}));
this.shouldReload = true;
},
/**
* Set the meta data to the stack.
* @param {String} key -
* @param {String} value -
*/
async setMeta(key, value, payload) {
if (Array.isArray(key)) {
const metadata = key;
metadata.forEach((meta) => {
this.setMeta(meta.key, meta.value);
});
return;
}
await this.load();
const metadata = this.findMeta(key);
if (metadata) {
metadata.value = value;
metadata.markAsUpdated = true;
} else {
this.metadata.push({
value, key, ...payload, markAsInserted: true,
});
}
},
/**
* Saved the modified metadata.
*/
async saveMeta() {
const inserted = this.metadata.filter((m) => (m.markAsInserted === true));
const updated = this.metadata.filter((m) => (m.markAsUpdated === true));
const deleted = this.metadata.filter((m) => (m.markAsDeleted === true));
const metadataDeletedKeys = deleted.map((m) => m.key);
const metadataInserted = inserted.map((m) => this.mapMetadata(m, 'format'));
const metadataUpdated = updated.map((m) => this.mapMetadata(m, 'format'));
const batchUpdate = (collection) => knex.transaction((trx) => {
const queries = collection.map((tuple) => {
const query = knex(this.tableName);
this.whereQuery(query, tuple.key);
this.extraMetadataQuery(query);
return query.update(tuple).transacting(trx);
});
return Promise.all(queries).then(trx.commit).catch(trx.rollback);
});
await Promise.all([
knex.insert(metadataInserted).into(this.tableName),
batchUpdate(metadataUpdated),
metadataDeletedKeys.length > 0
? this.query('whereIn', this.KEY_COLUMN, metadataDeletedKeys).destroy({
require: true,
}) : null,
]);
this.shouldReload = true;
},
/**
* Purge all the cached metadata in the memory.
*/
purgeMetadata() {
this.metadata = [];
this.shouldReload = true;
},
/**
* Parses the metadata value.
* @param {String} value -
* @param {String} valueType -
*/
parseMetaValue(value, valueType) {
let parsedValue;
switch (valueType) {
case 'integer':
parsedValue = parseInt(value, 10);
break;
case 'float':
parsedValue = parseFloat(value);
break;
case 'boolean':
parsedValue = Boolean(value);
break;
case 'json':
parsedValue = JSON.parse(parsedValue);
break;
default:
parsedValue = value;
break;
}
return parsedValue;
},
/**
* Format the metadata before saving to the database.
* @param {String|Number|Boolean} value -
* @param {String} valueType -
* @return {String|Number|Boolean} -
*/
formatMetaValue(value, valueType) {
let parsedValue;
switch (valueType) {
case 'number':
parsedValue = `${value}`;
break;
case 'boolean':
parsedValue = value ? '1' : '0';
break;
case 'json':
parsedValue = JSON.stringify(parsedValue);
break;
default:
parsedValue = value;
break;
}
return parsedValue;
},
mapMetadata(attr, parseType = 'parse') {
return {
key: attr[this.KEY_COLUMN],
value: (parseType === 'parse')
? this.parseMetaValue(
attr[this.VALUE_COLUMN],
this.TYPE_COLUMN ? attr[this.TYPE_COLUMN] : false,
)
: this.formatMetaValue(
attr[this.VALUE_COLUMN],
this.TYPE_COLUMN ? attr[this.TYPE_COLUMN] : false,
),
...this.extraColumns.map((extraCol) => ({
[extraCol]: attr[extraCol] || null,
})),
};
},
/**
* Parse the metadata collection.
* @param {Array} collection -
*/
mapMetadataCollection(collection, parseType = 'parse') {
return collection.map((model) => this.mapMetadata(model.attributes, parseType));
},
};

View File

@@ -0,0 +1,73 @@
export default class NestedSet {
/**
* Constructor method.
* @param {Object} options -
*/
constructor(items, options) {
this.options = {
parentId: 'parent_id',
id: 'id',
...options,
};
this.items = items;
this.collection = {};
this.toTree();
return this.collection;
}
/**
* Link nodes children.
*/
linkChildren() {
if (this.items.length <= 0) return false;
const map = {};
this.items.forEach((item) => {
map[item.id] = item;
map[item.id].children = [];
});
this.items.forEach((item) => {
const parentNodeId = item[this.options.parentId];
if (parentNodeId) {
map[parentNodeId].children.push(item);
}
});
return map;
}
toTree() {
const map = this.linkChildren();
const tree = {};
this.items.forEach((item) => {
const parentNodeId = item[this.options.parentId];
if (!parentNodeId) {
tree[item.id] = map[item.id];
}
});
this.collection = Object.values(tree);
}
walk() {
}
getParents() {
}
getChildren() {
}
toFlattenArray() {
}
toArray() {
}
}