flask server, gunicorn, fetch data in JS
This commit is contained in:
parent
2ed92e5412
commit
b5ad33f138
33
app.py
Normal file
33
app.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
from flask import Flask, request, jsonify
|
||||||
|
import requests, re, json, os, yaml
|
||||||
|
from db import *
|
||||||
|
from aliexpress import *
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
def get_conf():
|
||||||
|
'''return settings in settings.yaml file'''
|
||||||
|
with open(os.path.dirname(os.path.realpath(__file__))+"/settings.yaml", 'r') as conf_file:
|
||||||
|
settings = yaml.safe_load(conf_file)
|
||||||
|
return settings
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def hello_world():
|
||||||
|
return 'Hello, World!'
|
||||||
|
|
||||||
|
@app.route('/datahistory',methods = ['POST', 'GET'])
|
||||||
|
def data_history_request():
|
||||||
|
if request.method == 'GET':
|
||||||
|
print("fetching data history")
|
||||||
|
settings = get_conf()
|
||||||
|
return jsonify(get_history(settings["db"]))
|
||||||
|
|
||||||
|
@app.route('/dataitem',methods = ['POST', 'GET'])
|
||||||
|
def data_item_request():
|
||||||
|
if request.method == 'GET':
|
||||||
|
print("fetching data item")
|
||||||
|
settings = get_conf()
|
||||||
|
return jsonify(get_item(settings["db"]))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(debug = True)
|
65
db.py
65
db.py
@ -18,8 +18,8 @@ def add_item(db_settings, itemid, skuid, choice, attributes, image):
|
|||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
INSERT INTO item (itemid, skuid, choice, attributes, image)
|
INSERT INTO item (uuid, itemid, skuid, choice, attributes, image)
|
||||||
VALUES (%s, %s, %s, %s, %s)
|
VALUES (nextval('uuid_sequence'), %s, %s, %s, %s, %s)
|
||||||
""", (itemid, skuid, choice, attributes, image))
|
""", (itemid, skuid, choice, attributes, image))
|
||||||
connection.commit()
|
connection.commit()
|
||||||
connection.close()
|
connection.close()
|
||||||
@ -29,7 +29,7 @@ def add_history_entry(db_settings, itemid, skuid, choice, attributes, image, pri
|
|||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
SELECT *
|
SELECT uuid
|
||||||
FROM item
|
FROM item
|
||||||
WHERE itemid = %s
|
WHERE itemid = %s
|
||||||
AND skuid = %s
|
AND skuid = %s
|
||||||
@ -37,23 +37,54 @@ def add_history_entry(db_settings, itemid, skuid, choice, attributes, image, pri
|
|||||||
|
|
||||||
if cursor.rowcount == 0:
|
if cursor.rowcount == 0:
|
||||||
add_item(db_settings, itemid, skuid, choice, attributes, image)
|
add_item(db_settings, itemid, skuid, choice, attributes, image)
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT uuid
|
||||||
|
FROM item
|
||||||
|
WHERE itemid = %s
|
||||||
|
AND skuid = %s
|
||||||
|
""", (itemid, skuid))
|
||||||
|
|
||||||
|
uuid = cursor.fetchall()[0]
|
||||||
|
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
INSERT INTO history (itemid, skuid, price, currency, quantity, discount_percentage, h_timestamp)
|
INSERT INTO history (uuid, price, currency, quantity, discount_percentage, h_timestamp)
|
||||||
VALUES (%s, %s, %s, %s, %s, %s, (SELECT LOCALTIMESTAMP))
|
VALUES (%s, %s, %s, %s, %s, (SELECT LOCALTIMESTAMP))
|
||||||
""", (itemid, skuid, price, currency, quantity, discount_percentage))
|
""", (uuid, price, currency, quantity, discount_percentage))
|
||||||
connection.commit()
|
connection.commit()
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
||||||
|
def get_history(db_settings):
|
||||||
|
connection = connect_db(db_settings)
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT uuid, quantity, discount_percentage, price, currency, h_timestamp
|
||||||
|
FROM history
|
||||||
|
""")
|
||||||
|
results = cursor.fetchall()
|
||||||
|
cursor.close()
|
||||||
|
connection.close()
|
||||||
|
return results
|
||||||
|
|
||||||
|
def get_item(db_settings):
|
||||||
|
connection = connect_db(db_settings)
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT uuid, itemid, skuid, choice, attributes, image
|
||||||
|
FROM item
|
||||||
|
""")
|
||||||
|
results = cursor.fetchall()
|
||||||
|
cursor.close()
|
||||||
|
connection.close()
|
||||||
|
return results
|
||||||
|
|
||||||
def export_csv(db_settings):
|
def export_csv(db_settings):
|
||||||
connection = connect_db(db_settings)
|
connection = connect_db(db_settings)
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
SELECT i.itemid, i.skuid, i.choice, i.attributes, i.image, h.quantity, h.discount_percentage, h.price, h.currency, h.h_timestamp
|
SELECT i.uuid, i.itemid, i.skuid, i.choice, i.attributes, i.image, h.quantity, h.discount_percentage, h.price, h.currency, h.h_timestamp
|
||||||
FROM item i, history h
|
FROM item i, history h
|
||||||
WHERE i.itemid = h.itemid and i.skuid = h.skuid
|
WHERE i.uuid = h.uuid
|
||||||
""")
|
""")
|
||||||
results = cursor.fetchall()
|
results = cursor.fetchall()
|
||||||
with open(os.path.dirname(os.path.realpath(__file__))+"/output.csv", 'w') as csv_file:
|
with open(os.path.dirname(os.path.realpath(__file__))+"/output.csv", 'w') as csv_file:
|
||||||
@ -78,28 +109,36 @@ def initialize(db_settings):
|
|||||||
DROP TABLE IF EXISTS item
|
DROP TABLE IF EXISTS item
|
||||||
""")
|
""")
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
|
DROP SEQUENCE IF EXISTS uuid_sequence
|
||||||
|
""")
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE SEQUENCE uuid_sequence
|
||||||
|
INCREMENT BY 1
|
||||||
|
START WITH 1
|
||||||
|
""")
|
||||||
|
cursor.execute("""
|
||||||
CREATE TABLE item
|
CREATE TABLE item
|
||||||
(
|
(
|
||||||
|
uuid int,
|
||||||
itemid bigint,
|
itemid bigint,
|
||||||
skuid bigint,
|
skuid bigint,
|
||||||
choice boolean,
|
choice boolean,
|
||||||
attributes text[],
|
attributes text[],
|
||||||
image text,
|
image text,
|
||||||
primary key (itemid,skuid)
|
primary key (uuid)
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
CREATE TABLE history
|
CREATE TABLE history
|
||||||
(
|
(
|
||||||
itemid bigint,
|
uuid int,
|
||||||
skuid bigint,
|
|
||||||
quantity integer,
|
quantity integer,
|
||||||
discount_percentage numeric(2),
|
discount_percentage numeric(2),
|
||||||
price money,
|
price money,
|
||||||
currency varchar(4),
|
currency varchar(4),
|
||||||
h_timestamp timestamp,
|
h_timestamp timestamp,
|
||||||
foreign key (itemid,skuid) references item(itemid,skuid),
|
foreign key (uuid) references item(uuid),
|
||||||
primary key (itemid,skuid,h_timestamp)
|
primary key (uuid, h_timestamp)
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
17
gunicorn_config.py
Normal file
17
gunicorn_config.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
workers = int(os.environ.get('GUNICORN_PROCESSES', '2'))
|
||||||
|
|
||||||
|
threads = int(os.environ.get('GUNICORN_THREADS', '4'))
|
||||||
|
|
||||||
|
# timeout = int(os.environ.get('GUNICORN_TIMEOUT', '120'))
|
||||||
|
|
||||||
|
bind = os.environ.get('GUNICORN_BIND', '0.0.0.0:8080')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
forwarded_allow_ips = '*'
|
||||||
|
|
||||||
|
secure_scheme_headers = { 'X-Forwarded-Proto': 'https' }
|
76
web/app.js
76
web/app.js
@ -16,24 +16,69 @@ const width = window.innerWidth - margin.right - margin.left -10;
|
|||||||
// (resizes every elements)
|
// (resizes every elements)
|
||||||
window.onresize = function(){ location.reload(); }
|
window.onresize = function(){ location.reload(); }
|
||||||
|
|
||||||
d3.csv('http://127.0.0.1/output.csv', d => {
|
async function fetch_history() {
|
||||||
return {
|
try {
|
||||||
date: new Date(d.h_timestamp.replace(' ', 'T')),
|
const response = await fetch('http://127.0.0.1:8080/datahistory');
|
||||||
value: parseFloat(d.price.replace('$', '')),
|
const rawData = await response.json();
|
||||||
skuid: d.skuid,
|
// SELECT uuid, quantity, discount_percentage, price, currency, h_timestamp
|
||||||
itemid: d.itemid,
|
let historyData = rawData.map(d => ({
|
||||||
image: d.image,
|
uuid: d[0],
|
||||||
currency: d.currency
|
value: parseFloat(d[3].replace('$', '')),
|
||||||
|
currency: d[4],
|
||||||
|
date: new Date(d[5].replace(' ', 'T')),
|
||||||
|
}));
|
||||||
|
return historyData;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching data history: ', error);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}).then(function(data) {
|
}
|
||||||
|
|
||||||
|
async function fetch_item() {
|
||||||
|
try {
|
||||||
|
// SELECT uuid, itemid, skuid, choice, attributes, image
|
||||||
|
const response = await fetch('http://127.0.0.1:8080/dataitem');
|
||||||
|
const rawData = await response.json();
|
||||||
|
const items = rawData.reduce((item, row) => {
|
||||||
|
const uuid = row[0];
|
||||||
|
|
||||||
|
const itemid = row[1];
|
||||||
|
const skuid = row[2];
|
||||||
|
const choice = row[3];
|
||||||
|
const attributes = row[4];
|
||||||
|
const image = row[5];
|
||||||
|
|
||||||
|
const values = {
|
||||||
|
itemid: itemid,
|
||||||
|
skuid: skuid,
|
||||||
|
choice: choice,
|
||||||
|
attributes: attributes,
|
||||||
|
image: image
|
||||||
|
};
|
||||||
|
|
||||||
|
item[uuid] = values;
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching data item: ', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fetch_history().then(async function(data) {
|
||||||
|
items = await fetch_item();
|
||||||
|
|
||||||
// Date domain (width)
|
// Date domain (width)
|
||||||
const x = d3.scaleTime()
|
const x = d3.scaleTime()
|
||||||
.domain(d3.extent(data, d => d.date))
|
.domain(d3.extent(data, d => d.date))
|
||||||
.range([3, width-3]);
|
.range([3, width-3]);
|
||||||
|
|
||||||
// Group the data by (itemid, skuid)
|
// Group the data by (uuid)
|
||||||
const nestedData = d3.group(data, d => d.skuid);
|
const nestedData = d3.group(data, d => d.uuid);
|
||||||
// Create a div for each graph
|
// Create a div for each graph
|
||||||
const graphDivs = d3.select("#graphs")
|
const graphDivs = d3.select("#graphs")
|
||||||
.selectAll(".graph-container")
|
.selectAll(".graph-container")
|
||||||
@ -58,16 +103,17 @@ d3.csv('http://127.0.0.1/output.csv', d => {
|
|||||||
const svg = d3.select(this);
|
const svg = d3.select(this);
|
||||||
|
|
||||||
// context on right side
|
// context on right side
|
||||||
// text
|
const link = `https://fr.aliexpress.com/item/${items[key].itemid}.html`;
|
||||||
const link = `https://fr.aliexpress.com/item/${dataSubset[0].itemid}.html`;
|
|
||||||
// image
|
// image
|
||||||
svg.append("image")
|
svg.append("image")
|
||||||
.attr("x", width + margin.right*0.1)
|
.attr("x", width + margin.right*0.1)
|
||||||
.attr("y", height*0.1)
|
.attr("y", height*0.1)
|
||||||
.attr("width", height*0.8)
|
.attr("width", height*0.8)
|
||||||
.attr("height", height*0.8)
|
.attr("height", height*0.8)
|
||||||
.attr("xlink:href", dataSubset[0].image) // placeholder picture for now, should be item picture
|
.attr("xlink:href", items[key].image)
|
||||||
.on("click", function() { window.open(link); });
|
.on("click", function() {
|
||||||
|
window.open(link, '_blank', 'noopener,noreferrer');
|
||||||
|
});
|
||||||
|
|
||||||
// Price domain (height)
|
// Price domain (height)
|
||||||
const y = d3.scaleLinear()
|
const y = d3.scaleLinear()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user