diff options
Diffstat (limited to 'btcdashboard')
| -rw-r--r-- | btcdashboard/App.js | 81 | ||||
| -rw-r--r-- | btcdashboard/DOCS/react1 | 21 | ||||
| -rw-r--r-- | btcdashboard/pulltoredis.py | 56 | ||||
| -rw-r--r-- | btcdashboard/server3.js | 43 |
4 files changed, 201 insertions, 0 deletions
diff --git a/btcdashboard/App.js b/btcdashboard/App.js new file mode 100644 index 0000000..838ab4f --- /dev/null +++ b/btcdashboard/App.js | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | import React, { useState, useEffect } from 'react'; | ||
| 2 | import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; | ||
| 3 | |||
| 4 | const App = () => { | ||
| 5 | const [prices, setPrices] = useState([]); | ||
| 6 | const [rawData, setRawData] = useState(''); | ||
| 7 | const [error, setError] = useState(null); | ||
| 8 | |||
| 9 | const testingString = "hiiii"; | ||
| 10 | |||
| 11 | useEffect(() => { | ||
| 12 | const fetchPrices = async () => { | ||
| 13 | try { | ||
| 14 | console.log('Fetching prices...'); | ||
| 15 | const response = await fetch('/api/prices', { | ||
| 16 | method: 'GET', | ||
| 17 | headers: { | ||
| 18 | 'Content-Type': 'application/json', | ||
| 19 | }, | ||
| 20 | }); | ||
| 21 | console.log('Response status:', response.status); | ||
| 22 | console.log('Response headers:', response.headers); | ||
| 23 | if (!response.ok) { | ||
| 24 | throw new Error(`HTTP error! status: ${response.status}`); | ||
| 25 | } | ||
| 26 | const data = await response.json(); | ||
| 27 | console.log('Fetched data:', data); | ||
| 28 | setPrices(data); | ||
| 29 | setRawData(JSON.stringify(data, null, 2)); | ||
| 30 | setError(null); | ||
| 31 | } catch (error) { | ||
| 32 | console.error('Failed to fetch prices:', error); | ||
| 33 | setError(error.toString()); | ||
| 34 | setRawData(''); | ||
| 35 | } | ||
| 36 | }; | ||
| 37 | fetchPrices(); | ||
| 38 | const interval = setInterval(fetchPrices, 5000); | ||
| 39 | return () => clearInterval(interval); | ||
| 40 | }, []); | ||
| 41 | |||
| 42 | // prep data | ||
| 43 | const chartData = prices.map(item => { | ||
| 44 | const parsedItem = JSON.parse(item); | ||
| 45 | return { | ||
| 46 | price: parsedItem.price, | ||
| 47 | time: new Date(parsedItem.timestamp * 1000).toLocaleTimeString() | ||
| 48 | }; | ||
| 49 | }).reverse(); // show oldest data first | ||
| 50 | |||
| 51 | return ( | ||
| 52 | <div> | ||
| 53 | <h1>price</h1> | ||
| 54 | <p>test stuff: {testingString}</p> | ||
| 55 | {error && <p>Error: {error}</p>} | ||
| 56 | <pre>{rawData}</pre> | ||
| 57 | |||
| 58 | <h2>Price Graph</h2> | ||
| 59 | <ResponsiveContainer width="100%" height={400}> | ||
| 60 | <LineChart | ||
| 61 | data={chartData} | ||
| 62 | margin={{ | ||
| 63 | top: 5, | ||
| 64 | right: 30, | ||
| 65 | left: 20, | ||
| 66 | bottom: 5, | ||
| 67 | }} | ||
| 68 | > | ||
| 69 | <CartesianGrid strokeDasharray="3 3" /> | ||
| 70 | <XAxis dataKey="time" /> | ||
| 71 | <YAxis domain={['auto', 'auto']} /> | ||
| 72 | <Tooltip /> | ||
| 73 | <Legend /> | ||
| 74 | <Line type="monotone" dataKey="price" stroke="#8884d8" activeDot={{ r: 8 }} /> | ||
| 75 | </LineChart> | ||
| 76 | </ResponsiveContainer> | ||
| 77 | </div> | ||
| 78 | ); | ||
| 79 | }; | ||
| 80 | |||
| 81 | export default App; | ||
diff --git a/btcdashboard/DOCS/react1 b/btcdashboard/DOCS/react1 new file mode 100644 index 0000000..9f8da04 --- /dev/null +++ b/btcdashboard/DOCS/react1 | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | |||
| 2 | sudo dnf update -y | ||
| 3 | sudo dnf install nodejs npm -y | ||
| 4 | mkdir prices && cd prices | ||
| 5 | |||
| 6 | npx create-react-app client | ||
| 7 | cd client | ||
| 8 | cd .. | ||
| 9 | npm install axios recharts | ||
| 10 | |||
| 11 | mkdir server | ||
| 12 | cd server | ||
| 13 | npm init -y | ||
| 14 | cd .. | ||
| 15 | npm install express redis cors | ||
| 16 | |||
| 17 | sudo dnf install -y redis | ||
| 18 | sudo systemctl start redis | ||
| 19 | sudo systemctl enable redis | ||
| 20 | sudo systemctl status redis | ||
| 21 | |||
diff --git a/btcdashboard/pulltoredis.py b/btcdashboard/pulltoredis.py new file mode 100644 index 0000000..0a82fa7 --- /dev/null +++ b/btcdashboard/pulltoredis.py | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | import asyncio | ||
| 2 | import aiohttp | ||
| 3 | import redis | ||
| 4 | import json | ||
| 5 | import logging | ||
| 6 | import time | ||
| 7 | |||
| 8 | logging.basicConfig(level=logging.INFO) | ||
| 9 | logger = logging.getLogger(__name__) | ||
| 10 | |||
| 11 | # redis default port | ||
| 12 | redis_client = redis.Redis(host='localhost', port=6379, db=0) | ||
| 13 | |||
| 14 | URL = 'https://api.kraken.com/0/public/Ticker?pair=BTCUSD' | ||
| 15 | HEADERS = {'Accept': 'application/json'} | ||
| 16 | |||
| 17 | MAX_PRICES = 10 | ||
| 18 | UPDATE_INTERVAL = 3 # seconds | ||
| 19 | |||
| 20 | async def fetch_btc_price(session): | ||
| 21 | try: | ||
| 22 | async with session.get(URL, headers=HEADERS, ssl=False) as response: | ||
| 23 | data = await response.json() | ||
| 24 | return float(data['result']['XXBTZUSD']['c'][0]) | ||
| 25 | except Exception as e: | ||
| 26 | logger.error(f"error getting price: {e}") | ||
| 27 | return None | ||
| 28 | |||
| 29 | async def update_price(): | ||
| 30 | async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=False)) as session: | ||
| 31 | while True: | ||
| 32 | price = await fetch_btc_price(session) | ||
| 33 | if price: | ||
| 34 | timestamp = int(time.time()) # this is unix | ||
| 35 | price_data = json.dumps({"price": price, "timestamp": timestamp}) | ||
| 36 | |||
| 37 | redis_client.lpush('btc_prices', price_data) | ||
| 38 | |||
| 39 | redis_client.ltrim('btc_prices', 0, MAX_PRICES - 1) | ||
| 40 | |||
| 41 | logger.info(f"updated btc price: ${price}") | ||
| 42 | |||
| 43 | all_prices = redis_client.lrange('btc_prices', 0, -1) | ||
| 44 | logger.info("prices:") | ||
| 45 | for i, p in enumerate(all_prices, 1): | ||
| 46 | p_data = json.loads(p) | ||
| 47 | logger.info(f"{i}. ${p_data['price']} at {p_data['timestamp']}") | ||
| 48 | |||
| 49 | await asyncio.sleep(UPDATE_INTERVAL) | ||
| 50 | |||
| 51 | async def main(): | ||
| 52 | logger.info("btc price update") | ||
| 53 | await update_price() | ||
| 54 | |||
| 55 | if __name__ == "__main__": | ||
| 56 | asyncio.run(main()) | ||
diff --git a/btcdashboard/server3.js b/btcdashboard/server3.js new file mode 100644 index 0000000..72bfb8d --- /dev/null +++ b/btcdashboard/server3.js | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | const express = require('express'); | ||
| 2 | const cors = require('cors'); | ||
| 3 | const redis = require('redis'); | ||
| 4 | |||
| 5 | const app = express(); | ||
| 6 | const PORT = 3001; | ||
| 7 | |||
| 8 | const redisClient = redis.createClient({ | ||
| 9 | url: 'redis://localhost:6379' // this is default port | ||
| 10 | }); | ||
| 11 | |||
| 12 | // CORS | ||
| 13 | app.use((req, res, next) => { | ||
| 14 | res.header('Access-Control-Allow-Origin', '*'); | ||
| 15 | res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); | ||
| 16 | res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); | ||
| 17 | next(); | ||
| 18 | }); | ||
| 19 | |||
| 20 | app.get('/api/prices', async (req, res) => { | ||
| 21 | try { | ||
| 22 | const prices = await redisClient.lRange('btc_prices', 0, -1); | ||
| 23 | |||
| 24 | res.header('Access-Control-Allow-Origin', '*'); | ||
| 25 | res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); | ||
| 26 | res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); | ||
| 27 | |||
| 28 | res.json(prices); | ||
| 29 | } catch (error) { | ||
| 30 | console.error('Error fetching prices:', error); | ||
| 31 | res.status(500).json({ error: 'Internal Server Error' }); | ||
| 32 | } | ||
| 33 | }); | ||
| 34 | |||
| 35 | async function startServer() { | ||
| 36 | await redisClient.connect(); | ||
| 37 | |||
| 38 | app.listen(PORT, () => { | ||
| 39 | console.log(`Server running on http://localhost:${PORT}`); | ||
| 40 | }); | ||
| 41 | } | ||
| 42 | |||
| 43 | startServer().catch(console.error); | ||
