Compare commits
8 Commits
f4a8db56ff
...
develop
Author | SHA1 | Date | |
---|---|---|---|
4141f42ad8 | |||
3562021da7 | |||
2859574848 | |||
4a321b4105 | |||
7a6e9dec95 | |||
8e1e644904 | |||
c930ffc55e | |||
8c870cb43f |
4
.common.env.sample
Normal file
4
.common.env.sample
Normal file
@ -0,0 +1,4 @@
|
||||
DEBUG=true
|
||||
DB_HOST="db"
|
||||
ENV='dev'
|
||||
API_URL='http://localhost:8081'
|
4
.db.env.sample
Normal file
4
.db.env.sample
Normal file
@ -0,0 +1,4 @@
|
||||
MYSQL_ROOT_PASSWORD=
|
||||
MYSQL_USER=
|
||||
MYSQL_PASSWORD=
|
||||
MYSQL_DATABASE=
|
2
.env.sample
Normal file
2
.env.sample
Normal file
@ -0,0 +1,2 @@
|
||||
FRONTEND_PORT=#Port for frontend UI
|
||||
BACKEND_PORT=#Port for backend app
|
9
backend/Cron.Dockerfile
Normal file
9
backend/Cron.Dockerfile
Normal file
@ -0,0 +1,9 @@
|
||||
FROM ubuntu
|
||||
|
||||
RUN apt-get update && apt-get install -y cron
|
||||
|
||||
COPY ./automation/crontab /var/spool/cron/crontabs/backend
|
||||
|
||||
#ENTRYPOINT [ "/bin/bash" ]
|
||||
|
||||
CMD ["cron", "-f"]
|
@ -1,37 +1,13 @@
|
||||
FROM continuumio/miniconda3 as build
|
||||
FROM python:3.9 as runtime
|
||||
|
||||
WORKDIR /app
|
||||
WORKDIR /app/
|
||||
|
||||
COPY ./python /app/src
|
||||
COPY ./python/ /app/
|
||||
|
||||
RUN conda env create -f src/environment.yml
|
||||
RUN python -m pip install gunicorn httpx flask
|
||||
|
||||
#RUN echo "conda activate cryptos" >> ~/.bashrc
|
||||
#SHELL ["/bin/bash", "--login", "-c"]
|
||||
EXPOSE 5001
|
||||
|
||||
RUN conda install -c conda-forge conda-pack
|
||||
CMD ["gunicorn", "-b", "0.0.0.0:5001", "crypto.index:app"]
|
||||
|
||||
RUN conda pack -n cryptos -o /tmp/env.tar && \
|
||||
mkdir /venv && cd /venv && tar xf /tmp/env.tar && \
|
||||
rm /tmp/env.tar
|
||||
|
||||
RUN /venv/bin/conda-unpack
|
||||
|
||||
|
||||
FROM python:buster as runtime
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./python /app/src
|
||||
COPY ./api/bin /app/bin
|
||||
|
||||
COPY --from=build /venv /venv
|
||||
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
RUN pip install pyinstaller
|
||||
|
||||
RUN pyinstaller -F -n coingecko --clean --log-level DEBUG --distpath /app/bin /app/src/coingecko.py && \
|
||||
pyinstaller -F -n mindicador --clean --log-level DEBUG --distpath /app/bin /app/src/miindicador.py
|
||||
|
||||
ENTRYPOINT [ "/bin/bash" ]
|
||||
#ENTRYPOINT [ "/bin/bash" ]
|
||||
|
@ -63,4 +63,108 @@ class Coins {
|
||||
$output['deleted'] = $status;
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function values(Request $request, Response $response, ModelFactory $factory, $coin_id): Response {
|
||||
$coin = $factory->find(Coin::class)->one($coin_id);
|
||||
if (!$coin) {
|
||||
return $this->withJson($response, ['coin' => null, 'values' => []]);
|
||||
}
|
||||
$values = $coin->values();
|
||||
if ($values === null) {
|
||||
return $this->withJson($response, [
|
||||
'coin' => $coin->toArray(),
|
||||
'values' => []
|
||||
]);
|
||||
}
|
||||
usort($values, function($a, $b) {
|
||||
return $a->dateTime()->timestamp - $b->dateTime()->timestamp;
|
||||
});
|
||||
$values = array_map(function($item) {
|
||||
$arr = $item->toArray();
|
||||
$arr['formatted'] = $item->unit()->format($item->value);
|
||||
return $arr;
|
||||
}, $values);
|
||||
$output = [
|
||||
'coin' => $coin->toArray(),
|
||||
'values' => $values
|
||||
];
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function valuesMonth(Request $request, Response $response, ModelFactory $factory, $coin_id): Response {
|
||||
$coin = $factory->find(Coin::class)->one($coin_id);
|
||||
if (!$coin) {
|
||||
return $this->withJson($response, ['coin' => null, 'values' => []]);
|
||||
}
|
||||
$values = $coin->values('month');
|
||||
if ($values === null) {
|
||||
return $this->withJson($response, [
|
||||
'coin' => $coin->toArray(),
|
||||
'values' => []
|
||||
]);
|
||||
}
|
||||
usort($values, function($a, $b) {
|
||||
return $a->dateTime()->timestamp - $b->dateTime()->timestamp;
|
||||
});
|
||||
$values = array_map(function($item) {
|
||||
$arr = $item->toArray();
|
||||
$arr['formatted'] = $item->unit()->format($item->value);
|
||||
return $arr;
|
||||
}, $values);
|
||||
$output = [
|
||||
'coin' => $coin->toArray(),
|
||||
'values' => $values
|
||||
];
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function valuesSixMonths(Request $request, Response $response, ModelFactory $factory, $coin_id): Response {
|
||||
$coin = $factory->find(Coin::class)->one($coin_id);
|
||||
if (!$coin) {
|
||||
return $this->withJson($response, ['coin' => null, 'values' => []]);
|
||||
}
|
||||
$values = $coin->values('months');
|
||||
if ($values === null) {
|
||||
return $this->withJson($response, [
|
||||
'coin' => $coin->toArray(),
|
||||
'values' => []
|
||||
]);
|
||||
}
|
||||
usort($values, function($a, $b) {
|
||||
return $a->dateTime()->timestamp - $b->dateTime()->timestamp;
|
||||
});
|
||||
$values = array_map(function($item) {
|
||||
$arr = $item->toArray();
|
||||
$arr['formatted'] = $item->unit()->format($item->value);
|
||||
return $arr;
|
||||
}, $values);
|
||||
$output = [
|
||||
'coin' => $coin->toArray(),
|
||||
'values' => $values
|
||||
];
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function valuesYear(Request $request, Response $response, ModelFactory $factory, $coin_id): Response {
|
||||
$coin = $factory->find(Coin::class)->one($coin_id);
|
||||
if (!$coin) {
|
||||
return $this->withJson($response, ['coin' => null, 'values' => []]);
|
||||
}
|
||||
$values = $coin->values('year');
|
||||
if ($values === null) {
|
||||
return $this->withJson($response, [
|
||||
'coin' => $coin->toArray(),
|
||||
'values' => []
|
||||
]);
|
||||
}
|
||||
usort($values, function($a, $b) {
|
||||
return $a->dateTime()->timestamp - $b->dateTime()->timestamp;
|
||||
});
|
||||
$values = array_map(function($item) {
|
||||
$arr = $item->toArray();
|
||||
$arr['formatted'] = $item->unit()->format($item->value);
|
||||
return $arr;
|
||||
}, $values);
|
||||
$output = [
|
||||
'coin' => $coin->toArray(),
|
||||
'values' => $values
|
||||
];
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,18 @@
|
||||
<?php
|
||||
namespace ProVM\Crypto\Common\Service;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use Carbon\Carbon;
|
||||
use ProVM\Common\Factory\Model as Factory;
|
||||
use ProVM\Crypto\Coin;
|
||||
use ProVM\Crypto\Value;
|
||||
|
||||
class Update {
|
||||
protected $factory;
|
||||
protected $execs;
|
||||
public function __construct(Factory $factory, array $executables) {
|
||||
protected $client;
|
||||
public function __construct(Factory $factory, Client $client) {
|
||||
$this->factory = $factory;
|
||||
$this->execs = $executables;
|
||||
$this->client = $client;
|
||||
$this->load();
|
||||
}
|
||||
public function load() {
|
||||
@ -22,39 +24,154 @@ class Update {
|
||||
}
|
||||
protected $coins;
|
||||
public function register(int $coin_id, int $type = 0) {
|
||||
/*if (array_search($coin_id, $this->coins[$type]) !== false) {
|
||||
return;
|
||||
}*/
|
||||
if (array_search($coin_id, $this->coins[$type]) !== false) {
|
||||
return false;
|
||||
}
|
||||
$this->coins[$type] []= $coin_id;
|
||||
$this->getHistorical($coin_id, $type);
|
||||
$check = \ORM::for_table('coin_registers')->where('coin_id', $coin_id)->find_one();
|
||||
if (!$check) {
|
||||
\ORM::raw_execute("INSERT INTO coin_registers (coin_id, type) VALUES (?, ?)", [$coin_id, $type]);
|
||||
return \ORM::raw_execute("INSERT INTO coin_registers (coin_id, type, date_Time) VALUES (?, ?, NOW())", [$coin_id, $type]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
protected function getHistorical(int $coin_id, int $type) {
|
||||
$coin = $this->factory->find(Coin::class)->one($coin_id);
|
||||
$f = Carbon::now();
|
||||
$exe = [$this->execs[$type]];
|
||||
$url = [];
|
||||
switch ($type) {
|
||||
case 0:
|
||||
$exe []= '-i ' . $coin->identifier;
|
||||
$exe []= '-c usd,clp';
|
||||
$exe []= 'hist -hi';
|
||||
$exe []= '-f ' . $f->copy()->subYears(10)->timestamp;
|
||||
$exe []= '-t ' . $f->timestamp();
|
||||
break;
|
||||
return $this->getHistoricalCrypto($coin);
|
||||
case 1:
|
||||
$exe []= '-i ' . $coin->identifier;
|
||||
$exe []= 'hist -hi';
|
||||
$exe []= '-s ' . $f->copy()->subYears(10)->year;
|
||||
break;
|
||||
return $this->getHistoricalIndicador($coin);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
!d(implode(' ', $exe));
|
||||
$output = shell_exec(implode(' ', $exe));
|
||||
!d($output);
|
||||
}
|
||||
protected function getHistoricalCrypto(Coin $coin) {
|
||||
$f = Carbon::now();
|
||||
$url = [];
|
||||
$url []= 'crypto';
|
||||
$url []= 'historical';
|
||||
$url []= $coin->identifier;
|
||||
$url []= $f->copy()->subYears(10)->timestamp;
|
||||
$url []= $f->timestamp;
|
||||
$url = implode('/', $url);
|
||||
$to = $this->factory->find(Coin::class)->where([['code', 'USD']])->one();
|
||||
$response = $this->client->get($url);
|
||||
$results = json_decode($response->getBody()->getContents());
|
||||
$created = [];
|
||||
foreach ($results->prices as $result) {
|
||||
$d = Carbon::createFromTimestamp(substr($result[0], 0, -3));
|
||||
$value = $result[1];
|
||||
$data = [
|
||||
'date_time' => $d->format('Y-m-d H:i:s'),
|
||||
'coin_id' => $coin->id,
|
||||
'value' => $value,
|
||||
'unit_id' => $to->id
|
||||
];
|
||||
$value = Value::add($this->factory, $data);
|
||||
$status = $value->save();
|
||||
$created []= [
|
||||
'value' => $value->toArray(),
|
||||
'created' => $status
|
||||
];
|
||||
}
|
||||
return $created;
|
||||
}
|
||||
protected function getHistoricalIndicador(Coin $coin) {
|
||||
$f = Carbon::now();
|
||||
$urls = [];
|
||||
for ($i = 10; $i >= 0; $i --) {
|
||||
$url = [];
|
||||
$url []= 'indicador';
|
||||
$url []= 'historical';
|
||||
$url []= $coin->identifier;
|
||||
$url []= $f->copy()->subYears($i)->year;
|
||||
$urls []= implode('/', $url);
|
||||
}
|
||||
$to = $this->factory->find(Coin::class)->where([['code', 'CLP']])->one();
|
||||
$created = [];
|
||||
foreach ($urls as $url) {
|
||||
$response = $this->client->get($url);
|
||||
$results = json_decode($response->getBody()->getContents());
|
||||
foreach ($results->serie as $result) {
|
||||
$d = Carbon::parse($result->fecha);
|
||||
$value = $result->valor;
|
||||
$data = [
|
||||
'date_time' => $d->format('Y-m-d H:i:s'),
|
||||
'coin_id' => $coin->id,
|
||||
'value' => $value,
|
||||
'unit_id' => $to->id
|
||||
];
|
||||
$value = Value::add($this->factory, $data);
|
||||
$status = $value->save();
|
||||
$created []= [
|
||||
'value' => $value->toArray(),
|
||||
'created' => $status
|
||||
];
|
||||
}
|
||||
}
|
||||
return $created;
|
||||
}
|
||||
public function run() {
|
||||
$created = [];
|
||||
$created = array_merge($created, $this->getCryptos($this->coins[0]));
|
||||
$created = array_merge($created, $this->getIndicadores($this->coins[1]));
|
||||
return $created;
|
||||
}
|
||||
protected function getCryptos(array $coins) {
|
||||
$created = [];
|
||||
foreach ($coins as $coin_id) {
|
||||
$coin = $this->factory->find(Coin::class)->one($coin_id);
|
||||
$url = [];
|
||||
$url []= 'crypto';
|
||||
$url []= $coin->identifier;
|
||||
$url = implode('/', $url);
|
||||
$to = $this->factory->find(Coin::class)->where([['code', 'USD']])->one();
|
||||
$response = $this->client->get($url);
|
||||
$results = json_decode($response->getBody()->getContents());
|
||||
$data = [
|
||||
'date_time' => $results->n->last_updated_at,
|
||||
'coin_id' => $coin->id,
|
||||
'value' => 1 / $results->n->usd,
|
||||
'unit_id' => $to->id
|
||||
];
|
||||
$value = Value::add($this->factory, $data);
|
||||
$status = $value->save();
|
||||
$created []= [
|
||||
'value' => $value->toArray(),
|
||||
'created' => $status
|
||||
];
|
||||
}
|
||||
return $created;
|
||||
}
|
||||
protected function getIndicadores(array $coins) {
|
||||
$created = [];
|
||||
foreach ($coins as $coin_id) {
|
||||
$coin = $this->factory->find(Coin::class)->one($coin_id);
|
||||
$f = Carbon::now();
|
||||
$url = [];
|
||||
$url []= 'indicador';
|
||||
$url []= $coin->identifier;
|
||||
$url []= $f->format('d-m-Y');
|
||||
$url = implode('/', $url);
|
||||
$to = $this->factory->find(Coin::class)->where([['code', 'CLP']])->one();
|
||||
$response = $this->client->get($url);
|
||||
$results = json_decode($response->getBody()->getContents());
|
||||
$data = [
|
||||
'date_time' => $results->serie[0]->fecha,
|
||||
'coin_id' => $coin->id,
|
||||
'value' => $results->serie[0]->valor,
|
||||
'unit_id' => $to->id
|
||||
];
|
||||
$value = Value::add($this->factory, $data);
|
||||
$status = $value->save();
|
||||
$created []= [
|
||||
'value' => $value->toArray(),
|
||||
'created' => $status
|
||||
];
|
||||
}
|
||||
return $created;
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,8 @@
|
||||
"provm/models": "^1.0-rc",
|
||||
"spatie/crypto": "^2.0",
|
||||
"robmorgan/phinx": "^0.12.5",
|
||||
"nesbot/carbon": "^2.49"
|
||||
"nesbot/carbon": "^2.49",
|
||||
"guzzlehttp/guzzle": "^7.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5",
|
||||
|
@ -9,5 +9,11 @@ $app->group('/coins', function($app) {
|
||||
$app->group('/coin/{coin_id}', function($app) {
|
||||
$app->put('/edit', [Coins::class, 'edit']);
|
||||
$app->delete('/delete', [Coins::class, 'delete']);
|
||||
$app->group('/values', function($app) {
|
||||
$app->get('/month', [Coins::class, 'valuesMonth']);
|
||||
$app->get('/months', [Coins::class, 'valuesSixMonths']);
|
||||
$app->get('/year', [Coins::class, 'valuesYear']);
|
||||
$app->get('[/]', [Coins::class, 'values']);
|
||||
});
|
||||
$app->get('[/]', [Coins::class, 'show']);
|
||||
});
|
||||
|
@ -23,16 +23,5 @@ return [
|
||||
]);
|
||||
return (object) $arr;
|
||||
},
|
||||
'coingecko' => function(Container $c) {
|
||||
return implode(DIRECTORY_SEPARATOR, [
|
||||
$c->get('locations')->bin,
|
||||
'coingecko'
|
||||
]);
|
||||
},
|
||||
'mindicador' => function(Container $c) {
|
||||
return implode(DIRECTORY_SEPARATOR, [
|
||||
$c->get('locations')->bin,
|
||||
'mindicador'
|
||||
]);
|
||||
}
|
||||
'python_api' => $_ENV['PYTHON_API']
|
||||
];
|
||||
|
@ -12,13 +12,13 @@ return [
|
||||
ProVM\Common\Factory\Model::class => function(Container $container) {
|
||||
return new ProVM\Crypto\Common\Factory\Model();
|
||||
},
|
||||
GuzzleHttp\Client::class => function(Container $container) {
|
||||
return new GuzzleHttp\Client(['base_uri' => $container->get('python_api')]);
|
||||
},
|
||||
ProVM\Crypto\Common\Service\Update::class => function(Container $container) {
|
||||
return new ProVM\Crypto\Common\Service\Update(
|
||||
$container->get(ProVM\Crypto\Common\Factory\Model::class),
|
||||
[
|
||||
$container->get('coingecko'),
|
||||
$container->get('mindicador')
|
||||
]
|
||||
$container->get(GuzzleHttp\Client::class)
|
||||
);
|
||||
}
|
||||
];
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace ProVM\Crypto;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use ProVM\Common\Alias\Model;
|
||||
use ProVM\Common\Factory\Model as Factory;
|
||||
|
||||
@ -16,19 +17,52 @@ use ProVM\Common\Factory\Model as Factory;
|
||||
*/
|
||||
class Coin extends Model {
|
||||
protected static $_table = 'coins';
|
||||
protected static $fields = ['code', 'name', 'prefix', 'suffix', 'decimals', 'ref_url'];
|
||||
protected static $fields = ['code', 'name', 'identifier', 'prefix', 'suffix', 'decimals', 'ref_url'];
|
||||
|
||||
public function format(float $value): string {
|
||||
$output = [];
|
||||
if ($this->prefix == '') {
|
||||
if ($this->prefix != '') {
|
||||
$output []= $this->prefix;
|
||||
}
|
||||
$output []= number_format($value, $this->decimals ?? 0, ',', '.');
|
||||
if ($this->suffix == '') {
|
||||
if ($this->suffix != '') {
|
||||
$output []= $this->suffix;
|
||||
}
|
||||
return implode(' ', $output);
|
||||
}
|
||||
protected $values;
|
||||
public function values($period = null) {
|
||||
if ($this->values === null) {
|
||||
$this->values = $this->parentOf(Value::class, [Model::CHILD_KEY => 'coin_id']);
|
||||
}
|
||||
if ($this->values === null) {
|
||||
return $this->values;
|
||||
}
|
||||
if ($period === null) {
|
||||
return $this->values;
|
||||
}
|
||||
$f = Carbon::now();
|
||||
switch ($period) {
|
||||
case 'month':
|
||||
case 'mes':
|
||||
$m = $f->copy()->subMonths(1);
|
||||
return array_filter($this->values, function($item) use ($m) {
|
||||
return ($item->dateTime()->greaterThanOrEqualTo($m));
|
||||
});
|
||||
case 'months':
|
||||
case 'meses':
|
||||
$m = $f->copy()->subMonths(6);
|
||||
return array_filter($this->values, function($item) use ($m) {
|
||||
return ($item->dateTime()->greaterThanOrEqualTo($m));
|
||||
});
|
||||
case 'year':
|
||||
case 'año':
|
||||
$m = $f->copy()->subYears(1);
|
||||
return array_filter($this->values, function($item) use ($m) {
|
||||
return ($item->dateTime()->greaterThanOrEqualTo($m));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static function find(Factory $factory, $input) {
|
||||
return $factory->find(Coin::class)->where([['code', $input->code]])->one();
|
||||
|
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
||||
0 2 * * * curl backend/update
|
||||
0 2 * * * curl http://backend/update
|
||||
|
Binary file not shown.
Binary file not shown.
BIN
backend/python/crypto/__pycache__/coingecko.cpython-39.pyc
Normal file
BIN
backend/python/crypto/__pycache__/coingecko.cpython-39.pyc
Normal file
Binary file not shown.
BIN
backend/python/crypto/__pycache__/index.cpython-39.pyc
Normal file
BIN
backend/python/crypto/__pycache__/index.cpython-39.pyc
Normal file
Binary file not shown.
BIN
backend/python/crypto/__pycache__/miindicador.cpython-39.pyc
Normal file
BIN
backend/python/crypto/__pycache__/miindicador.cpython-39.pyc
Normal file
Binary file not shown.
@ -6,6 +6,8 @@ import datetime
|
||||
|
||||
class CoinGecko:
|
||||
def __init__(self, base_url: str = None):
|
||||
print('Creating object CoinGecko')
|
||||
|
||||
if base_url is None:
|
||||
base_url = 'https://api.coingecko.com/api/v3'
|
||||
self.base_url = base_url
|
||||
@ -30,10 +32,14 @@ class CoinGecko:
|
||||
return json.loads(resp.text)
|
||||
|
||||
def list(self):
|
||||
print('Getting list of coins from {}'.format(self.base_url))
|
||||
|
||||
url = self.__build_url('coins/list')
|
||||
return self.__get(url)
|
||||
|
||||
def get(self, ids: tuple, currencies: tuple, last_updated: bool = True):
|
||||
print('Getting {} in {} from {}'.format(ids, currencies, self.base_url))
|
||||
|
||||
sub = 'simple/price'
|
||||
query = '&'.join([
|
||||
'='.join(['ids', ','.join(ids)]),
|
||||
@ -48,6 +54,8 @@ class CoinGecko:
|
||||
return res
|
||||
|
||||
def historical(self, id_: str, currency: str, from_: str, to: str):
|
||||
print('Getting historical data for {} in {} from {} to {} from {}'.format(id_, currency, from_, to, self.base_url))
|
||||
|
||||
sub = '/'.join([
|
||||
'coins',
|
||||
id_,
|
||||
@ -75,16 +83,19 @@ if __name__ == '__main__':
|
||||
hparser.add_argument('-f', '--from_')
|
||||
hparser.add_argument('-t', '--to')
|
||||
args = parser.parse_args()
|
||||
|
||||
print('Called with args: {}'.format(vars(args)))
|
||||
|
||||
cg = CoinGecko(args.url)
|
||||
_ids = tuple(args.ids.split(','))
|
||||
_currencies = tuple(args.currencies.split(','))
|
||||
if 'historical' in args and args.historical:
|
||||
from_ = args.from_
|
||||
if '-' in from_:
|
||||
from_ = str(datetime.datetime.fromisoformat(from_).timestamp())
|
||||
to = args.to
|
||||
if '-' in to:
|
||||
to = str(datetime.datetime.fromisoformat(to).timestamp())
|
||||
print(cg.historical(id_=_ids[0], currency=_currencies[0], from_=from_, to=to))
|
||||
from__ = args.from_
|
||||
if '-' in from__:
|
||||
from__ = str(datetime.datetime.fromisoformat(from__).timestamp())
|
||||
to_ = args.to
|
||||
if '-' in to_:
|
||||
to_ = str(datetime.datetime.fromisoformat(to_).timestamp())
|
||||
print(cg.historical(id_=_ids[0], currency=_currencies[0], from_=from__, to=to_))
|
||||
exit()
|
||||
print(cg.get(ids=_ids, currencies=_currencies))
|
50
backend/python/crypto/index.py
Normal file
50
backend/python/crypto/index.py
Normal file
@ -0,0 +1,50 @@
|
||||
from flask import Flask
|
||||
|
||||
from crypto.coingecko import CoinGecko
|
||||
from crypto.miindicador import MiIndicador
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def main():
|
||||
output = [
|
||||
"<html>",
|
||||
"<body>",
|
||||
"Welcome",
|
||||
"</body>",
|
||||
"</html>"
|
||||
]
|
||||
return "\n".join(output)
|
||||
|
||||
|
||||
@app.route('/crypto/historical/<ids>/<from_>/<to>')
|
||||
@app.route('/crypto/historical/<ids>/<from_>/<to>/<currencies>')
|
||||
def historical_crypto(ids, from_, to, currencies='usd'):
|
||||
cg = CoinGecko()
|
||||
return cg.historical(id_=ids, from_=from_, to=to, currency=currencies)
|
||||
|
||||
|
||||
@app.route('/crypto/<ids>')
|
||||
@app.route('/crypto/<ids>/<currencies>')
|
||||
def crypto(ids, currencies=('usd', 'clp')):
|
||||
cg = CoinGecko()
|
||||
return cg.get(ids, currencies)
|
||||
|
||||
|
||||
@app.route('/indicador/historical/<ind>')
|
||||
@app.route('/indicador/historical/<ind>/<since>')
|
||||
def historical_indicador(ind, since=None):
|
||||
mi = MiIndicador()
|
||||
return mi.historical(ind, since)
|
||||
|
||||
|
||||
@app.route('/indicador/<ind>')
|
||||
@app.route('/indicador/<ind>/<fecha>')
|
||||
def indicador(ind, fecha=None):
|
||||
mi = MiIndicador()
|
||||
return mi.get(ind, fecha)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5001, debug=True)
|
@ -6,6 +6,8 @@ import datetime
|
||||
|
||||
class MiIndicador:
|
||||
def __init__(self, base_url: str = None):
|
||||
print('Creating object Mindicador')
|
||||
|
||||
if base_url is None:
|
||||
base_url = 'https://mindicador.cl/api'
|
||||
self.base_url = base_url
|
||||
@ -30,10 +32,14 @@ class MiIndicador:
|
||||
return json.loads(resp.text)
|
||||
|
||||
def list(self):
|
||||
print('List possible indicators')
|
||||
|
||||
url = self.__build_url('')
|
||||
return self.__get(url)
|
||||
|
||||
def get(self, indicador: str, fecha: str = None):
|
||||
print('Getting {} in date {} from {}'.format(indicador, fecha, self.base_url))
|
||||
|
||||
url = indicador
|
||||
if fecha is not None:
|
||||
url = '/'.join([url, fecha])
|
||||
@ -45,6 +51,8 @@ class MiIndicador:
|
||||
return res
|
||||
|
||||
def historical(self, indicador: str, since: str = None):
|
||||
print('Getting historical data for {} since {} from {}'.format(indicador, since, self.base_url))
|
||||
|
||||
sub = indicador
|
||||
if since is not None:
|
||||
sub = '/'.join([sub, since])
|
||||
@ -65,6 +73,9 @@ if __name__ == '__main__':
|
||||
hparser.add_argument('-hi', '--historical', action='store_true')
|
||||
hparser.add_argument('-s', '--since')
|
||||
args = parser.parse_args()
|
||||
|
||||
print('Called with args: {}'.format(vars(args)))
|
||||
|
||||
mi = MiIndicador(args.url)
|
||||
if 'historical' in args and args.historical:
|
||||
print(mi.historical(args.indicador, args.since))
|
@ -1,10 +0,0 @@
|
||||
name: cryptos
|
||||
channels:
|
||||
- defaults
|
||||
dependencies:
|
||||
- httpx=0.17.1
|
||||
- pip=21.1.2
|
||||
- python=3.9.5
|
||||
- setuptools=52.0.0
|
||||
# - pip:
|
||||
# - pyinstaller==4.3
|
@ -1,30 +0,0 @@
|
||||
# This file may be used to create an environment using:
|
||||
# $ conda create --name <env> --file <this file>
|
||||
# platform: win-64
|
||||
altgraph=0.17=pypi_0
|
||||
ca-certificates=2021.5.25=haa95532_1
|
||||
certifi=2021.5.30=py39haa95532_0
|
||||
future=0.18.2=pypi_0
|
||||
h11=0.12.0=pyhd3eb1b0_0
|
||||
h2=4.0.0=py39haa95532_3
|
||||
hpack=4.0.0=py_0
|
||||
httpcore=0.12.3=pyhd3eb1b0_0
|
||||
httpx=0.17.1=pyhd3eb1b0_0
|
||||
hyperframe=6.0.1=pyhd3eb1b0_0
|
||||
idna=2.10=pyhd3eb1b0_0
|
||||
openssl=1.1.1k=h2bbff1b_0
|
||||
pefile=2021.5.24=pypi_0
|
||||
pip=21.1.2=py39haa95532_0
|
||||
pyinstaller=4.3=pypi_0
|
||||
pyinstaller-hooks-contrib=2021.1=pypi_0
|
||||
python=3.9.5=h6244533_3
|
||||
pywin32-ctypes=0.2.0=pypi_0
|
||||
rfc3986=1.4.0=py_0
|
||||
setuptools=52.0.0=py39haa95532_0
|
||||
sniffio=1.2.0=py39haa95532_1
|
||||
sqlite=3.35.4=h2bbff1b_0
|
||||
tzdata=2020f=h52ac0ba_0
|
||||
vc=14.2=h21ff451_1
|
||||
vs2015_runtime=14.27.29016=h5e58377_2
|
||||
wheel=0.36.2=pyhd3eb1b0_0
|
||||
wincertstore=0.2=py39h2bbff1b_0
|
3
build.python.bat
Normal file
3
build.python.bat
Normal file
@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
|
||||
docker build -f "./backend/Py.Dockerfile" .
|
6
build.sh
Normal file
6
build.sh
Normal file
@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
pyinstaller -F -n coingecko --clean --log-level DEBUG --distpath /app/bin/ /app/src/coingecko.py
|
||||
pyinstaller -F -n mindicador --clean --log-level DEBUG --distpath /app/bin/ /app/src/miindicador.py
|
||||
#python -m nuitka --follow-imports --standalone --output-dir="/app/bin" /app/src/coingecko.py
|
||||
#python -m nuitka --follow-imports --standalone --output-dir="/app/bin" /app/src/miindicador.py
|
@ -24,7 +24,7 @@ services:
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: PHP.Dockerfile
|
||||
env_file: common.env
|
||||
env_file: .common.env
|
||||
volumes:
|
||||
- .:/app
|
||||
depends_on:
|
||||
@ -36,23 +36,29 @@ services:
|
||||
context: ./backend
|
||||
dockerfile: PHP.Dockerfile
|
||||
env_file:
|
||||
- common.env
|
||||
- .common.env
|
||||
- .db.env
|
||||
volumes:
|
||||
- .:/app
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
# python:
|
||||
# container_name: python
|
||||
# build:
|
||||
# context: ./backend
|
||||
# dockerfile: Py.Dockerfile
|
||||
# volumes:
|
||||
# - ./backend/python:/app/src
|
||||
# - ./backend/api/bin:/app/bin
|
||||
# tty: true
|
||||
# stdin_open: true
|
||||
cron:
|
||||
container_name: crypto-cron
|
||||
build:
|
||||
context: ./backend
|
||||
dockerfile: Cron.Dockerfile
|
||||
volumes:
|
||||
- ./backend/automation/crontab:/var/spool/cron/crontabs/backend
|
||||
|
||||
python:
|
||||
container_name: crypto-python
|
||||
build:
|
||||
context: ./backend
|
||||
dockerfile: Py.Dockerfile
|
||||
volumes:
|
||||
- ./backend/python:/app
|
||||
- ./backend/automation/bin:/app/bin
|
||||
|
||||
db:
|
||||
container_name: crypto-db
|
||||
|
@ -12,4 +12,6 @@ class Coins {
|
||||
public function get(Request $request, Response $response, View $view, $coin_id): Response {
|
||||
return $view->render($response, 'coins.show', compact('coin_id'));
|
||||
}
|
||||
public function values(Request $request, Response $response, View $view, $coin_id): Response {
|
||||
}
|
||||
}
|
||||
|
@ -37,23 +37,26 @@
|
||||
api: '{{$urls->api}}',
|
||||
base: '{{$urls->base}}'
|
||||
},
|
||||
table: null,
|
||||
modal: null,
|
||||
setup: function(table, modal) {
|
||||
this.table = table
|
||||
this.modal = modal
|
||||
table.find('plus icon').css('cursor', 'pointer').click(() => {
|
||||
addCoin()
|
||||
})
|
||||
table.find('tbody').append($('<div></div>').attr('class', 'ui active dimmer').append(
|
||||
$('<div></div>').attr('class', 'ui loader')
|
||||
))
|
||||
table.find('.plus.icon').css('cursor', 'pointer').click((e) => {
|
||||
this.add()
|
||||
})
|
||||
this.load(table)
|
||||
this.load()
|
||||
},
|
||||
load: function(table) {
|
||||
load: function() {
|
||||
const url = this.urls.api + '/coins'
|
||||
const table = this.table
|
||||
const body = table.find('tbody')
|
||||
table.find('tbody').append($('<div></div>').attr('class', 'ui active dimmer').append(
|
||||
$('<div></div>').attr('class', 'ui loader')
|
||||
))
|
||||
$.getJSON(url, (data) => {
|
||||
body.html('')
|
||||
data.coins.forEach((v) => {
|
||||
@ -95,6 +98,12 @@
|
||||
).append(
|
||||
$('<input />').attr('type', 'text').attr('name', 'name')
|
||||
)
|
||||
).append(
|
||||
$('<div></div>').attr('class', 'field').append(
|
||||
$('<label></label>').html('Identificador')
|
||||
).append(
|
||||
$('<input />').attr('type', 'text').attr('name', 'identifier')
|
||||
)
|
||||
).append(
|
||||
$('<div></div>').attr('class', 'field').append(
|
||||
$('<label></label>').html('Código')
|
||||
@ -134,6 +143,7 @@
|
||||
const fields = [
|
||||
'name',
|
||||
'code',
|
||||
'identifier',
|
||||
'prefix',
|
||||
'suffix',
|
||||
'decimals',
|
||||
@ -149,7 +159,7 @@
|
||||
data = JSON.stringify(data)
|
||||
$.post(url, data, (response) => {
|
||||
if (response.created === true) {
|
||||
return reload()
|
||||
return this.load()
|
||||
}
|
||||
}, 'json')
|
||||
}
|
||||
@ -226,7 +236,7 @@
|
||||
data: data,
|
||||
success: (response) => {
|
||||
if (response.edited === true) {
|
||||
return reload()
|
||||
return this.load()
|
||||
}
|
||||
},
|
||||
dataType: 'json'
|
||||
@ -244,7 +254,7 @@
|
||||
success: (response) => {
|
||||
console.debug(response)
|
||||
if (response.deleted === true) {
|
||||
return reload()
|
||||
return this.load()
|
||||
}
|
||||
},
|
||||
dataType: 'json'
|
||||
|
@ -2,30 +2,131 @@
|
||||
|
||||
@section('page_content')
|
||||
<h3 class="ui header">Moneda - <span class="coin_name"></span></h3>
|
||||
<div class="ui list" id="coin_data">
|
||||
</div>
|
||||
<div class="ui list" id="coin_data"></div>
|
||||
<canvas id="coin_graph" width="400" height="200"></canvas>
|
||||
<table class="ui table" id="coin_values">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Fecha
|
||||
</th>
|
||||
<th>
|
||||
Valor
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
@endsection
|
||||
|
||||
@push('page_scripts')
|
||||
<script type="text/javascript">
|
||||
$(document).ready(() => {
|
||||
const id = '{{$coin_id}}'
|
||||
const api_url = '{{$urls->api}}'
|
||||
const url = api_url + '/coin/' + id
|
||||
$.getJSON(url, (data) => {
|
||||
$('.coin_name').html(data.coin.name)
|
||||
const coin = {
|
||||
id: {{$coin_id}},
|
||||
urls: {
|
||||
api: '{{$urls->api}}'
|
||||
},
|
||||
data: {},
|
||||
values: {},
|
||||
draw: function() {
|
||||
$('.coin_name').html(this.data.name)
|
||||
$('#coin_data').append(
|
||||
$('<div></div>').attr('class', 'item').html('Código: ' + data.coin.code)
|
||||
$('<div></div>').attr('class', 'item').html('Código: ' + this.data.code)
|
||||
).append(
|
||||
$('<div></div>').attr('class', 'item').html('Prefijo: ' + data.coin.prefix)
|
||||
$('<div></div>').attr('class', 'item').html('Prefijo: ' + this.data.prefix)
|
||||
).append(
|
||||
$('<div></div>').attr('class', 'item').html('Sufijo: ' + data.coin.suffix)
|
||||
$('<div></div>').attr('class', 'item').html('Sufijo: ' + this.data.suffix)
|
||||
).append(
|
||||
$('<div></div>').attr('class', 'item').html('Decimales: ' + data.coin.decimals)
|
||||
$('<div></div>').attr('class', 'item').html('Decimales: ' + this.data.decimals)
|
||||
).append(
|
||||
$('<div></div>').attr('class', 'item').html('Url: ' + data.coin.ref_url)
|
||||
$('<div></div>').attr('class', 'item').html('Url: ' + this.data.ref_url)
|
||||
)
|
||||
})
|
||||
},
|
||||
loading: function(elem) {
|
||||
elem.html('')
|
||||
elem.append(
|
||||
$('<div></div>').attr('class', 'ui active dimmer').append(
|
||||
$('<div></div>').attr('class', 'ui indeterminate elastic text loader').html('Cargando los datos.')
|
||||
)
|
||||
)
|
||||
},
|
||||
getData: function() {
|
||||
const elem_id = '#coin_data'
|
||||
this.loading($(elem_id))
|
||||
const url = this.urls.api + '/coin/' + this.id
|
||||
$.getJSON(url, (data) => {
|
||||
$(elem_id).html('')
|
||||
this.data = data.coin
|
||||
this.draw()
|
||||
this.getValues()
|
||||
})
|
||||
},
|
||||
getValues: function() {
|
||||
/*const elem_id = '#coin_values'
|
||||
$(elem_id).show()
|
||||
this.loading($(elem_id).find('tbody'))*/
|
||||
this.loading($('#coin_graph'))
|
||||
const url = this.urls.api + '/coin/' + this.id + '/values'
|
||||
$.getJSON(url, (data) => {
|
||||
//$(elem_id).find('tbody').html('')
|
||||
this.values = data.values
|
||||
this.graphValues()
|
||||
})
|
||||
},
|
||||
showValues: function() {
|
||||
const list_id = '#coin_values'
|
||||
list = $(list_id).find('tbody')
|
||||
const f = Intl.DateTimeFormat('es-CL', {dateStyle: 'medium', timeStyle: 'long'})
|
||||
$.each(this.values, (i, elem) => {
|
||||
const d = new Date(elem.date_time)
|
||||
list.append(
|
||||
$('<tr></tr>').append(
|
||||
$('<td></td>').html(f.format(d))
|
||||
).append(
|
||||
$('<td></td>').html(elem.formatted)
|
||||
)
|
||||
)
|
||||
})
|
||||
},
|
||||
graphValues: function() {
|
||||
const ctx = document.getElementById('coin_graph').getContext('2d')
|
||||
let labels = []
|
||||
let values = []
|
||||
const f = Intl.DateTimeFormat('es-CL', {dateStyle: 'medium'})
|
||||
$.each(this.values, (i, el) => {
|
||||
const d = new Date(el.date_time)
|
||||
labels.push(f.format(d))
|
||||
values.push(el.value)
|
||||
})
|
||||
const chart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
label: this.data.name,
|
||||
data: values,
|
||||
fill: false,
|
||||
borderWidth: 1,
|
||||
tension: 0.1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
setup: function() {
|
||||
this.getData()
|
||||
$('#coin_values').hide()
|
||||
}
|
||||
}
|
||||
$(document).ready(() => {
|
||||
coin.setup()
|
||||
})
|
||||
</script>
|
||||
@endpush
|
||||
|
@ -1,5 +1,220 @@
|
||||
@extends('layout.base')
|
||||
|
||||
@section('page_content')
|
||||
Home
|
||||
<h3>
|
||||
Home
|
||||
</h3>
|
||||
|
||||
<div class="ui top attached tabular menu" id="coins_menu">
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('page_scripts')
|
||||
<script type="text/javascript">
|
||||
const types = ['month', 'months', 'year']
|
||||
const coins_menu = $('#coins_menu')
|
||||
const f = Intl.DateTimeFormat()
|
||||
let active = false
|
||||
class Coin {
|
||||
constructor(data) {
|
||||
this.data = data
|
||||
this.values = {
|
||||
month: [],
|
||||
months: [],
|
||||
year: []
|
||||
}
|
||||
}
|
||||
getValues(api_url) {
|
||||
const base_url = api_url + '/coin/' + this.data.id + '/values/'
|
||||
let promises = []
|
||||
$.each(types, (i, t) => {
|
||||
let url = base_url + t
|
||||
promises.push($.getJSON(url, (data) => {
|
||||
this.values[t] = data.values
|
||||
}))
|
||||
})
|
||||
Promise.all(promises).then(() => {
|
||||
this.draw()
|
||||
})
|
||||
}
|
||||
size() {
|
||||
let sum = 0
|
||||
$.each(types, (i, t) => {
|
||||
sum += this.values[t].length
|
||||
})
|
||||
return sum
|
||||
}
|
||||
draw() {
|
||||
if (this.size() == 0) {
|
||||
return
|
||||
}
|
||||
const m = $('<div></div>').attr('class', 'item').attr('data-tab', this.data.code).html(this.data.name)
|
||||
coins_menu.append(m)
|
||||
const tabs = $('<div></div>').attr('class', 'ui top attached tabular menu')
|
||||
const column = $('<div></div>').attr('class', 'eight wide column').append(tabs)
|
||||
let active2 = false
|
||||
$.each(types, (i, t) => {
|
||||
if (this.values[t].length == 0) {
|
||||
return
|
||||
}
|
||||
const canvas = $('<canvas></canvas>').attr('id', 'canvas_' + t + '_' + this.data.code).attr('width', '200').attr('height', '200')
|
||||
const tab = $('<div></div>').attr('class', 'item').attr('data-tab', this.data.code + t).html(t.toLowerCase().split(' ').map(function(word) {
|
||||
return (word.charAt(0)).toUpperCase() + word.slice(1)
|
||||
}).join(' '))
|
||||
tabs.append(tab)
|
||||
const sg = $('<div></div>').attr('class', 'ui bottom attached tab segment').attr('data-tab', this.data.code + t).append(
|
||||
$('<div></div>').attr('class', 'content').append(canvas)
|
||||
)
|
||||
column.append(sg)
|
||||
if (!active2) {
|
||||
tab.addClass('active')
|
||||
sg.addClass('active')
|
||||
active2 = true
|
||||
}
|
||||
|
||||
let labels = []
|
||||
let values = []
|
||||
$.each(this.values[t], (k, val) => {
|
||||
const d = new Date(val.date_time)
|
||||
labels.push(f.format(d))
|
||||
values.push(val.value)
|
||||
})
|
||||
const min = Math.min(...values)
|
||||
const max = Math.max(...values)
|
||||
const range = max - min
|
||||
const chart = new Chart(canvas[0], {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
label: this.data.name,
|
||||
data: values,
|
||||
fill: false,
|
||||
borderWidth: 1,
|
||||
tension: 0.1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
min: min - range / 2,
|
||||
max: max + range / 2
|
||||
}/*,
|
||||
x: {
|
||||
display: false
|
||||
}*/
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
const seg = $('<div></div>').attr('class', 'ui bottom attached tab segment').attr('data-tab', this.data.code).append(
|
||||
$('<div></div>').attr('class', 'ui centered grid').append(column)
|
||||
)
|
||||
coins_menu.after(seg)
|
||||
if (!active) {
|
||||
m.addClass('active')
|
||||
seg.addClass('active')
|
||||
active = true
|
||||
}
|
||||
m.tab()
|
||||
tabs.find('.item').tab()
|
||||
}
|
||||
}
|
||||
const coins = {
|
||||
data: [],
|
||||
urls: {
|
||||
api: '{{$urls->api}}'
|
||||
},
|
||||
getCoins: function() {
|
||||
const url = this.urls.api + '/coins'
|
||||
$.getJSON(url, (data) => {
|
||||
$.each(data.coins, (i, el) => {
|
||||
const c = new Coin(el)
|
||||
c.getValues(this.urls.api)
|
||||
})
|
||||
})
|
||||
},
|
||||
getValues: function() {
|
||||
let p = []
|
||||
$.each(this.data, (i, el) => {
|
||||
const url = this.urls.api + '/coin/' + el.id + '/values/months'
|
||||
p.push($.getJSON(url, (data) => {
|
||||
this.data[i].values = data.values
|
||||
}))
|
||||
})
|
||||
Promise.all(p).then(() => {
|
||||
this.draw()
|
||||
})
|
||||
},
|
||||
draw: function() {
|
||||
const menu = $('#coin_menu')
|
||||
const f = Intl.DateTimeFormat()
|
||||
let active = false
|
||||
$.each(this.data, (i, el) => {
|
||||
if (el.values.length == 0) {
|
||||
return
|
||||
}
|
||||
const m = $('<div></div>').attr('class', 'item').attr('data-tab', el.code).html(el.name)
|
||||
menu.append(m)
|
||||
const canvas = $('<canvas></canvas>').attr('id', 'canvas' + el.code).attr('width', '200').attr('height', '200')
|
||||
const seg = $('<div></div>').attr('class', 'ui bottom attached tab segment').attr('data-tab', el.code).append(
|
||||
$('<div></div>').attr('class', 'ui centered grid').append(
|
||||
$('<div></div>').attr('class', 'eight wide column').append(canvas)
|
||||
)
|
||||
)
|
||||
menu.after(seg)
|
||||
if (!active) {
|
||||
m.addClass('active')
|
||||
seg.addClass('active')
|
||||
active = true
|
||||
}
|
||||
m.tab()
|
||||
let labels = []
|
||||
let values = []
|
||||
$.each(el.values, (k, val) => {
|
||||
const d = new Date(val.date_time)
|
||||
labels.push(f.format(d))
|
||||
values.push(val.value)
|
||||
})
|
||||
const min = Math.min(...values)
|
||||
const max = Math.max(...values)
|
||||
const range = max - min
|
||||
const chart = new Chart(canvas[0], {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
label: el.name,
|
||||
data: values,
|
||||
fill: false,
|
||||
borderWidth: 1,
|
||||
tension: 0.1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
min: min - range / 2,
|
||||
max: max + range / 2
|
||||
}/*,
|
||||
x: {
|
||||
display: false
|
||||
}*/
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
setup: function() {
|
||||
this.getCoins()
|
||||
}
|
||||
}
|
||||
$(document).ready(() => {
|
||||
coins.setup()
|
||||
})
|
||||
</script>
|
||||
@endpush
|
||||
|
@ -33,34 +33,33 @@ return [
|
||||
},
|
||||
'scripts' => function() {
|
||||
$arr = [
|
||||
[
|
||||
'full' => '<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>'
|
||||
],
|
||||
[
|
||||
'full' => '<script src="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/semantic.min.js" integrity="sha512-1Nyd5H4Aad+OyvVfUOkO/jWPCrEvYIsQENdnVXt1+Jjc4NoJw28nyRdrpOCyFH4uvR3JmH/5WmfX1MJk2ZlhgQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>'
|
||||
]
|
||||
'<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>',
|
||||
'<script src="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/semantic.min.js" integrity="sha512-1Nyd5H4Aad+OyvVfUOkO/jWPCrEvYIsQENdnVXt1+Jjc4NoJw28nyRdrpOCyFH4uvR3JmH/5WmfX1MJk2ZlhgQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>',
|
||||
'<script src="https://cdn.jsdelivr.net/npm/chart.js@3.4.1/dist/chart.min.js"></script>'
|
||||
];
|
||||
foreach ($arr as $i => $a) {
|
||||
if (strpos($a, '<script') !== false) {
|
||||
$a = ['full' => $a];
|
||||
} else {
|
||||
$a = ['url' => $a];
|
||||
}
|
||||
$arr[$i] = (object) $a;
|
||||
}
|
||||
return $arr;
|
||||
},
|
||||
'styles' => function() {
|
||||
$arr = [
|
||||
[
|
||||
'link' => '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/semantic.min.css" integrity="sha512-g/MzOGVPy3OQ4ej1U+qe4D/xhLwUn5l5xL0Fa7gdC258ZWVJQGwsbIR47SWMpRxSPjD0tfu/xkilTy+Lhrl3xg==" crossorigin="anonymous" referrerpolicy="no-referrer" />'
|
||||
],
|
||||
[
|
||||
'url' => 'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/brand-icons.woff2'
|
||||
],
|
||||
[
|
||||
'url' => 'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/icons.woff2'
|
||||
],
|
||||
[
|
||||
'url' => 'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/outline-icons.woff2'
|
||||
]
|
||||
'<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/semantic.min.css" integrity="sha512-g/MzOGVPy3OQ4ej1U+qe4D/xhLwUn5l5xL0Fa7gdC258ZWVJQGwsbIR47SWMpRxSPjD0tfu/xkilTy+Lhrl3xg==" crossorigin="anonymous" referrerpolicy="no-referrer" />',
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/brand-icons.woff2',
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/icons.woff2',
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/outline-icons.woff2'
|
||||
];
|
||||
foreach ($arr as $i => $a) {
|
||||
if (strpos($a, '<link') !== false) {
|
||||
$a = ['link' => $a];
|
||||
} else {
|
||||
$a = ['url' => $a];
|
||||
}
|
||||
$arr[$i] = (object) $a;
|
||||
}
|
||||
return $arr;
|
||||
|
5
run.python.bat
Normal file
5
run.python.bat
Normal file
@ -0,0 +1,5 @@
|
||||
@echo off
|
||||
|
||||
set pwd=%cd%
|
||||
|
||||
docker run -it -v "%pwd%/backend/python:/app/src" -v "%pwd%/backend/automation/bin:/app/bin" -v "%pwd%/build.sh:/app/build.sh" crypto_python
|
Reference in New Issue
Block a user