<commit_before>var Vis = require('vis');
var remote = require('remote');
var dialog = remote.require('dialog');
var fs = require('fs');

var network;
var inc = 0;

$(document).ready(function () {
    $('#submit-btn').click(function () {
	var obj = getBFSPath(network.body.data.nodes.get()[0]);
	markAllNodesAsUnvisited(obj.path);
	bfsRootAnimation(obj.path);
	rootCodeLineAnimation();
	bfsNodesAnimation(obj.path, obj.iter);
    });
});

// create a network
var container = $('#tree-simple')[0];

var options = {
    autoResize: true,
    manipulation: {
	enabled: true,
	initiallyActive: true,
	addNode: function (nodeData, callback) {
	    if (network.body.nodes === {}) {
		inc = 1;
	    }
	    else {
		inc++;
	    }
	    nodeData.label = inc;
	    nodeData.color = '#009688';
	    nodeData.font = {
		color: '#fff'
	    };
	    nodeData.visited = false;
	    nodeData.adjacencyList = [];
	    nodeData.predecessor = null;
	    callback(nodeData);
	},
	editNode: function (nodeData, callback) {
	    nodeData.root = true;
	    nodeData.color = '#3f51b5';
	    callback(nodeData);
	},
	addEdge: function (edgeData,callback) {
	    var fromNode = network.body.data.nodes.get().filter(function (x) {
		return x.id === edgeData.from;}
	    );
	    var toNode = network.body.data.nodes.get().filter(function (x) {
		return x.id === edgeData.to;}
	    );
	    fromNode[0].adjacencyList.push(toNode[0]);
		if (edgeData.from === edgeData.to) {
		    var r = confirm('Do you want to connect the node to itself?');
		    if (r === true) {
			callback(edgeData);
		    }
		}
		else {
		    callback(edgeData);
		}
	}
    },
    interaction: {
	navigationButtons: true
    },
    physics: {
	enabled: false
    }
};

network = new Vis.Network(container, [], options);

function bfsRootAnimation(path) {
    var root = path[0];
    for (var index = 1; index < 3; index++) {
	(function (ind) {
	    setTimeout(function () {
		if (ind === 1) {
		    root.visited = true;
		    root.color = '#3f51b5';
		    network = rebuildNetwork(path);
		}
		else {
		    appendToQueue(root.label);
		}
	    }, (1000 * ind));
	})(index);
    }
}

function bfsNodesAnimation(path, iter) {
    var queue = [path[0]];
    for (var index = 0; index < iter; index++) {
	(function (ind) {
	    setTimeout(function () {
		var u = queue.pop();
		removeFromQueue();
		var adjacencyList = u.adjacencyList;
		for (var index1 = 0; index1 < adjacencyList.length; index1++) {
		    (function (ind1) {
			setTimeout(function () {
			    if (!adjacencyList[ind1].visited) {
				adjacencyList[ind1].visited = true;
				adjacencyList[ind1].color = '#3f51b5';
				adjacencyList[ind1].predecessor = u;
				network = rebuildNetwork(path);
				queue.push(adjacencyList[ind1]);
				appendToQueue(adjacencyList[ind1].label);
			    }
			}, (3000 + (4000 * ind) + (ind1 * (parseFloat(4000) / adjacencyList.length))));
		    })(index1);
		}
	    }, (3000 + (4000 * ind)));
	})(index);
    }
}

function getBFSPath(root) {
    var queue = [];
    var numberOfQueueIterations = 0;
    var path = [root];
    queue.push(root);
    while (queue.length > 0) {
	var u = queue.pop();
	var adjacencyList = u.adjacencyList;
	for (var i = 0; i < adjacencyList.length; i++) {
	    if (!adjacencyList[i].visited) {
		adjacencyList[i].visited = true;
		adjacencyList[i].predecessor = u;
		queue.push(adjacencyList[i]);
		path.push(adjacencyList[i]);
	    }
	}
	numberOfQueueIterations++;
    }
    return {path: path, iter: numberOfQueueIterations};
}

function rootCodeLineAnimation() {
    for (var index1 = 0; index1 < 3; index1++) {
	(function (ind1) {
	    setTimeout(function () {
		unHighlightCodeLine(ind1 - 1);
		highlightCodeLine(ind1);
	    }, (1000 * ind1));
	})(index1);
    }
}

function bfsNodesCodeLineAnimation(iter) {
    for (var index = 0; index < iter; index++) {
	(function (ind) {
	    setTimeout(function () {
		removeFromQueue();
		var adjacencyList = u.adjacencyList;
		for (var index1 = 0; index1 < adjacencyList.length; index1++) {
		    (function (ind1) {
			setTimeout(function () {
			    if (!adjacencyList[ind1].visited) {
				adjacencyList[ind1].visited = true;
				adjacencyList[ind1].color = '#3f51b5';
				adjacencyList[ind1].predecessor = u;
				network = rebuildNetwork(path);
				queue.push(adjacencyList[ind1]);
				appendToQueue(adjacencyList[ind1].label);
			    }
			}, (3000 + (4000 * ind) + (ind1 * (parseFloat(4000) / adjacencyList.length))));
		    })(index1);
		}
	    }, (3000 + (4000 * ind)));
	})(index);
    }
}

function highlightCodeLine(number) {
    $('#line-' + number).css('color', 'red');
}

function unHighlightCodeLine(number) {
    $('#line-' + number).css('color', '#3f51b5');
}

function appendToQueue(text) {
    var th = '' + text + '	';
    $('#queue-row').append(th);
}

function removeFromQueue() {
    $('#queue-row').find('th:last').remove();
}

function markAllNodesAsUnvisited(path) {
    for (var i = 0; i < path.length; i++) {
	path[i].visited = false;
    }
}

function createAlert(alertText) {
    var alert = "
 <button type='button' class='close' data-dismiss='alert'> x </button> Oh snap! " + alertText + '
';
    return alert;
}

function rebuildNetwork(nodes) {
    var data = {
	nodes: nodes,
	edges: network.body.data.edges
    };

    network.destroy();
    network = new Vis.Network(container, data, options);
    return network;
}
<commit_msg>Reverted the state of the animation... Still queue cannot allow more than one element at the same time<commit_after>var Vis = require('vis');
var remote = require('remote');
var dialog = remote.require('dialog');
var fs = require('fs');

var network;
var inc = 0;

$(document).ready(function () {
    $('#submit-btn').click(function () {
	var obj = getBFSPath(network.body.data.nodes.get()[0]);
	markAllNodesAsUnvisited(obj.path);
	bfsRootAnimation(obj.path);
	rootCodeLineAnimation();
	bfsNodesAnimation(obj.path, obj.iter);
    });
});

// create a network
var container = $('#tree-simple')[0];

var options = {
    autoResize: true,
    manipulation: {
	enabled: true,
	initiallyActive: true,
	addNode: function (nodeData, callback) {
	    if (network.body.nodes === {}) {
		inc = 1;
	    }
	    else {
		inc++;
	    }
	    nodeData.label = inc;
	    nodeData.color = '#009688';
	    nodeData.font = {
		color: '#fff'
	    };
	    nodeData.visited = false;
	    nodeData.adjacencyList = [];
	    nodeData.predecessor = null;
	    callback(nodeData);
	},
	editNode: function (nodeData, callback) {
	    nodeData.root = true;
	    nodeData.color = '#3f51b5';
	    callback(nodeData);
	},
	addEdge: function (edgeData,callback) {
	    var fromNode = network.body.data.nodes.get().filter(function (x) {
		return x.id === edgeData.from;}
	    );
	    var toNode = network.body.data.nodes.get().filter(function (x) {
		return x.id === edgeData.to;}
	    );
	    fromNode[0].adjacencyList.push(toNode[0]);
		if (edgeData.from === edgeData.to) {
		    var r = confirm('Do you want to connect the node to itself?');
		    if (r === true) {
			callback(edgeData);
		    }
		}
		else {
		    callback(edgeData);
		}
	}
    },
    interaction: {
	navigationButtons: true
    },
    physics: {
	enabled: false
    }
};

network = new Vis.Network(container, [], options);

function bfsRootAnimation(path) {
    var root = path[0];
    for (var index = 1; index < 3; index++) {
	(function (ind) {
	    setTimeout(function () {
		if (ind === 1) {
		    root.visited = true;
		    root.color = '#3f51b5';
		    network = rebuildNetwork(path);
		}
		else {
		    appendToQueue(root.label);
		}
	    }, (1000 * ind));
	})(index);
    }
}

function bfsNodesAnimation(path, iter) {
    var queue = [path[0]];
    for (var index = 0; index < iter; index++) {
	(function (ind) {
	    setTimeout(function () {
		var u = queue.pop();
		removeFromQueue();
		var adjacencyList = u.adjacencyList;
		for (var index1 = 0; index1 < adjacencyList.length; index1++) {
		    (function (ind1) {
			setTimeout(function () {
			    if (!adjacencyList[ind1].visited) {
				adjacencyList[ind1].visited = true;
				adjacencyList[ind1].color = '#3f51b5';
				adjacencyList[ind1].predecessor = u;
				network = rebuildNetwork(path);
				queue.push(adjacencyList[ind1]);
				console.log("queue len: " + queue.length);
				appendToQueue(adjacencyList[ind1].label);
			    }
			}, (3000 + (4000 * ind) + (ind1 * (parseFloat(4000) / adjacencyList.length))));
		    })(index1);
		}
	    }, (3000 + (4000 * ind)));
	})(index);
    }
}

function getBFSPath(root) {
    var queue = [];
    var numberOfQueueIterations = 0;
    var path = [root];
    queue.push(root);
    while (queue.length > 0) {
	var u = queue.pop();
	var adjacencyList = u.adjacencyList;
	for (var i = 0; i < adjacencyList.length; i++) {
	    if (!adjacencyList[i].visited) {
		adjacencyList[i].visited = true;
		adjacencyList[i].predecessor = u;
		queue.push(adjacencyList[i]);
		path.push(adjacencyList[i]);
	    }
	}
	numberOfQueueIterations++;
    }
    return {path: path, iter: numberOfQueueIterations};
}

function rootCodeLineAnimation() {
    for (var index1 = 0; index1 < 3; index1++) {
	(function (ind1) {
	    setTimeout(function () {
		unHighlightCodeLine(ind1 - 1);
		highlightCodeLine(ind1);
	    }, (1000 * ind1));
	})(index1);
    }
}

function bfsNodesCodeLineAnimation(iter) {
    for (var index = 0; index < iter; index++) {
	(function (ind) {
	    setTimeout(function () {
		removeFromQueue();
		var adjacencyList = u.adjacencyList;
		for (var index1 = 0; index1 < adjacencyList.length; index1++) {
		    (function (ind1) {
			setTimeout(function () {
			    if (!adjacencyList[ind1].visited) {
				adjacencyList[ind1].visited = true;
				adjacencyList[ind1].color = '#3f51b5';
				adjacencyList[ind1].predecessor = u;
				network = rebuildNetwork(path);
				queue.push(adjacencyList[ind1]);
				appendToQueue(adjacencyList[ind1].label);
			    }
			}, (3000 + (4000 * ind) + (ind1 * (parseFloat(4000) / adjacencyList.length))));
		    })(index1);
		}
	    }, (3000 + (4000 * ind)));
	})(index);
    }
}

function highlightCodeLine(number) {
    $('#line-' + number).css('color', 'red');
}

function unHighlightCodeLine(number) {
    $('#line-' + number).css('color', '#3f51b5');
}

function appendToQueue(text) {
    var th = '' + text + '	';
    $('#queue-row').append(th);
}

function removeFromQueue() {
    $('#queue-row').find('th:last').remove();
}

function markAllNodesAsUnvisited(path) {
    for (var i = 0; i < path.length; i++) {
	path[i].visited = false;
    }
}

function createAlert(alertText) {
    var alert = "
 <button type='button' class='close' data-dismiss='alert'> x </button> Oh snap! " + alertText + '
';
    return alert;
}

function rebuildNetwork(nodes) {
    var data = {
	nodes: nodes,
	edges: network.body.data.edges
    };

    network.destroy();
    network = new Vis.Network(container, data, options);
    return network;
}
<|endoftext|>


<commit_before>'use strict';

// ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { ExchangeError, ArgumentsRequired, BadRequest, InsufficientFunds, OrderNotFound } = require ('./base/errors');
const { TICK_SIZE } = require ('./base/functions/number');

// ---------------------------------------------------------------------------

module.exports = class dsx extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'dsx',
            'name': 'DSX',
            'countries': [ 'UK' ],
            'rateLimit': 100,
            'has': {
                'CORS': false,
                'cancelOrder': true,
                'createLimitOrder': true,
                'createMarketOrder': false,
                'createOrder': true,
                'fetchBalance': true,
                'fetchCurrencies': true,
                'fetchL2OrderBook': true,
                'fetchMarkets': true,
                'fetchOrderBook': true,
                'fetchTicker': false,
                'fetchTickers': true,
                'fetchTrades': false,
                'fetchMyTrades': true,
                'fetchDepositAddress': true,
                'fetchOrder': true,
                'fetchOrders': true,
                'fetchOpenOrders': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/51840849/76909626-cb2bb100-68bc-11ea-99e0-28ba54f04792.jpg',
                'api': {
                    'public': 'https://api.dsxglobal.com/api/2/public',
                    'private': 'https://api.dsxglobal.com/api/2',
                },
                'www': 'http://dsxglobal.com',
                'doc': [
                    'https://api.dsxglobal.com',
                ],
            },
            'fees': {
                'trading': {
                    'tierBased': true,
                    'percentage': true,
                    'maker': 0.15 / 100,
                    'taker': 0.25 / 100,
                },
            },
            'timeframes': {
                '1m': 'M1',
                '3m': 'M3',
                '5m': 'M5',
                '15m': 'M15',
                '30m': 'M30',
                '1h': 'H1',
                '4h': 'H4',
                '1d': 'D1',
                '1w': 'D7',
                '1M': '1M',
            },
            'api': {
                // market data (public)
                'public': {
                    'get': [
                        'currency',
                        'currency/{currency}',
                        'symbol',
                        'symbol/{symbol}',
                        'ticker',
                        'ticker/{symbol}',
                        'trades',
                        'trades/{symbol}',
                        'orderbook',
                        'orderbook/{symbol}',
                        'candles',
                        'candles/{symbol}',
                    ],
                },
                // trading (private)
                'private': {
                    'get': [
                        'trading/balance',
                        'account/balance',
                        'account/crypto/address/{currency}',
                        'account/crypto/is-mine/{address}',
                        'account/transactions',
                        'account/transactions/{id}',
                        'sub-acc/balance/{subAccountUserID}',
                        'sub-acc',
                        'sub-acc/acl',
                        'trading/fee/{symbol}',
                        'history/order',
                        'history/trades',
                        'history/order/{orderId}/trades',
                        'order',
                        'order/{clientOrderId}',
                    ],
                    'post': [
                        'order',
                        'account/crypto/address/{currency}',
                        'account/crypto/withdraw',
                        'account/crypto/transfer-convert',
                        'account/transfer',
                        'sub-acc/freeze',
                        'sub-acc/activate',
                        'sub-acc/transfer',
                    ],
                    'put': [
                        'order/{clientOrderId}',
                        'account/crypto/withdraw/{id}',
                        'sub-acc/acl/{subAccountUserId}',
                    ],
                    'delete': [
                        'order',
                        'order/{clientOrderId}',
                        'account/crypto/withdraw/{id}',
                    ],
                },
            },
            'precisionMode': TICK_SIZE,
            'exceptions': {
                'exact': {
                    'Insufficient funds': InsufficientFunds,
                    'Symbol not found': BadRequest,
                    'Price not a valid number': BadRequest,
                    'Quantity too low': BadRequest,
                    'Order not found': OrderNotFound,
                    'Validation error': OrderNotFound,
                },
                'broad': {
                },
            },
            'options': {
                'fetchTickersMaxLength': 250,
            },
            'commonCurrencies': {
                'DSH': 'DASH',
            },
        });
    }

    async fetchCurrenciesFromCache (params = {}) {
        const options = this.safeValue (this.options, 'fetchCurrencies', {});
        const timestamp = this.safeInteger (options, 'timestamp');
        const expires = this.safeInteger (options, 'expires', 1000);
        const now = this.milliseconds ();
        if ((timestamp === undefined) || ((now - timestamp) > expires)) {
            const currencies = await this.publicGetCurrency (params);
            this.options['fetchCurrencies'] = this.extend (options, {
                'currencies': currencies,
                'timestamp': now,
            });
        }
        return this.safeValue (this.options, 'fetchCurrencies', {});
    }

    async fetchCurrencies (params = {}) {
        const response = await this.fetchCurrenciesFromCache (params);
        const currencies = this.safeValue (response, 'currencies', {});
        //   [ { id: 'BCH',
        //     fullName: 'Bitcoin Cash',
        //     crypto: true,
        //     payinEnabled: true,
        //     payinPaymentId: false,
        //     payinConfirmations: 2,
        //     payoutEnabled: true,
        //     payoutIsPaymentId: false,
        //     transferEnabled: true,
        //     delisted: false,
        //     payoutFee: '0.000500000000' }, ...
        const result = {};
        for (let i = 0; i < currencies.length; i++) {
            const currency = currencies[i];
            const id = this.safeString (currency, 'id');
            const name = this.safeString (currency, 'fullName');
            const code = this.safeCurrencyCode (id, name);
            const transferEnabled = currency['transferEnabled'];
            const delisted = currency['delisted'];
            // payoutEnabled means that API support withdrawals in thsi currency
            const active = !delisted && transferEnabled;
            const fee = this.safeFloat (currency, 'payoutFee');
            result[code] = {
                'id': id,
                'code': code,
                'name': name,
                'active': active,
                'fee': fee,
                'info': currency,
            };
        }
        return result;
    }

    async fetchMarkets (params = {}) {
        const response = await this.publicGetSymbol (params);
        //
        //     [
        //         {
        //             "id":"BTCUSDT",
        //             "baseCurrency":"BTC",
        //             "quoteCurrency":"USDT",
        //             "quantityIncrement":"0.00001",
        //             "tickSize":"0.01",
        //             "takeLiquidityRate":"0.0025",
        //             "provideLiquidityRate":"0.0015",
        //             "feeCurrency":"USDT"
        //         }
        //     ]
        //
        const result = [];
        for (let i = 0; i < response.length; i++) {
            const market = response[i];
            const id = this.safeString (market, 'id');
            const baseId = this.safeString (market, 'baseCurrency');
            const quoteId = this.safeString (market, 'quoteCurrency');
            const base = this.safeCurrencyCode (baseId);
            const quote = this.safeCurrencyCode (quoteId);
            const symbol = base + '/' + quote;
            const maker = this.safeFloat (market, 'provideLiquidityRate');
            const taker = this.safeFloat (market, 'takeLiquidityRate');
            const precision = {
                'price': this.safeFloat (market, 'tickSize'),
                'amount': this.safeFloat (market, 'quantityIncrement'),
            };
            const active = undefined;
            const lowercaseBaseId = this.safeStringLower (market, 'baseCurrency');
            const lowercaseQuoteId = this.safeStringLower (market, 'quoteCurrency');
            const lowercaseId = lowercaseBaseId + lowercaseQuoteId;
            result.push ({
                'id': id,
                // https://github.com/ccxt/ccxt/pull/5786
                'lowercaseId': lowercaseId,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'maker': maker,
                'taker': taker,
                // 'feeCurrency': this.safeString (market, 'feeCurrency'),
                'active': active,
                'precision': precision,
                'info': market,
            });
        }
        return result;
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        const tickers = await this.publicGetTicker (params);
        const result = {};
        for (let i = 0; i < tickers.length; i++) {
            const ticker = tickers[i];
            const id = this.safeString (ticker, 'symbol');
            const market = this.markets_by_id[id];
            const symbol = market['symbol'];
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    parseTicker (ticker, market = undefined) {
        //
        //   { symbol: 'BSVUSD',
        //     ask: '100000.000',
        //     bid: '0.207',
        //     last: null,
        //     open: null,
        //     low: '0',
        //     high: '0',
        //     volume: '0',
        //     volumeQuote: '0',
        //     timestamp: '2020-03-27T10:31:08.583Z' }
        //
        const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp'));
        const symbol = market['symbol'];
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeFloat (ticker, 'high'),
            'low': this.safeFloat (ticker, 'low'),
            'bid': this.safeFloat (ticker, 'bid'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'ask'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': this.safeFloat (ticker, 'open'),
            'close': undefined,
            'last': this.safeFloat (ticker, 'last'),
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': this.safeFloat (ticker, 'volume'),
            'quoteVolume': this.safeFloat (ticker, 'volumeQuote'),
            'info': ticker,
        };
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        // the API docs are wrong - all orderbooks get returned if no symbol is present so we can implement fetchOrderbooks using publicGetOrderbook()
        if (symbol === undefined) {
            throw new ArgumentsRequired (this.id + ' fetchOrderBook() requires a symbol argument');
        }
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'symbol': market['id'],
        };
        const orderbook = await this.publicGetOrderbookSymbol (this.extend (request, params));
        const timestamp = this.parse8601 (this.safeString (orderbook, 'timestamp'));
        return this.parseOrderBook (orderbook, timestamp, 'bid', 'ask', 'price', 'size');
    }

    parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) {
        return [
            this.parse8601 (this.safeString (ohlcv, 'timestamp')),
            this.safeFloat (ohlcv, 'open'),
            this.safeFloat (ohlcv, 'max'),
            this.safeFloat (ohlcv, 'min'),
            this.safeFloat (ohlcv, 'close'),
            this.safeFloat (ohlcv, 'volume'),
        ];
    }

    async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'symbol': market['id'],
            'period': this.timeframes[timeframe],
        };
        if (since !== undefined) {
            request['from'] = this.iso8601 (since);
        }
        if (limit !== undefined) {
            request['limit'] = limit;
        }
        const response = await this.publicGetCandlesSymbol (this.extend (request, params));
        return this.parseOHLCVs (response, market, timeframe, since, limit);
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        const type = this.safeString (params, 'type', 'trading');
        const method = 'privateGet' + this.capitalize (type) + 'Balance';
        const query = this.omit (params, 'type');
        const response = await this[method] (query);
        const result = { 'info': response };
        for (let i = 0; i < response.length; i++) {
            const balance = response[i];
            const currencyId = this.safeString (balance, 'currency');
            const code = this.safeCurrencyCode (currencyId);
            const account = this.account ();
            account['free'] = this.safeFloat (balance, 'available');
            account['used'] = this.safeFloat (balance, 'reserved');
            result[code] = account;
        }
        return this.parseBalance (result);
    }

    async fetchDepositAddress (code, params = {}) {
        await this.loadMarkets ();
        const currency = this.currency (code);
        const request = {
            'currency': currency['id'],
        };
        const response = await this.privateGetAccountCryptoAddressCurrency (this.extend (request, params));
        // xrp ->  {"address":"rwpMsdkfjskdjf","paymentId":"1483475384577"}
        return {
            'currency': code,
            'address': this.safeString (response, 'address'),
            'tag': this.safeString (response, '1444344687'),
            'info': response,
        };
    }

    async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const request = {};
        if (limit !== undefined) {
            request['limit'] = limit;
        }
        const response = await this.privateGetAccountTransactions (this.extend (request, params));
        return this.parseTransactions (response);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'symbol': market['id'],
            'side': side,
            'quantity': this.amountToPrecision (symbol, amount),
            'price': this.priceToPrecision (symbol, price),
        };
        const response = await this.privatePostOrder (this.extend (request, params));
        return this.parseOrder (response);
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        const request = {
            'clientOrderId': id,
        };
        const response = await this.privateDeleteOrderClientOrderId (this.extend (request, params));
        return this.parseOrder (response);
    }

    async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const request = {};
        if (limit !== undefined) {
            request['limit'] = limit;
        }
        const response = await this.privateGetOrder (this.extend (request, params));
        return this.parseOrders (response);
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        const request = {
            'clientOrderId': id,
        };
        const response = await this.privateGetHistoryOrder (this.extend (request, params));
        return this.parseOrders (response);
    }

    async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const request = {};
        if (symbol !== undefined) {
            const market = this.market (symbol);
            request['symbol'] = market['id'];
        }
        if (limit !== undefined) {
            request['limit'] = limit;
        }
        if (since !== undefined) {
            request['from'] = since;
        }
        const response = await this.privateGetHistoryOrder (this.extend (request, params));
        return this.parseOrders (response);
    }

    async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const request = {};
        if (symbol !== undefined) {
            const market = this.market (symbol);
            request['symbol'] = market['id'];
        }
        if (limit !== undefined) {
            request['limit'] = limit;
        }
        if (since !== undefined) {
            request['from'] = since;
        }
        const response = await this.privateGetHistoryTrades (this.extend (request, params));
        return this.parseTrades (response);
    }

    parseTrade (trade, market = undefined) {
        //   { id: 809014577,
        //     clientOrderId: '2c7d66b38a095603797b8b260ffa5f33',
        //     orderId: 226958186479,
        //     symbol: 'ETHBTC',
        //     side: 'sell',
        //     quantity: '0.1000',
        //     price: '0.020504',
        //     fee: '0.000005126000',
        //     timestamp: '2020-03-27T15:48:00.315Z' }
        const timestamp = this.parse8601 (this.safeString (trade, 'timestamp'));
        market = this.parseMarket (this.safeString (trade, 'symbol'));
        const price = this.safeFloat (trade, 'price');
        const amount = this.safeFloat (trade, 'quantity');
        return {
            'id': this.safeString (trade, 'id'),
            'order': this.safeString (trade, 'clientOrderId'),
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': market['symbol'],
            'type': undefined,
            'side': this.safeString (trade, 'side'),
            'takerOrMaker': undefined,
            'price': price,
            'amount': amount,
            'cost': price * amount,
            'fee': {
                'cost': this.safeFloat (trade, 'fee'),
                'currency': this.safeString (market, 'feeCurrency'),
            },
            'info': trade,
        };
    }

    calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) {
        const market = this.markets[symbol];
        let key = 'quote';
        const rate = market[takerOrMaker];
        let cost = parseFloat (this.costToPrecision (symbol, amount * rate));
        if (side === 'sell') {
            cost *= price;
        } else {
            key = 'base';
        }
        return {
            'type': takerOrMaker,
            'currency': market[key],
            'rate': rate,
            'cost': cost,
        };
    }

    parseOrderStatus (status) {
        const statuses = {
            'new': 'open',
            'suspended': 'open',
            'partiallyFilled': 'open',
            'filled': 'closed',
            'canceled': 'canceled',
            'expired': 'canceled',
        };
        return this.safeString (statuses, status, status);
    }

    parseMarket (id) {
        if (id in this.markets_by_id) {
            return this.markets_by_id[id];
        } else {
            // the following is a fix for
            // https://github.com/ccxt/ccxt/pull/5786
            // https://github.com/ccxt/ccxt/issues/5770
            let markets_by_other_id = this.safeValue (this.options, 'markets_by_other_id');
            if (markets_by_other_id === undefined) {
                this.options['markets_by_other_id'] = this.indexBy (this.markets, 'otherId');
                markets_by_other_id = this.options['markets_by_other_id'];
            }
            if (id in markets_by_other_id) {
                return markets_by_other_id[id];
            }
        }
        return undefined;
    }

    parseOrder (order, market = undefined) {
        //   cancelOrder response
        //   { id: 226952257529,
        //     clientOrderId: '2241bed127c1756240641b6159a05d33',
        //     symbol: 'ETHBTC',
        //     side: 'sell',
        //     status: 'new',
        //     type: 'limit',
        //     timeInForce: 'GTC',
        //     price: '1.000000',
        //     quantity: '0.1000',
        //     postOnly: false,
        //     cumQuantity: '0',
        //     createdAt: '2020-03-27T15:20:41.427Z',
        //     updatedAt: '2020-03-27T15:20:41.427Z' }
        //
        const id = this.safeString (order, 'clientOrderId');
        const clientOrderId = id;
        const status = this.parseOrderStatus (this.safeString (order, 'status'));
        const timestamp = this.parse8601 (this.safeString (order, 'createdAt'));
        const marketId = this.safeString (order, 'symbol');
        market = this.parseMarket (marketId);
        let symbol = undefined;
        if (market !== undefined) {
            symbol = market['symbol'];
        }
        const filled = this.safeFloat (order, 'cumQuantity');
        const amount = this.safeFloat (order, 'quantity');
        const price = this.safeFloat (order, 'price');
        const remaining = amount - filled;
        const orderType = this.safeString (order, 'type');
        const side = this.safeString (order, 'side');
        return {
            'id': id,
            'clientOrderId': clientOrderId,
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'lastTradeTimestamp': undefined,
            'type': orderType,
            'side': side,
            'price': price,
            'cost': undefined,
            'amount': amount,
            'remaining': remaining,
            'filled': filled,
            'status': status,
            'fee': undefined,
            'trades': undefined,
            'average': undefined,
            'info': order,
        };
    }

    parseTransactionStatus (status) {
        const statuses = {
            'created': 'pending',
            'pending': 'pending',
            'failed': 'failed',
            'success': 'ok',
        };
        return this.safeString (statuses, status, status);
    }

    parseTransactionType (status) {
        // bankToExchange and exchangeToBank are internal movements
        const statuses = {
            'payout': 'withdrawal',
            'payin': 'deposit',
            'deposit': 'deposit',
            'withdraw': 'withdrawal',
        };
        return this.safeString (statuses, status, status);
    }

    parseTransaction (transaction, currency = undefined) {
        //   {
        //     "id": "73443ee9f54c",
        //     "fee": "0.001",
        //     "hash": "3f077268b8dda69",
        //     "type": "payout",
        //     "index": 5430881244,
        //     "amount": "6.999000000000000000000000",
        //     "status": "success",
        //     "address": "L45PsQU7VoqaNa5G336Z68umhG",
        //     "currency": "LTC",
        //     "createdAt": "2020-05-15T20:39:47.140Z",
        //     "updatedAt": "2020-05-15T21:34:58.887Z",
        //     "confirmations": 5
        //   }
        const id = this.safeString (transaction, 'id');
        const timestamp = this.parse8601 (this.safeString (transaction, 'createdAt'));
        const updated = this.parse8601 (this.safeString (transaction, 'updatedAt'));
        const currencyId = this.safeString (transaction, 'currency');
        const code = this.safeCurrencyCode (currencyId, currency);
        const status = this.parseTransactionStatus (this.safeString (transaction, 'status'));
        const amount = this.safeFloat (transaction, 'amount');
        const address = this.safeString (transaction, 'address');
        const txid = this.safeString (transaction, 'hash');
        let fee = undefined;
        const feeCost = this.safeFloat (transaction, 'fee');
        if (feeCost !== undefined) {
            fee = {
                'cost': feeCost,
                'currency': code,
            };
        }
        const type = this.parseTransactionType (this.safeString (transaction, 'type'));
        return {
            'info': transaction,
            'id': id,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'address': address,
            'tag': undefined,
            'type': type,
            'amount': amount,
            'currency': code,
            'status': status,
            'updated': updated,
            'fee': fee,
        };
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'][api];
        url += '/' + this.implodeParams (path, params);
        const query = this.omit (params, this.extractParams (path));
        if (api === 'public') {
            if (Object.keys (query).length) {
                url += '?' + this.urlencode (query);
            }
        } else {
            const auth = this.apiKey + ':' + this.secret;
            headers = {
                'Authorization': 'Basic ' + this.stringToBase64 (auth),
            };
            if (method === 'POST' || method === 'PUT') {
                if (Object.keys (query).length) {
                    headers['Content-Type'] = 'application/json';
                    body = this.json (query);
                }
            } else if (method === 'GET') {
                if (Object.keys (query).length) {
                    url += '?' + this.urlencode (query);
                }
            }
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) {
        if (response === undefined) {
            return; // fallback to default error handler
        }
        if ('error' in response) {
            // {"error":{"code":20001,"message":"Insufficient funds","description":"Check that the funds are sufficient, given commissions"}}
            const error = response['error'];
            const code = this.safeString (error, 'code');
            const message = this.safeString (error, 'message');
            const feedback = this.id + ' ' + body;
            this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback);
            this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback);
            this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback);
            throw new ExchangeError (feedback); // unknown message
        }
    }
};
<commit_msg>dsx derived from hitbtc<commit_after>'use strict';

// ---------------------------------------------------------------------------

const hitbtc = require ('./hitbtc.js');

// ---------------------------------------------------------------------------

module.exports = class dsx extends hitbtc {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'dsx',
            'name': 'DSX',
            'countries': [ 'UK' ],
            'rateLimit': 100,
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/51840849/76909626-cb2bb100-68bc-11ea-99e0-28ba54f04792.jpg',
                'api': {
                    'public': 'https://api.dsxglobal.com',
                    'private': 'https://api.dsxglobal.com',
                },
                'www': 'http://dsxglobal.com',
                'doc': [
                    'https://api.dsxglobal.com',
                ],
            },
            'fees': {
                'trading': {
                    'tierBased': true,
                    'percentage': true,
                    'maker': 0.15 / 100,
                    'taker': 0.25 / 100,
                },
            },
            'timeframes': {
                '1m': 'M1',
                '3m': 'M3',
                '5m': 'M5',
                '15m': 'M15',
                '30m': 'M30',
                '1h': 'H1',
                '4h': 'H4',
                '1d': 'D1',
                '1w': 'D7',
                '1M': '1M',
            },
        });
    }
};
<|endoftext|>


<commit_before>// ffs/wizard module
if (typeof turbo === "undefined") turbo={};
turbo.ffs = function() {
  var ffs = {};
  var freeFormQuery;

  /* this converts a random boolean expression into a normalized form:
   * A∧B∧… ∨ C∧D∧… ∨ …
   * for example: A∧(B∨C) ⇔ (A∧B)∨(A∧C)
   */
  function normalize(query) {
    var normalized_query = {
      logical:"or",
      queries:[]
    };
    function normalize_recursive(rem_query) {
      if (!rem_query.logical) {
        return [{
          logical: "and",
          queries: [rem_query]
        }];
      } else if (rem_query.logical === "and") {
        var c1 = normalize_recursive( rem_query.queries[0] );
        var c2 = normalize_recursive( rem_query.queries[1] );
        // return cross product of c1 and c2
        var c = [];
        for (var i=0; i<c1.length; i++)
          for (var j=0; j<c2.length; j++) {
            c.push({
              logical: "and",
              queries: c1[i].queries.concat(c2[j].queries)
            });
          }
        return c;
      } else if (rem_query.logical === "or") {
        var c1 = normalize_recursive( rem_query.queries[0] );
        var c2 = normalize_recursive( rem_query.queries[1] );
        return [].concat(c1,c2);

      } else {
        alert("unsupported boolean operator: "+rem_query.logical);
      }
    }
    normalized_query.queries = normalize_recursive(query);
    return normalized_query;
  }

  ffs.construct_query = function(search, comment) {
    try {
      ffs = turbo.ffs.parser.parse(search);
    } catch(e) {
      //alert("parse error :(");
      return false;
    }

    var query_parts = [];
    var bounds_part;

    query_parts.push('<!--');
    if (comment) {
      query_parts.push(comment)
    } else {
      query_parts.push('This has been generated by the overpass-turbo wizard.');
      query_parts.push('The original search was:');
      query_parts.push('“'+search+'”');
    }
    query_parts.push('-->');
    query_parts.push('<osm-script output="json" timeout="25">');

    switch(ffs.bounds) {
      case "area":
        query_parts.push('  <!-- fetch area “'+ffs.area+'” to search in -->');
        query_parts.push('  <id-query {{nominatimArea:'+ffs.area+'}} into="area"/>');
        bounds_part = '<area-query from="area"/>';
      break;
      case "around":
        query_parts.push('  <!-- adjust the search radius (in meters) here -->');
        query_parts.push('  {{radius=1000}}');
        bounds_part = '<around {{nominatimCoords:'+ffs.area+'}} radius="{{radius}}"/>';
      break;
      case "bbox":
        bounds_part = '<bbox-query {{bbox}}/>';
      break;
      case "global":
        bounds_part = undefined;
      break;
      default:
        alert("unknown bounds condition: "+ffs.bounds);
        return false;
      break;
    }

    function get_query_clause(condition) {
      function esc(str) {
        // overpass API gets confused over tabs and newline characters
        // see https://github.com/drolbr/Overpass-API/issues/91
        return htmlentities(str).replace(/\t/g,"	").replace(/\n/g,"
").replace(/\r/g,"
");
      }
      function escRegexp(str) {
        return str.replace(/([()[{*+.$^\\|?])/g, '\\$1');
      }
      var key = esc(condition.key);
      var val = esc(condition.val);
      // convert substring searches into matching regexp ones
      if (condition.query === "substr") {
        condition.query = "like";
        condition.val={regex:escRegexp(condition.val)};
      }
      // special case for empty values
      // see https://github.com/drolbr/Overpass-API/issues/53
      if (val === '') {
        if (condition.query === "eq") {
          condition.query = "like";
          condition.val={regex:'^$'};
        } else if (condition.query === "neq") {
          condition.query = "notlike";
          condition.val={regex:'^$'};
        }
      }
      // special case for empty values
      // see https://github.com/drolbr/Overpass-API/issues/53#issuecomment-26325122
      if (key === '') {
        if (condition.query === "key") {
          condition.query = "likelike";
          key='^$';
          condition.val={regex: '.*'};
        } else if (condition.query === "eq") {
          condition.query = "likelike";
          key='^$';
          condition.val={regex: '^'+escRegexp(condition.val)+'$'};
        } else if (condition.query === "like") {
          condition.query = "likelike";
          key='^$';
        }
      }
      // construct the query clause
      switch(condition.query) {
        case "key":
          return '<has-kv k="'+key+'"/>';
        case "nokey":
          return '<has-kv k="'+key+'" modv="not" regv=".*"/>';
        case "eq":
          return '<has-kv k="'+key+'" v="'+val+'"/>';
        case "neq":
          return '<has-kv k="'+key+'" modv="not" v="'+val+'"/>';
        case "like":
          return '<has-kv k="'+key+'" regv="'+esc(condition.val.regex)+'"'
                 +(condition.val.modifier==="i"?' case="ignore"':'')
                 +'/>';
        case "likelike":
          return '<has-kv regk="'+key+'" regv="'+esc(condition.val.regex)+'"'
                 +(condition.val.modifier==="i"?' case="ignore"':'')
                 +'/>';
        case "notlike":
          return '<has-kv k="'+key+'" modv="not" regv="'+esc(condition.val.regex)+'"'
                 +(condition.val.modifier==="i"?' case="ignore"':'')
                 +'/>';
        case "meta":
          switch(condition.meta) {
            case "id":
              return function(type) {
                return '<id-query type="'+type+'" ref="'+val+'"/>';
              };
            case "newer":
              if (condition.val.match(/^-?\d+ ?(seconds?|minutes?|hours?|days?|weeks?|months?|years?)?$/))
                return '<newer than="{{date:'+val+'}}"/>';
              return '<newer than="'+val+'"/>';
            case "user":
              return '<user name="'+val+'"/>';
            case "uid":
              return '<user uid="'+val+'"/>';
            default:
              console.log("unknown query type: meta/"+condition.meta);
              return false;
          }
        case "free form":
          // own module, special cased below
        default:
          console.log("unknown query type: "+condition.query);
          return false;
      }
    }
    function get_query_clause_str(condition) {
      function quotes(s) {
        if (s.match(/^[a-zA-Z0-9_]+$/) === null)
          return '"'+s.replace(/"/g,'\\"')+'"';
        return s;
      }
      function quoteRegex(s) {
        if (s.regex.match(/^[a-zA-Z0-9_]+$/) === null || s.modifier)
          return '/'+s.regex.replace(/\//g,'\\/')+'/'+(s.modifier||'');
        return s.regex;
      }
      switch(condition.query) {
        case "key":
          return quotes(condition.key)+'=*';
        case "nokey":
          return quotes(condition.key)+'!=*';
        case "eq":
          return quotes(condition.key)+'='+quotes(condition.val);
        case "neq":
          return quotes(condition.key)+'!='+quotes(condition.val);
        case "like":
          return quotes(condition.key)+'~'+quoteRegex(condition.val);
        case "likelike":
          return '~'+quotes(condition.key)+'~'+quoteRegex(condition.val);
        case "notlike":
          return quotes(condition.key)+'!~'+quoteRegex(condition.val);
        case "substr":
          return quotes(condition.key)+':'+quotes(condition.val);
        case "meta":
          switch(condition.meta) {
            case "id":
              return 'id:'+quotes(condition.val);
            case "newer":
              return 'newer:'+quotes(condition.val);
            case "user":
              return 'user:'+quotes(condition.val);
            case "uid":
              return 'uid:'+quotes(condition.val);
            default:
              return '';
          }
        case "free form":
          return quotes(condition.free);
        default:
          return '';
      }
    }

    ffs.query = normalize(ffs.query);

    query_parts.push('  <!-- gather results -->');
    query_parts.push('  <union>');
    for (var i=0; i<ffs.query.queries.length; i++) {
      var and_query = ffs.query.queries[i];

      var types = ['node','way','relation'];
      var clauses = [];
      var clauses_str = [];
      for (var j=0; j<and_query.queries.length; j++) {
        var cond_query = and_query.queries[j];
        // todo: looks like some code duplication here could be reduced by refactoring
        if (cond_query.query === "free form") {
          // eventually load free form query module
          if (!freeFormQuery) freeFormQuery = turbo.ffs.free();
          var ffs_clause = freeFormQuery.get_query_clause(cond_query);
          if (ffs_clause === false)
            return false;
          // restrict possible data types
          types = types.filter(function(t) {
            return ffs_clause.types.indexOf(t) != -1;
          });
          // add clauses
          clauses_str.push(get_query_clause_str(cond_query));
          clauses = clauses.concat(ffs_clause.conditions.map(function(condition) {
            return get_query_clause(condition);
          }));
        } else if (cond_query.query === "type") {
          // restrict possible data types
          types = types.indexOf(cond_query.type) != -1 ? [cond_query.type] : [];
        } else {
          // add another query clause
          clauses_str.push(get_query_clause_str(cond_query));
          var clause = get_query_clause(cond_query);
          if (clause === false) return false;
          clauses.push(clause);
        }
      }
      clauses_str = clauses_str.join(' and ');

      // construct query
      query_parts.push('    <!-- query part for: “'+clauses_str+'” -->')
      for (var t=0; t<types.length; t++) {
        query_parts.push('    <query type="'+types[t]+'">');
        for (var c=0; c<clauses.length; c++)
          if (typeof clauses[c] !== "function")
            query_parts.push('      '+clauses[c]);
          else
            query_parts.push('      '+clauses[c](types[t]));
        if (bounds_part)
          query_parts.push('      '+bounds_part);
        query_parts.push('    </query>');
      }
    }
    query_parts.push('  </union>');

    query_parts.push('  <!-- print results -->');
    query_parts.push('  <print mode="body"/>');
    query_parts.push('  <recurse type="down"/>');
    query_parts.push('  <print mode="skeleton" order="quadtile"/>');

    query_parts.push('</osm-script>');

    return query_parts.join('\n');
  }

  // this is a "did you mean …" mechanism against typos in preset names
  ffs.repair_search = function(search) {
    try {
      ffs = turbo.ffs.parser.parse(search);
    } catch(e) {
      return false;
    }

    function quotes(s) {
      if (s.match(/^[a-zA-Z0-9_]+$/) === null)
        return '"'+s.replace(/"/g,'\\"')+'"';
      return s;
    }

    var search_parts = [];
    var repaired = false;

    ffs.query = normalize(ffs.query);
    ffs.query = _.flatten(_.pluck(ffs.query.queries,"queries"));
    ffs.query.forEach(function(cond_query) {
      if (cond_query.query === "free form") {
        // eventually load free form query module
        if (!freeFormQuery) freeFormQuery = turbo.ffs.free();
        var ffs_clause = freeFormQuery.get_query_clause(cond_query);
        if (ffs_clause === false) {
          // try to find suggestions for occasional typos
          var fuzzy = freeFormQuery.fuzzy_search(cond_query);
          var free_regex = new RegExp("['\"]?"+cond_query.free+"['\"]?");
          if (fuzzy && search.match(free_regex)) {
            search_parts = search_parts.concat(search.split(free_regex));
            search = search_parts.pop();
            var replacement = quotes(fuzzy);
            search_parts.push(replacement);
            repaired = true;
          }
        }
      }
    });
    search_parts.push(search);

    if (!repaired)
      return false;
    return search_parts;
  }

  return ffs;
};<commit_msg>basic prototype of wizard producing ql output<commit_after>// ffs/wizard module
if (typeof turbo === "undefined") turbo={};
turbo.ffs = function() {
  var ffs = {};
  var freeFormQuery;

  /* this converts a random boolean expression into a normalized form:
   * A∧B∧… ∨ C∧D∧… ∨ …
   * for example: A∧(B∨C) ⇔ (A∧B)∨(A∧C)
   */
  function normalize(query) {
    var normalized_query = {
      logical:"or",
      queries:[]
    };
    function normalize_recursive(rem_query) {
      if (!rem_query.logical) {
        return [{
          logical: "and",
          queries: [rem_query]
        }];
      } else if (rem_query.logical === "and") {
        var c1 = normalize_recursive( rem_query.queries[0] );
        var c2 = normalize_recursive( rem_query.queries[1] );
        // return cross product of c1 and c2
        var c = [];
        for (var i=0; i<c1.length; i++)
          for (var j=0; j<c2.length; j++) {
            c.push({
              logical: "and",
              queries: c1[i].queries.concat(c2[j].queries)
            });
          }
        return c;
      } else if (rem_query.logical === "or") {
        var c1 = normalize_recursive( rem_query.queries[0] );
        var c2 = normalize_recursive( rem_query.queries[1] );
        return [].concat(c1,c2);

      } else {
        alert("unsupported boolean operator: "+rem_query.logical);
      }
    }
    normalized_query.queries = normalize_recursive(query);
    return normalized_query;
  }

  ffs.construct_query = function(search, comment) {
    try {
      ffs = turbo.ffs.parser.parse(search);
    } catch(e) {
      //alert("parse error :(");
      return false;
    }

    var query_parts = [];
    var bounds_part;

    query_parts.push('/*');
    if (comment) {
      query_parts.push(comment)
    } else {
      query_parts.push('This has been generated by the overpass-turbo wizard.');
      query_parts.push('The original search was:');
      query_parts.push('“'+search+'”');
    }
    query_parts.push('*/');
    query_parts.push('[out:json][timeout:25];');

    switch(ffs.bounds) {
      case "area":
        query_parts.push('// fetch area “'+ffs.area+'” to search in');
        query_parts.push('{{nominatimArea:'+ffs.area+'}} (._)->.searchArea;');
        bounds_part = '(area.searchArea)';
      break;
      case "around":
        query_parts.push('// adjust the search radius (in meters) here');
        query_parts.push('{{radius=1000}}');
        bounds_part = 'around({{nominatimCoords:'+ffs.area+'}}, radius={{radius}})';
      break;
      case "bbox":
        bounds_part = '({{bbox}})';
      break;
      case "global":
        bounds_part = undefined;
      break;
      default:
        alert("unknown bounds condition: "+ffs.bounds);
        return false;
      break;
    }

    function get_query_clause(condition) {
      function esc(str) {
        // overpass API gets confused over tabs and newline characters
        // see https://github.com/drolbr/Overpass-API/issues/91
        return htmlentities(str).replace(/\t/g,"	").replace(/\n/g,"
").replace(/\r/g,"
");
      }
      function escRegexp(str) {
        return str.replace(/([()[{*+.$^\\|?])/g, '\\$1');
      }
      var key = esc(condition.key);
      var val = esc(condition.val);
      // convert substring searches into matching regexp ones
      if (condition.query === "substr") {
        condition.query = "like";
        condition.val={regex:escRegexp(condition.val)};
      }
      // special case for empty values
      // see https://github.com/drolbr/Overpass-API/issues/53
      if (val === '') {
        if (condition.query === "eq") {
          condition.query = "like";
          condition.val={regex:'^$'};
        } else if (condition.query === "neq") {
          condition.query = "notlike";
          condition.val={regex:'^$'};
        }
      }
      // special case for empty values
      // see https://github.com/drolbr/Overpass-API/issues/53#issuecomment-26325122
      if (key === '') {
        if (condition.query === "key") {
          condition.query = "likelike";
          key='^$';
          condition.val={regex: '.*'};
        } else if (condition.query === "eq") {
          condition.query = "likelike";
          key='^$';
          condition.val={regex: '^'+escRegexp(condition.val)+'$'};
        } else if (condition.query === "like") {
          condition.query = "likelike";
          key='^$';
        }
      }
      // construct the query clause
      switch(condition.query) {
        case "key":
          return '["'+key+'"]';
        case "nokey":
          return '["'+key+'"!~.*]';
        case "eq":
          return '["'+key+'"="'+val+'"]';
        case "neq":
          return '["'+key+'"!="'+val+'"]';
        case "like":
          return '["'+key+'"~"'+esc(condition.val.regex)+'"'
                 //todo: +(condition.val.modifier==="i"?' case="ignore"':'')
                 +']';
        case "likelike":
          return '[~"'+key+'"~"'+esc(condition.val.regex)+'"'
                 //todo: +(condition.val.modifier==="i"?' case="ignore"':'')
                 +']';
        case "notlike":
          return '["'+key+'"!~"'+esc(condition.val.regex)+'"'
                 //todo: +(condition.val.modifier==="i"?' case="ignore"':'')
                 +']';
        case "meta":
          switch(condition.meta) {
            case "id":
              return '('+val+')';
            case "newer":
              if (condition.val.match(/^-?\d+ ?(seconds?|minutes?|hours?|days?|weeks?|months?|years?)?$/))
                return '(newer:"{{date:'+val+'}}")';
              return '(newer:"'+val+'")';
            case "user":
              return '(user:"'+val+'")';
            case "uid":
              return '(uid:"'+val+'")';
            default:
              console.log("unknown query type: meta/"+condition.meta);
              return false;
          }
        case "free form":
          // own module, special cased below
        default:
          console.log("unknown query type: "+condition.query);
          return false;
      }
    }
    function get_query_clause_str(condition) {
      function quotes(s) {
        if (s.match(/^[a-zA-Z0-9_]+$/) === null)
          return '"'+s.replace(/"/g,'\\"')+'"';
        return s;
      }
      function quoteRegex(s) {
        if (s.regex.match(/^[a-zA-Z0-9_]+$/) === null || s.modifier)
          return '/'+s.regex.replace(/\//g,'\\/')+'/'+(s.modifier||'');
        return s.regex;
      }
      switch(condition.query) {
        case "key":
          return quotes(condition.key)+'=*';
        case "nokey":
          return quotes(condition.key)+'!=*';
        case "eq":
          return quotes(condition.key)+'='+quotes(condition.val);
        case "neq":
          return quotes(condition.key)+'!='+quotes(condition.val);
        case "like":
          return quotes(condition.key)+'~'+quoteRegex(condition.val);
        case "likelike":
          return '~'+quotes(condition.key)+'~'+quoteRegex(condition.val);
        case "notlike":
          return quotes(condition.key)+'!~'+quoteRegex(condition.val);
        case "substr":
          return quotes(condition.key)+':'+quotes(condition.val);
        case "meta":
          switch(condition.meta) {
            case "id":
              return 'id:'+quotes(condition.val);
            case "newer":
              return 'newer:'+quotes(condition.val);
            case "user":
              return 'user:'+quotes(condition.val);
            case "uid":
              return 'uid:'+quotes(condition.val);
            default:
              return '';
          }
        case "free form":
          return quotes(condition.free);
        default:
          return '';
      }
    }

    ffs.query = normalize(ffs.query);

    query_parts.push('// gather results');
    query_parts.push('(');
    for (var i=0; i<ffs.query.queries.length; i++) {
      var and_query = ffs.query.queries[i];

      var types = ['node','way','relation'];
      var clauses = [];
      var clauses_str = [];
      for (var j=0; j<and_query.queries.length; j++) {
        var cond_query = and_query.queries[j];
        // todo: looks like some code duplication here could be reduced by refactoring
        if (cond_query.query === "free form") {
          // eventually load free form query module
          if (!freeFormQuery) freeFormQuery = turbo.ffs.free();
          var ffs_clause = freeFormQuery.get_query_clause(cond_query);
          if (ffs_clause === false)
            return false;
          // restrict possible data types
          types = types.filter(function(t) {
            return ffs_clause.types.indexOf(t) != -1;
          });
          // add clauses
          clauses_str.push(get_query_clause_str(cond_query));
          clauses = clauses.concat(ffs_clause.conditions.map(function(condition) {
            return get_query_clause(condition);
          }));
        } else if (cond_query.query === "type") {
          // restrict possible data types
          types = types.indexOf(cond_query.type) != -1 ? [cond_query.type] : [];
        } else {
          // add another query clause
          clauses_str.push(get_query_clause_str(cond_query));
          var clause = get_query_clause(cond_query);
          if (clause === false) return false;
          clauses.push(clause);
        }
      }
      clauses_str = clauses_str.join(' and ');

      // construct query
      query_parts.push('  // query part for: “'+clauses_str+'”')
      for (var t=0; t<types.length; t++) {
        var buffer = '  '+types[t];
        for (var c=0; c<clauses.length; c++)
          buffer += clauses[c];
        if (bounds_part)
          buffer += bounds_part;
        buffer += ';';
        query_parts.push(buffer);
      }
    }
    query_parts.push(');');

    query_parts.push('// print results');
    query_parts.push('out body;');
    query_parts.push('>;');
    query_parts.push('out skel qt;');

    return query_parts.join('\n');
  }

  // this is a "did you mean …" mechanism against typos in preset names
  ffs.repair_search = function(search) {
    try {
      ffs = turbo.ffs.parser.parse(search);
    } catch(e) {
      return false;
    }

    function quotes(s) {
      if (s.match(/^[a-zA-Z0-9_]+$/) === null)
        return '"'+s.replace(/"/g,'\\"')+'"';
      return s;
    }

    var search_parts = [];
    var repaired = false;

    ffs.query = normalize(ffs.query);
    ffs.query = _.flatten(_.pluck(ffs.query.queries,"queries"));
    ffs.query.forEach(function(cond_query) {
      if (cond_query.query === "free form") {
        // eventually load free form query module
        if (!freeFormQuery) freeFormQuery = turbo.ffs.free();
        var ffs_clause = freeFormQuery.get_query_clause(cond_query);
        if (ffs_clause === false) {
          // try to find suggestions for occasional typos
          var fuzzy = freeFormQuery.fuzzy_search(cond_query);
          var free_regex = new RegExp("['\"]?"+cond_query.free+"['\"]?");
          if (fuzzy && search.match(free_regex)) {
            search_parts = search_parts.concat(search.split(free_regex));
            search = search_parts.pop();
            var replacement = quotes(fuzzy);
            search_parts.push(replacement);
            repaired = true;
          }
        }
      }
    });
    search_parts.push(search);

    if (!repaired)
      return false;
    return search_parts;
  }

  return ffs;
};<|endoftext|>


<commit_before>import { d } from 'lightsaber/lib/log'
import React from 'react'

import Auth from '../../models/Authentication'

export default class Login extends React.Component {
  demoLogin = (event) => {
    const uportAddress = event.target.dataset.uport
    Auth.setCurrentUser({uportAddress})
    this.props.history.push('/home')
  }

  render() {
    return (

            Login as 0xa778edb246f6def1c51e599c23e7c8f1d1493633

            Login as 0xa778edb246f6def1c51e599c23e7c8f1d1493633

            Login as 0xe9e77f329190eb415e09050183b79c859572659b

          Login as Harlan

    )
  }
}
<commit_msg>Update staging logins; demo button tweaks<commit_after>import { d } from 'lightsaber/lib/log'
import React from 'react'

import Auth from '../../models/Authentication'

export default class Login extends React.Component {
  demoLogin = (event) => {
    const uportAddress = event.target.dataset.uport
    Auth.setCurrentUser({uportAddress})
    this.props.history.push('/home')
  }

  render() {
    return (

          Staging: Login as 0x75e9b52ac5fec235d8dcec5cb3d9623390c7eefd

          Staging: Login as 0xc0967d2f86d02d0c8b9ebb6de7dc00ef6f18f666

          Dev: Login as 0xe9e77f329190eb415e09050183b79c859572659b

          Login as Harlan

    )
  }
}
<|endoftext|>


<commit_before>// the eslint "no-unused-expressions" rule is triggered by the `expect`
// assertions in the tests. So, in order to avoid problems with eslint, I'm
// disabling that rule in this file
//
/* eslint-disable no-unused-expressions */

import React from 'react';
import { shallow, render, mount } from 'enzyme';
import chai from 'chai';
import Form from './Form';

const expect = chai.expect;

/* *****************************************************************************
the Form component
    should be a <form> element
*/
describe('the Form component', () => {

    it('should be a <form> element', () => {
        const component = shallow(<Form />);

        expect(component.is('form')).to.equal(true);
    });

});

/* *****************************************************************************
a Form component with a TextInput element
    should include a <Form.TextInput> as a child
*/
describe('a Form component with a TextInput element', () => {

    it('should include a <Form.TextInput> as a child', () => {
        const component = shallow(
            <Form>
                <Form.TextInput />
            </Form>
        );

        expect(component.find(Form.TextInput)).to.have.length(1);
    });
});

/* *****************************************************************************
when initializing a Form with a required TextInput
    the global validation message should not be displayed with a valid value
    the component validation message should not be displayed with a valid value
    the global validation message should not be displayed with an invalid value
    the component validation message should not be displayed with an invalid value
*/
describe('when initializing a Form with a required TextInput', () => {

    const required = true;

    it('the validation message should not be displayed with a valid value', () => {
        const initialValue = 'something';

        const component = render(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        expect(component.find('Alert')).to.have.length(0);
        expect(component.find('.has-error')).to.have.length(0);
    });

    it('the component validation message should not be displayed with a valid value', () => {
        const initialValue = 'something';

        const component = render(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        expect(component.find('.help-block')).to.have.length(0);
    });

    it('the validation message should not be displayed with an invalid value', () => {
        const initialValue = '';

        const component = render(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        expect(component.find('Alert')).to.have.length(0);
        expect(component.find('.has-error')).to.have.length(0);
    });

    it('the component validation message should not be displayed with an invalid value', () => {
        const initialValue = '';

        const component = render(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        expect(component.find('.help-block')).to.have.length(0);
    });

});

/* *****************************************************************************
when initializing a Form with a non-required TextInput
    the global validation message should not be displayed with a valid value
    the component validation message should not be displayed with a valid value
    the global validation message should not be displayed with an invalid value
    the component validation message should not be displayed with an invalid value
*/
describe('when initializing a Form with a non-required TextInput', () => {

    const required = false;

    it('the validation message should not be displayed with a valid value', () => {
        const initialValue = 'something';

        const component = render(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        expect(component.find('Alert')).to.have.length(0);
        expect(component.find('.has-error')).to.have.length(0);
    });

    it('the component validation message should not be displayed with a valid value', () => {
        const initialValue = 'something';

        const component = render(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        expect(component.find('.help-block')).to.have.length(0);
    });

    it('the validation message should not be displayed with an invalid value', () => {
        const initialValue = '';

        const component = render(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        expect(component.find('Alert')).to.have.length(0);
        expect(component.find('.has-error')).to.have.length(0);
    });

    it('the component validation message should not be displayed with an invalid value', () => {
        const initialValue = '';

        const component = render(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        expect(component.find('.help-block')).to.have.length(0);
    });

});

/* *****************************************************************************
when changing the value of a required TextInput (but not blurring)
    the global validation message should not be displayed with a valid value
    the component validation message should not be displayed with a valid value
    the global validation message should not be displayed with an invalid value
    the component validation message should not be displayed with an invalid value
*/
describe('when changing the value of a required TextInput (but not blurring)', () => {

    const required = true;

    it('the validation message should not be displayed with a valid value', () => {
        const initialValue = 'something';
        const finalValue = 'something else';

        const component = mount(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        component.find('input').simulate('change', {
            target: { value: finalValue }
        });

        expect(component.find('Alert')).to.have.length(0);
        expect(component.find('.has-error')).to.have.length(0);
    });

    // it('the component validation message should not be displayed with a valid value', () => {});

    // it('the validation message should not be displayed with an invalid value', () => {});

    // it('the component validation message should not be displayed with an invalid value', () => {});

});
<commit_msg>updated spec for validation error message on change<commit_after>// the eslint "no-unused-expressions" rule is triggered by the `expect`
// assertions in the tests. So, in order to avoid problems with eslint, I'm
// disabling that rule in this file
//
/* eslint-disable no-unused-expressions */

import React from 'react';
import { shallow, render, mount } from 'enzyme';
import chai from 'chai';
import Form from './Form';

const expect = chai.expect;

/* *****************************************************************************
the Form component
    should be a <form> element
*/
describe('the Form component', () => {

    it('should be a <form> element', () => {
        const component = shallow(<Form />);

        expect(component.is('form')).to.equal(true);
    });

});

/* *****************************************************************************
a Form component with a TextInput element
    should include a <Form.TextInput> as a child
*/
describe('a Form component with a TextInput element', () => {

    it('should include a <Form.TextInput> as a child', () => {
        const component = shallow(
            <Form>
                <Form.TextInput />
            </Form>
        );

        expect(component.find(Form.TextInput)).to.have.length(1);
    });
});

/* *****************************************************************************
when initializing a Form with a required TextInput
    the global validation message should not be displayed with a valid value
    the component validation message should not be displayed with a valid value
    the global validation message should not be displayed with an invalid value
    the component validation message should not be displayed with an invalid value
*/
describe('when initializing a Form with a required TextInput', () => {

    const required = true;

    it('the validation message should not be displayed with a valid value', () => {
        const initialValue = 'something';

        const component = render(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        expect(component.find('Alert')).to.have.length(0);
        expect(component.find('.has-error')).to.have.length(0);
    });

    it('the component validation message should not be displayed with a valid value', () => {
        const initialValue = 'something';

        const component = render(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        expect(component.find('.help-block')).to.have.length(0);
    });

    it('the validation message should not be displayed with an invalid value', () => {
        const initialValue = '';

        const component = render(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        expect(component.find('Alert')).to.have.length(0);
        expect(component.find('.has-error')).to.have.length(0);
    });

    it('the component validation message should not be displayed with an invalid value', () => {
        const initialValue = '';

        const component = render(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        expect(component.find('.help-block')).to.have.length(0);
    });

});

/* *****************************************************************************
when initializing a Form with a non-required TextInput
    the global validation message should not be displayed with a valid value
    the component validation message should not be displayed with a valid value
    the global validation message should not be displayed with an invalid value
    the component validation message should not be displayed with an invalid value
*/
describe('when initializing a Form with a non-required TextInput', () => {

    const required = false;

    it('the validation message should not be displayed with a valid value', () => {
        const initialValue = 'something';

        const component = render(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        expect(component.find('Alert')).to.have.length(0);
        expect(component.find('.has-error')).to.have.length(0);
    });

    it('the component validation message should not be displayed with a valid value', () => {
        const initialValue = 'something';

        const component = render(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        expect(component.find('.help-block')).to.have.length(0);
    });

    it('the validation message should not be displayed with an invalid value', () => {
        const initialValue = '';

        const component = render(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        expect(component.find('Alert')).to.have.length(0);
        expect(component.find('.has-error')).to.have.length(0);
    });

    it('the component validation message should not be displayed with an invalid value', () => {
        const initialValue = '';

        const component = render(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        expect(component.find('.help-block')).to.have.length(0);
    });

});

/* *****************************************************************************
when changing the value of a required TextInput (but not blurring)
    the global validation message should not be displayed with a valid value
    the component validation message should not be displayed with a valid value
    the global validation message should not be displayed with an invalid value
    the component validation message should not be displayed with an invalid value
*/
describe('when changing the value of a required TextInput (but not blurring)', () => {

    const required = true;

    it('the validation message should not be displayed with a valid value', () => {
        const initialValue = 'something';
        const finalValue = 'something else';

        const component = mount(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        component.find('input').simulate('change', {
            target: { value: finalValue }
        });

        expect(component.find('Alert')).to.have.length(0);
        expect(component.find('.has-error')).to.have.length(0);
    });

    it('the component validation message should not be displayed with a valid value', () => {
        const initialValue = 'something';
        const finalValue = 'something else';

        const component = mount(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        component.find('input').simulate('change', {
            target: { value: finalValue }
        });

        expect(component.find('.help-block')).to.have.length(0);
    });

    it('the validation message should not be displayed with an invalid value', () => {
        const initialValue = 'something';
        const finalValue = '';

        const component = mount(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        component.find('input').simulate('change', {
            target: { value: finalValue }
        });

        expect(component.find('Alert')).to.have.length(0);
        expect(component.find('.has-error')).to.have.length(0);
    });

    it('the component validation message should not be displayed with an invalid value', () => {
        const initialValue = 'something';
        const finalValue = '';

        const component = mount(
            <Form>
                <Form.TextInput required={required} value={initialValue} />
            </Form>
        );

        component.find('input').simulate('change', {
            target: { value: finalValue }
        });

        expect(component.find('.help-block')).to.have.length(0);
    });

});
<|endoftext|>


<commit_before>const serviceService = require('../services/service-service');
const { Service } = require('../models/service');
const { checkStructure, createUserError } = require('../../utils');

/**
 * Fetch all the services from the database.
 *
 * @param req Request
 * @param res Response
 * @return {Promise.<void>} Nothing
 */
async function getAllServices(req, res) {
    const services = await serviceService.getAllServices();

    res.json(services);
}

/**
 * Create a service.
 *
 * @param req Request
 * @param res Response
 * @return {Promise.<void>} Nothing
 */
async function createService(req, res) {

    // FIXME We should check the type of each provided field, instead of just the presence
    if (!checkStructure(req.body, ['name', 'startAt', 'endAt', 'nbMax', 'category'])) {
        throw createUserError(
            'BadRequest',
            'The body has missing properties, needed: [\'name\', \'startAt\', \'endAt\', \'nbMax\', \'category\']'
        );
    }

    let newService = new Service({
        name: req.body.name,
        startAt: req.body.startAt,
        endAt: req.body.endAt,
        nbMax: req.body.nbMax,
        category: req.body.category,
    });

    newService = await serviceService.createService(newService);

    res.json(newService);
}

/**
 * Get a service by its id.
 *
 * @param req Request
 * @param res Response
 * @return {Promise.<void>} Nothing
 */
async function getServiceById(req, res) {
    const serviceId = req.params.id;

    const service = await serviceService.getServiceById(serviceId);

    res.json(service);
}

/**
 * Update a service.
 *
 * @param req Request
 * @param res Response
 * @return {Promise.<void>} Nothing
 */
async function updateService(req, res) {

    // FIXME We should check the type of each provided field, instead of just the presence
    if (!checkStructure(req.body, ['name', 'startAt', 'endAt', 'nbMax', 'category'])) {
        throw createUserError(
            'BadRequest',
            'The body has missing properties, needed: [\'name\', \'startAt\', \'endAt\', \'nbMax\', \'category\']'
        );
    }

    let newService = new Service({
        name: req.body.name,
        startAt: req.body.startAt,
        endAt: req.body.endAt,
        nbMax: req.body.nbMax,
        category: req.body.category,
    });

    const serviceId = req.params.id;

    newService = await serviceService.updateService(serviceId, newService);

    res.json(newService);
}

/**
 * Delete a service.
 *
 * @param req Request
 * @param res Response
 * @return {Promise.<void>} Nothing
 */
async function deleteService(req, res) {
    const serviceId = req.params.id;

    const service = await serviceService.deleteService(serviceId);

    res.json(service);
}

module.exports = {
    getAllServices,
    createService,
    updateService,
    getServiceById,
    deleteService
};
<commit_msg>Fix for review<commit_after>const serviceService = require('../services/service-service');
const { Service } = require('../models/');
const { checkStructure, createUserError } = require('../../utils');

/**
 * Fetch all the services from the database.
 *
 * @param req Request
 * @param res Response
 * @return {Promise.<void>} Nothing
 */
async function getAllServices(req, res) {
    const services = await serviceService.getAllServices();

    res.json(services);
}

/**
 * Create a service.
 *
 * @param req Request
 * @param res Response
 * @return {Promise.<void>} Nothing
 */
async function createService(req, res) {

    // FIXME We should check the type of each provided field, instead of just the presence
    if (!checkStructure(req.body, ['startAt', 'endAt', 'nbMax', 'category'])) {
        throw createUserError(
            'BadRequest',
            'The body has missing properties, needed: [\'startAt\', \'endAt\', \'nbMax\', \'category\']'
        );
    }

    let newService = new Service({
        name: req.body.name,
        startAt: req.body.startAt,
        endAt: req.body.endAt,
        nbMax: req.body.nbMax,
        category: req.body.category,
    });

    newService = await serviceService.createService(newService);

    res.json(newService);
}

/**
 * Get a service by its id.
 *
 * @param req Request
 * @param res Response
 * @return {Promise.<void>} Nothing
 */
async function getServiceById(req, res) {
    const serviceId = req.params.id;

    const service = await serviceService.getServiceById(serviceId);

    res.json(service);
}

/**
 * Update a service.
 *
 * @param req Request
 * @param res Response
 * @return {Promise.<void>} Nothing
 */
async function updateService(req, res) {

    // FIXME We should check the type of each provided field, instead of just the presence
    if (!checkStructure(req.body, ['startAt', 'endAt', 'nbMax', 'category'])) {
        throw createUserError(
            'BadRequest',
            'The body has missing properties, needed: [\'startAt\', \'endAt\', \'nbMax\', \'category\']'
        );
    }

    let newService = new Service({
        name: req.body.name,
        startAt: req.body.startAt,
        endAt: req.body.endAt,
        nbMax: req.body.nbMax,
        category: req.body.category,
    });

    const serviceId = req.params.id;

    newService = await serviceService.updateService(serviceId, newService);

    res.json(newService);
}

/**
 * Delete a service.
 *
 * @param req Request
 * @param res Response
 * @return {Promise.<void>} Nothing
 */
async function deleteService(req, res) {
    const serviceId = req.params.id;

    const service = await serviceService.deleteService(serviceId);

    res.json(service);
}

module.exports = {
    getAllServices,
    createService,
    updateService,
    getServiceById,
    deleteService
};
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import Head from '../Head/Head';

class Home extends Component {
    render() {
        return (

        );
    }
}

export default Home;<commit_msg>Updating Header component reference<commit_after>import React, {Component} from 'react';
import Head from '../Header/Header';

class Home extends Component {
    render() {
        return (

        );
    }
}

export default Home;<|endoftext|>


<commit_before>var multiparty = require('multiparty'),
    xlsx = require('node-xlsx'),
    util = require('util'),
    path = require('path');

    processGRNmap = function (path, res, app) {
      var sheet,
          currentSheet,
          network = {
            genes: [],
            links: [],
            errors: [],
            positiveWeights: [],
            negativeWeights: []
          },
          currentLink,
          currentGene,
          currentGenePair;

      try {
        sheet = xlsx.parse(path);
      } catch (err) {
        return res.json(400, "Unable to read input. The file may be corrupt.");
      }

      // For the time being, send the result in a form readable by people
      //TODO: Optimize the result for D3
      res.header('Access-Control-Allow-Origin', app.get('corsOrigin'));
      //Look for the worksheet containing the network data
      for (var i = 0; i < sheet.worksheets.length; i++) {
        if (sheet.worksheets[i].name == "network") {
          //Here we have found a sheet containing simple data. We keep looking
          //in case there is also a sheet with optimized weights
          currentSheet = sheet.worksheets[i];
        } else if (sheet.worksheets[i].name == "network_optimized_weights") {
          //We found a sheet with optimized weights, which is the ideal data source.
          //So we stop looking.
          currentSheet = sheet.worksheets[i];
          break;
        }
      }

      if(currentSheet === undefined) {
        return res.json(400, "This file does not have a 'network' sheet or a 'network_optimized_weights' sheet. Please select another" +
          " file, or rename the sheet containing the adjacency matrix accordingly. Please refer to the " +
          "Documentation page for more information.");
      }

      for (var j = 1; j < currentSheet.data.length; j++) {
        try {
          try {
            currentGene = {name: currentSheet.data[0][j].value}
            currentGenePair = {name: currentSheet.data[j][0].value}
            console.log("Current gene: " + currentGene.name + ".  Current Pair: " + currentGenePair.name);
          } catch (err) {
            return res.json(400, "One of your gene names appears to be corrupt. Please fix the error and try uploading again.");
          }
          if(currentGene.name != currentGenePair.name) {
            return res.json(400, "One of your gene names appears not to have a corresponding pair. Please fix the error and try uploading again.");
          }
          if(currentSheet.data[0][j].value.length > 12 ) {
            return res.json(400, "Gene names must be at most 12 characters in length. The gene " + currentSheet.data[0][j].value +
              " is greater than 12 characters. Please edit the name and resubmit your sheet.");
          }
          network.genes.push(currentGene);
        } catch (err) {
          network.errors.push(err.message);
        }
        for(var k = 1; k < currentSheet.data[j].length; k++) {
          try {
            if (currentSheet.data[j][k].value != 0) {
              currentLink = {source: k - 1, target: j - 1, value: currentSheet.data[j][k].value};
              if (currentLink.value > 0) {
                currentLink.type = "arrowhead";
                currentLink.stroke = "MediumVioletRed";
                network.positiveWeights.push(currentLink.value);
              } else {
                currentLink.type = "repressor";
                currentLink.stroke = "DarkTurquoise";
                network.negativeWeights.push(currentLink.value);
              }
              network.links.push(currentLink);
            }
          } catch (err) {
            network.errors.push(err.message);
          }
        }
      }
      return res.json(network);
    };

module.exports = function (app) {
  //parse the incoming form data, then parse the spreadsheet. Finally, send back json.
  app.post('/upload', function (req, res) {
    //TODO: Add file validation
    (new multiparty.Form()).parse(req, function (err, fields, files) {
      if (err) {
        return res.json(400, "There was a problem uploading your file. Please try again.");
      }

      try {
        var input = files.file[0].path;
      } catch (err) {
        return res.json(400, "No upload file selected.");
      }

      if (path.extname(input) !== ".xlsx") {
        return res.json(400, "Invalid input file. Please select an Excel Workbook (*.xlsx) file." +
          "

Note that Excel 97-2003 Workbook (*.xls) files are not able to be read by GRNsight.");
      }

      return processGRNmap(input, res, app);
    });
  });

  app.get('/demo/unweighted', function (req, res) {
    return processGRNmap("test/dahlquist_wt-data_21-gene-sample-input_20140122.xlsx", res, app);
  });

  app.get('/demo/weighted', function (req, res) {
    return processGRNmap("test/dahlquist_wt-data_21-gene-_sample-output_20140122_est_out_1.xlsx", res, app);
  });
}<commit_msg>Removed a console.log<commit_after>var multiparty = require('multiparty'),
    xlsx = require('node-xlsx'),
    util = require('util'),
    path = require('path');

    processGRNmap = function (path, res, app) {
      var sheet,
          currentSheet,
          network = {
            genes: [],
            links: [],
            errors: [],
            positiveWeights: [],
            negativeWeights: []
          },
          currentLink,
          currentGene,
          currentGenePair;

      try {
        sheet = xlsx.parse(path);
      } catch (err) {
        return res.json(400, "Unable to read input. The file may be corrupt.");
      }

      // For the time being, send the result in a form readable by people
      //TODO: Optimize the result for D3
      res.header('Access-Control-Allow-Origin', app.get('corsOrigin'));
      //Look for the worksheet containing the network data
      for (var i = 0; i < sheet.worksheets.length; i++) {
        if (sheet.worksheets[i].name == "network") {
          //Here we have found a sheet containing simple data. We keep looking
          //in case there is also a sheet with optimized weights
          currentSheet = sheet.worksheets[i];
        } else if (sheet.worksheets[i].name == "network_optimized_weights") {
          //We found a sheet with optimized weights, which is the ideal data source.
          //So we stop looking.
          currentSheet = sheet.worksheets[i];
          break;
        }
      }

      if(currentSheet === undefined) {
        return res.json(400, "This file does not have a 'network' sheet or a 'network_optimized_weights' sheet. Please select another" +
          " file, or rename the sheet containing the adjacency matrix accordingly. Please refer to the " +
          "Documentation page for more information.");
      }

      for (var j = 1; j < currentSheet.data.length; j++) {
        try {
          try {
            currentGene = {name: currentSheet.data[0][j].value}
            currentGenePair = {name: currentSheet.data[j][0].value}
          } catch (err) {
            return res.json(400, "One of your gene names appears to be corrupt. Please fix the error and try uploading again.");
          }
          if(currentGene.name != currentGenePair.name) {
            return res.json(400, "One of your gene names appears not to have a corresponding pair. Please fix the error and try uploading again.");
          }
          if(currentSheet.data[0][j].value.length > 12 ) {
            return res.json(400, "Gene names must be at most 12 characters in length. The gene " + currentSheet.data[0][j].value +
              " is greater than 12 characters. Please edit the name and resubmit your sheet.");
          }
          network.genes.push(currentGene);
        } catch (err) {
          network.errors.push(err.message);
        }
        for(var k = 1; k < currentSheet.data[j].length; k++) {
          try {
            if (currentSheet.data[j][k].value != 0) {
              currentLink = {source: k - 1, target: j - 1, value: currentSheet.data[j][k].value};
              if (currentLink.value > 0) {
                currentLink.type = "arrowhead";
                currentLink.stroke = "MediumVioletRed";
                network.positiveWeights.push(currentLink.value);
              } else {
                currentLink.type = "repressor";
                currentLink.stroke = "DarkTurquoise";
                network.negativeWeights.push(currentLink.value);
              }
              network.links.push(currentLink);
            }
          } catch (err) {
            network.errors.push(err.message);
          }
        }
      }
      return res.json(network);
    };

module.exports = function (app) {
  //parse the incoming form data, then parse the spreadsheet. Finally, send back json.
  app.post('/upload', function (req, res) {
    //TODO: Add file validation
    (new multiparty.Form()).parse(req, function (err, fields, files) {
      if (err) {
        return res.json(400, "There was a problem uploading your file. Please try again.");
      }

      try {
        var input = files.file[0].path;
      } catch (err) {
        return res.json(400, "No upload file selected.");
      }

      if (path.extname(input) !== ".xlsx") {
        return res.json(400, "Invalid input file. Please select an Excel Workbook (*.xlsx) file." +
          "

Note that Excel 97-2003 Workbook (*.xls) files are not able to be read by GRNsight.");
      }

      return processGRNmap(input, res, app);
    });
  });

  app.get('/demo/unweighted', function (req, res) {
    return processGRNmap("test/dahlquist_wt-data_21-gene-sample-input_20140122.xlsx", res, app);
  });

  app.get('/demo/weighted', function (req, res) {
    return processGRNmap("test/dahlquist_wt-data_21-gene-_sample-output_20140122_est_out_1.xlsx", res, app);
  });
}<|endoftext|>