I have an API made on Flask that has several endpoints. I am trying to use data from these end points to display a chart on my front end that shows the data to the user.
The following are my exact requirements:
Implement one or more types of charts that can be used to effectively visualize data supplied from the API endpoints. Users should be able to pick different metrics to visualize and compare with others.
My Flask API:
import os
from flask import Flask, jsonify, session, request
import sqlalchemy
import time
from functools import wraps
# web app
app = Flask(__name__)
app.secret_key = 'super_secure_key_that_should_be_in_.env'
# database engine
engine = sqlalchemy.create_engine(os.getenv('SQL_URI'))
def rate_limit(**limit_kwargs):
def decorator(function):
#wraps(function)
def wrapper(*args, **kwargs):
limit = limit_kwargs['limit'] if 'limit' in limit_kwargs else 5
seconds = limit_kwargs['window'] if 'window' in limit_kwargs else 60
session_key = 'rate_limit_' + str(request.url_rule)
if session_key not in session:
session[session_key] = []
window: list = session[session_key]
if len(window) < limit:
window.append(int(time.time()))
session[session_key] = window
return function(*args, **kwargs)
if time.time() - window[0] < seconds:
return jsonify(error='Rate limit exceeded'), 429
window.pop(0)
window.append(int(time.time()))
session[session_key] = window
return function(*args, **kwargs)
return wrapper
return decorator
#app.route('/')
#rate_limit()
def index():
return 'Welcome 😎'
#app.route('/events/hourly')
#rate_limit(limit=3, window=20)
def events_hourly():
return queryHelper('''
SELECT date, hour, events
FROM public.hourly_events
ORDER BY date, hour
LIMIT 168;
''')
#app.route('/events/daily')
#rate_limit(limit=2)
def events_daily():
return queryHelper('''
SELECT date, SUM(events) AS events
FROM public.hourly_events
GROUP BY date
ORDER BY date
LIMIT 7;
''')
#app.route('/stats/hourly')
#rate_limit(limit=3, window=20)
def stats_hourly():
return queryHelper('''
SELECT date, hour, impressions, clicks, revenue
FROM public.hourly_stats
ORDER BY date, hour
LIMIT 168;
''')
#app.route('/stats/daily')
#rate_limit(limit=2)
def stats_daily():
return queryHelper('''
SELECT date,
SUM(impressions) AS impressions,
SUM(clicks) AS clicks,
SUM(revenue) AS revenue
FROM public.hourly_stats
GROUP BY date
ORDER BY date
LIMIT 7;
''')
#app.route('/poi')
#rate_limit(limit=3)
def poi():
return queryHelper('''
SELECT *
FROM public.poi;
''')
def queryHelper(query):
with engine.connect() as conn:
result = conn.execute(query).fetchall()
return jsonify([dict(row.items()) for row in result])
My React App.js:
import React, {Component} from 'react';
import logo from './logo.svg';
import './App.css';
import DailyEvents from "./Components/DailyEvents";
class App extends Component {
render() {
return (
<div className="App">
<header className= "App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title"> Welcome to React</h1>
</header>
<DailyEvents />
</div>
);
}
}
export default App;
My Components/DailyEvents.js:
import React, { Component } from 'react';
import axios from 'axios';
class App extends Component {
state = {
dates: []
}
componentDidMount() {
axios.get('api_url_or_localhost/events/daily')
.then(res => res.json())
.then((data) => {
this.setState({ dates: data })
})
.catch(console.log)
}
}
export default App;
I keep getting a TypeError that instance.render is not a function.
What I am trying to do is use my Flask API endpoints to visualize the data on the front-end.
try using axios :
https://alligator.io/react/axios-react/
it should look like this :
axios.get('put the query address')
.then(response => {
process your response
})
Can / Should you make changes to the Flask API, or is that code part of the requirements? Because I'm pretty sure you won't be able to return those Date objects straight from the DB without serializing them first.
I suggest you use Postman (or just a browser, for get requests) to test the routes locally and basically have a look at the data first.
React can use Axios or fetch to get this data and visualize it.
import React, { Component } from 'react'
class App extends Component {
state = {
dates: []
}
componentDidMount() {
fetch('api_url_or_localhost/events/daily'))
.then(res => res.json())
.then((data) => {
this.setState({ dates: data })
})
.catch(console.log)
}
...
}
see this https://pusher.com/tutorials/consume-restful-api-react for more details
Related
I have a Flask app with ORM SQLAlchemy, configured like this
def create_app():
app = Flask(__name__, static_folder='../client',
static_url_path='', template_folder='../client')
app.secret_key = secret_keys[2]
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:***#localhost:3307/project***'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config.update({
'SQLALCHEMY_POOL_SIZE': 100,
'SQLALCHEMY_POOL_TIMEOUT': 0
})
db.init_app(app)
Please note that the pool_size and timeout is set like that, so I can work, because the app will routinely create new and new connections and trigger the timeout (15+10). I'm not sure why this is happening, here are a few bits of code I am using:
#api.route('/api/get-variations', methods=['POST'])
def get_variations():
#
# Returns the appropriate variations for that product ID
#
product_id = request.json
data = {'data': [variation.to_dict() for variation in productvar.query.filter_by(ProductID=product_id).all()]}
return data
The model
class productvar(db.Model):
__tablename__ = 'product_variations'
id = db.Column('VariationID', db.Integer, primary_key = True)
ProductID = db.Column(db.Integer)
Variation = db.Column(db.String(50))
def to_dict(self):
return {
'Value': self.id,
'Variation': self.Variation
}
JS
function change_dropdown(htmlVariation, product) {
var productID = product
fetch('/api/get-variations', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(productID),
})
// get the variations for the product in json
.then((result) => { return result.json(); })
.then((data) => {
// replace the dropdown with the appropriate variations
var newVariations = data;
for (var i = 0; i < newVariations?.data.length; i++) {
var new_option = document.createElement("option");
new_option.text = newVariations?.data[i].Variation
new_option.value = newVariations?.data[i].Value
htmlVariation[0].appendChild(new_option);
}
})
}
The flow usually goes like this > you select an option from the website, it then goes via JS to the server and amends the dropdown (hence change_variation).
What am I doing wrong here? Shouldn't SQLAlchemy reuse the connections? Why is it constantly creating new connections? I'm imaginging that if there are 20 or more users on my website, going through various combinations, they will effectively timeout the server.
Should I go via normal SQLAlchemy and create and dispose of the engine?
Please mind that I already tried this but I can't seem to get it to actually kill the connections
db.session.close()
engine = db.get_engine(app=create_app())
engine.dispose()
All help wildly appreciated!
I'm using Django and React for a project that consumes Trello's API. I created functions in django views.py that will get user's boards, lists and cards. The problem is, to get a list, I need that the user select a board, because the list's endpoint requires a board id (and so on). I can show the user's boards on my react page and storage the board's id on a variable in Index.js (on pages folder), but I have no ideia how I can send the selected board id to views.py to consume Trello's api according to the user's selected board. Here's my code.
Django views.py:
from rest_framework.decorators import api_view
from rest_framework.response import Response
from dotenv import load_dotenv
from treegia.settings import TRELLO_URL
import os
import requests
load_dotenv()
TRELLO_KEY = os.getenv('TRELLO_KEY')
TRELLO_TOKEN = os.getenv('TRELLO_TOKEN')
#api_view(['GET'])
def get_boards(request):
if request.method == 'GET':
board_endpoint = TRELLO_URL+'members/me/boards'
jsonObj = {'fields':'name,id', 'key':TRELLO_KEY, 'token':TRELLO_TOKEN}
boards = requests.get(board_endpoint, json=jsonObj).json()
return Response(boards)
#api_view(['GET'])
def get_lists(request):
if request.method == 'GET':
list_endpoint = TRELLO_URL+ 'boards/' + id_board + '/lists'
jsonObj = {'fields':'name,id', 'id':id_board, 'key':TRELLO_KEY, 'token':TRELLO_TOKEN}
lists = requests.get(list_endpoint, json=jsonObj).json()
return Response(lists)
#api_view(['GET'])
def get_cards(request):
if request.method == 'GET':
card_endpoint = TRELLO_URL+ 'lists/' + id_list + '/cards'
jsonObj = {'fields':'name,id', 'id':id_list, 'key':TRELLO_KEY, 'token':TRELLO_TOKEN}
cards = requests.get(card_endpoint, json=jsonObj).json()
return Response(cards)
React Index.js:
import React from 'react';
import Navbar from '../components/Navbar';
import '../styles/index.css'
const Index = () => {
const [boards, setBoards] = React.useState([]);
const [boardId, setBoardId] = React.useState([]);
const [lists, setLists] = React.useState([]);
const [listId, setListId] = React.useState([]);
React.useEffect(() => {
getBoards();
}, []);
React.useEffect(() => {
getLists();
}, []);
async function getBoards() {
await fetch('http://localhost:8000/boards/')
.then(resp => resp.json())
.then(data => {
console.log(data);
if(data) setBoards(data);
})
}
async function getLists() {
await fetch('http://127.0.0.1:8000/lists/')
.then(resp => resp.json())
.then(data => {
console.log(data);
if(data) setLists(data);
})
}
return (
<div id='index-page'>
<Navbar />
<div className='boards'>
<h1>Your boards</h1>
<div className='boards-container'>
{boards.map(board => (
<button key={board.id} onClick={() => {
setBoardId(board.id)
}}>{board.name}</button>
))}
</div>
</div>
<div className='lists'>
<h1>Your lists</h1>
<div className='lists-container'>
{lists.map(list => (
<button key={list.id} onClick={() => {
setListId(list.id)
}}>{list.name}</button>
))}
</div>
</div>
{boardId ? <p>{boardId}</p> : ''}
{listId ? <p>{listId}</p> : ''}
</div>
);
}
export default Index;
Basically, when the user selects a board, I what to send the id to get_lists function. Is this possible to do?
In the view
def get_lists(request):
print(request.query_params['id_list']) # 'id_list being the variable you want to pass from the front end
this sets that we are expecting a parameter from the front end get request.
Remember to add a try catch arround it..cos if that query_param doesnt exist it will throw and erroe.
Non in the FE.. while making a request, set request parameter 'id_list = '
var url = new URL('http://127.0.0.1:8000/lists')
var params = {id_list:'3'} // or whatever the params
url.search = new URLSearchParams(params).toString();
fetch(url);
Essentially you add the query params to the request url like
http://127.0.0.1:8000/lists/?id_list=3&another_param=2..
test the backend using postman or smthn before integration..to make sure the correct url and stuff.
Status Quo:
Whenever a user visits my web application, Axios makes a request to a third party API to fetch data and populate the frontend with that data using v-for.
Conclusion: I have one API call per website visitor.
Desired status:
Whenever a user visits my web application, Axios shall fetch the data from the SQLite database which itself is populated every XX seconds by a python request to reduce API calls.
Questions:
Now I implemented a SQLite database using Django models and views. So far so good, the API gets fetched regularly and updates the database table properly.
1.) How can I now call the data in the database using Axios? As by my research Axios somehow needs to call a view and the view will call the data from the database, is this correct?
2.) If Axios needs to call a view, do I need another view.py file that calls the database? If I would insert the needed view function into the existing view.py file it would initiate another API call, wouldn't it?
3.) And how can I implement the link to the view function to Axios? Instead of a third party API url would I just use the path to the view file?
Quotes_app/Views.py:
from django.shortcuts import render
from Quotes_app.models import ratesEUR
import json
import requests
response = requests.get("http://data.fixer.io/api/latest?access_key=XXXX&base=EUR")
rates_EUR = json.loads(response.content.decode('utf-8'))
timestamp = rates_EUR['timestamp']
base = rates_EUR['base']
date = rates_EUR['date']
rates = rates_EUR['rates']
id = 1
rates_new = ratesEUR(id=id, timestamp=timestamp, base=base, date=date, rates=rates)
rates_new.save()
def render_Quotes_app(request, template="Quotes_app/templates/Quotes_app/Quotes_app.html"):
return render(request, template)
Quotes_app/models.py:
from django.db import models
class ratesEUR(models.Model):
timestamp = models.CharField(max_length=10)
base = models.CharField(max_length=3)
date = models.DateField(auto_now=False, auto_now_add=False)
rates = models.CharField(max_length=8)
def __str__(self):
return self.base
Vue.js/axios: (as of now directly fetching the API)
Vue.config.devtools = true;
var app = new Vue({
delimiters: ['[[', ']]'],
el: '.eurQuotesWrapper',
data() {
return {
rates: [],
};
},
computed: {
rates1() {
const ratesArr1 = Object.entries(this.rates);
const ret = ratesArr1.reduce((a, c, i, d) => {
if (i < d.length / 2) a[c[0]] = c[1];
return a;
}, {});
console.log('rates1', ret);
return ret;
},
rates2() {
const ratesArr2 = Object.entries(this.rates);
const ret = ratesArr2.reduce((a, c, i, d) => {
if (i >= d.length / 2) a[c[0]] = c[1];
return a;
}, {});
console.log('rates2', ret);
return ret;
}
},
created() {
axios
.get("http://data.fixer.io/api/latest?access_key=XXXX&base=EUR")
.then(response => {
this.rates = response.data.rates;
console.log(this.rates);
for(key in this.rates) {
this.rates[key] = new Intl.NumberFormat('de-DE', {
minimumFractionDigits: 5,
maximumFractionDigits: 5
}).format(this.rates[key]);
}
console.log(this.rates);
});
}
});
In advance thank you very much for your help!
You can use DRF(Django Rest Framework) to make REST API's or You can use JsonResponse
to send JSON objects as response.
from django.http import JsonResponse
data = # Your data
JsonResponse(data, encoder=MyJSONEncoder)
For more details visit django's documentation
Better would be if you use DRF for making rest apis. It's much easier.
I'm using React, Apollo and GraphQL at the frontend and Django, Python and Graphene at the backend.
I want to implement search_bar and when user click on search button I want to execute GraphQL query with the user input.
At the backend I want to grap that string and I want to pass it to an function and then returned result from the function I want to pass back to the frontend.
I have code as follows:
FRONTEND
export const GET_RESULTS = gql`
query SearchVinResults($vin: String!){
searchVinResults(vin: $vin) {
id
label
url
}
}`;
class VinSearch extends Component {
onVinSearch(e) {
e.preventDefault();
const {value, client: {query}} = this.props;
query({query: GET_RESULTS, variables: { vin: value }});
}
render() {
const {value, clearSearch, onChange} = this.props;
return (
<SearchField
onChange={e => onChange(e)}
clearSearch={() => clearSearch()}
value={value}
clearIcon="fal fa-times"
placeholder="Search VIN"
withButton={true}
buttonStyles={{borderLeftWidth: 0}}
onClick={e => this.onVinSearch(e)}
/>
);
}
}
export default withApollo(VinSearch);
Backend
class Query(object):
search_vin_results = ???? # What do I need to do here
def resolve_search_vin_results(self, info, **kwargs):
# Here I want to do something like
vin = info['vin']
results = get_vins(vin)
return results
Any idea?
I created an API that can get data from MySQL. It working if the stockList is defined. However, in actual world, I need to get it from the Ionic app, and the stockList is defined by individual user.
Simply put stockList =[] does not work.
Currently the flask_app.py is as below:
from flask import Flask,jsonify,abort,make_response,request
import MySQLdb
import MySQLdb.cursors
app = Flask(__name__)
#app.route('/KLSEwatch', methods=['GET'])
def KLSEwatch():
db = MySQLdb.connect(host='vinvin.mysql.pythonanywhere-services.com',user='vinvin',passwd='xxx',db='vinukdb$default',cursorclass=MySQLdb.cursors.DictCursor)
curs = db.cursor()
stockList = ['SHELL','GENM']
placeholders = ','.join(['%s'] * len(stockList))
query = 'SELECT * FROM KLSE WHERE Stock IN ({})'.format(placeholders)
curs.execute(query,tuple(stockList))
f = curs.fetchall()
return jsonify({'Stock': f})
what I shall replace stockList as it shall get the data from user, which is from an Ionic app. The data is can be string or a 4 digits numbers
Below is the code in watchlistCtrl.js in Ionic app
//setting get counter-number of get requests-
var getCounter = 0;
for (var market in watchListQuery) {
if(watchListQuery[market].length>0){
getCounter += 1;
}
}
if(getCounter == 0)
$ionicLoading.hide();
$scope.watchedStocks = [];
for (var market in watchListQuery) {
if(watchListQuery[market].length>0){
var queryString = watchListQuery[market].toString().replace(/,/g, "','");
$webServicesFactory.get($marketProvider[market].queryURL+"/watchlist_query", {AnonymousToken: $marketProvider[market].token}, {parameters:{stockList:queryString}}).then(
function success(data) {
getCounter -=1 ;
$scope.watchedStocks = $scope.watchedStocks.concat(data);
if(getCounter <= 0)
$ionicLoading.hide();
},
function error() {
$ionicLoading.hide();
}
);
}
}//end of for each loop
You didn't show us any of your Ionic code, but here's a simple example of taking input from your Ionic app and submitting it to Flask. First, some HTML for the frontend (I'm only using Angular, since that is the common theme here - the rest of Ionic isn't relevant to this problem):
<!-- templates/home.html -->
<!doctype html>
<html lang="en">
<head>
<title>Ionic / Flask</title>
</head>
<body>
<div ng-app="app">
<div ng-controller="MyCtrl">
<p>Enter a comma-separated string value, like "BAC,XYZ"</p>
<input type="text" ng-model="stockList">
<button ng-click="submit()">Submit</button>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.14/angular.js"></script>
<script>
angular.module('app', [])
.controller('MyCtrl', function ($http, $log, $scope) {
$scope.stockList = '';
$scope.submit = function () {
$http.get('/KLSEwatch', {params: {stockList: $scope.stockList}}).then(function (result) {
$log.log('This is the query to execute: ',result.data)
})
};
})
</script>
</body>
</html>
And then here's a modified version of your Flask app, to demonstrate that this will generate the correct query:
# app.py
from flask import Flask,jsonify,abort,make_response,request, render_template
import MySQLdb
import MySQLdb.cursors
app = Flask(__name__)
app.config['DEBUG'] = True
#app.route('/')
def home():
return render_template('home.html')
#app.route('/KLSEwatch', methods=['GET'])
def KLSEwatch():
stockList = request.args['stockList'].split(',')
placeholders = ','.join(['%s'] * len(stockList))
query = 'SELECT * FROM KLSE WHERE Stock IN ({})'.format(placeholders)
print('This is the query: %s' % (query % tuple(stockList)))
return query % tuple(stockList)
app.run()
All you need to do is run the app, enter a string value into the input field & submit it, and then check the results in your browser console log, or in the output for the Flask app.