I am implementing an object-detection web application using React and Tensorflow JS. I converted my model to a tensorflow JS model, such that I can load it into my React application. I want to load the model using a simple HTTP endpoint, which is a Flask server currently hosting on my local machine. The Flask main file looks as follows:
from flask import Flask
from flask_cors import CORS, cross_origin
import os
app = Flask(__name__)
cors = CORS(app)
#app.route('/')
def hello_world():
return 'Hello, World!'
#app.route('/model', methods=['GET'])
def get_modeljson():
"""
Get the model.json file and return it's contents.
"""
current_dir = os.getcwd()
file_path = os.path.join(current_dir, "models", "model.json")
with open(file_path, "r") as f:
return f.read()
if __name__ == '__main__':
app.run(debug=True, host="0.0.0.0", threaded=True)
I have written a function in my React application that loads the graph model using the endpoint /model that is defined in the code above. The React function looks as follows:
import {useEffect, useState} from 'react';
import * as tf from '#tensorflow/tfjs';
import {loadGraphModel} from '#tensorflow/tfjs-converter';
function Model(props) {
const [model, setModel] = useState();
async function loadModel() {
try {
const model_url = "http://127.0.0.1:5000/model";
const result = await fetch(model_url);
const result_json = await result.json();
const model = await loadGraphModel(result_json);
console.log('model loaded...')
setModel(model);
console.log("Model correctly loaded");
} catch (err) {
console.log(err);
console.log("failed load model");
}
}
useEffect(() => {
tf.ready().then(() => {
loadModel();
});
}, []);
async function predictFunction() {
// use model to make predictions
}
return (
<Button onClick={() => {
predictFunction();
}}
/>
);
}
export default Model;
The FLASK API returns correctly the model.json file, however loadGraphModel returns the following error:
TypeError: url.startsWith is not a function
at indexedDBRouter (indexed_db.ts:215)
at router_registry.ts:95
at Array.forEach (<anonymous>)
at Function.getHandlers (router_registry.ts:94)
at Function.getLoadHandlers (router_registry.ts:84)
at Module.getLoadHandlers (router_registry.ts:110)
at GraphModel.findIOHandler (graph_model.ts:107)
at GraphModel.load (graph_model.ts:126)
at loadGraphModel (graph_model.ts:440)
at loadModel (Model.js:16)
I can not find any documentation about url.startsWith. Who sees what is going wrong here?
Going through the code I see a major issue with it, where you are trying to basically send a model.json from the backend to the frontend and then load the model from that model.json and perform inference on it. It would work but it is not efficient at all. Imagine having to do this a couple hundred times and I know the model.json file can be big in size. Instead there are two routes that you could go with:
Host the model on the backend, send the data to the backend through a POST request and then make predictions on the data from the request.
Use the model on the frontend and then make predictions on the input data from there.
There are some errors in the code which are causing the error but this is the issue that you need to fix first. If you could give me more information about the inputs you are working with I could draft up a workable solution.
Related
I'm learning Sveltekit and i've been stuck on passing data from backend to frontend. I have a 3rd party API that i want to call and send back the results that gives me the bearer needed for all the other API calls.
The problem is that the CORS policys of this server (that i have no control over) isn't configured well and i can't call the API from the browser.
So i wanted to call it from the backend and send the result to the front, what I did is a "+page.server.ts" file with the API call in the "load" function and puts the results in a store that i created in the stores.ts file :
import { writable } from 'svelte/store';
export const token = writable('');
So in the front (+page.svelte) I just imported the token from the store and showed it :
<script>
import { token } from "../stores";
</script>
<h1>{$token}</h1>
And it works for a second before the token is overriden with an empty string and i don't get why.
Do you have any idea of what could cause this ?
Feel free to ask for more informations if needed, because i'm lost right now
You cannot access a svelte store from within a server file.
To call a api from +page.server.js. Create a folder in your routes and add a +page.server.js file and add the following:
/** #type {import('./$types').PageServerLoad} */
export async function load({ fetch }) {
const res = await fetch('https://api.agify.io/?name=bella', {
method: 'GET'
});
const data = await res.json();
console.log(data);
return {
props: {
data: data
}
};
}
Then create a +page.svelte with the following code:
<script>
/** #type {import('./$types').PageData} */
export let data;
console.log(data);
let name = data.props.data.name;
let age = data.props.data.age;
</script>
<h1>
{name} <!-- bella -->
{age} <!-- the age -->
</h1>
SvelteKit docs have info that could be of use. SvelteKit docs loading data
I'm using Uppy for file uploads in React, with a Rails API using Shrine.
I'm trying to show a preview for an uploaded video before submitting a form. It's important to emphasize that this is specifically for a video upload, not an image. So the 'thumbnail:generated' event will not apply here.
I can't seem to find any events that uppy provides that returns a cached video preview (like thumbnail:generated does) or anything that passes back a presigned url for the uploaded file (less expected, obviously), so the only option I see is constructing the url manually. Here's what I'm currently trying for that (irrelevant code removed for brevity):
import React, { useEffect, useState } from 'react'
import AwsS3 from '#uppy/aws-s3'
import Uppy from '#uppy/core'
import axios from 'axios'
import { DragDrop } from '#uppy/react'
import { API_BASE } from '../../../api'
const constructParams = (metadata) => ([
`?X-Amz-Algorithm=${metadata['x-amz-algorithm']}`,
`&X-Amz-Credential=${metadata['x-amz-credential']}`,
`&X-Amz-Date=${metadata['x-amz-date']}`,
'&X-Amz-Expires=900',
'&X-Amz-SignedHeaders=host',
`&X-Amz-Signature=${metadata['x-amz-signature']}`,
].join('').replaceAll('/', '%2F'))
const MediaUploader = () => {
const [videoSrc, setVideoSrc] = useState('')
const uppy = new Uppy({
meta: { type: 'content' },
restrictions: {
maxNumberOfFiles: 1
},
autoProceed: true,
})
const getPresigned = async (id, type) => {
const response = await axios.get(`${API_BASE}/s3/params?filename=${id}&type=${type}`)
const { fields, url } = response.data
const params = constructParams(fields)
const presignedUrl = `${url}/${fields.key}${params}`
console.log('presignedUrl from Shrine request data: ', presignedUrl)
setVideoSrc(presignedUrl)
}
useEffect(() => {
uppy
.use(AwsS3, {
id: `AwsS3:${Math.random()}`,
companionUrl: API_BASE,
})
uppy.on('upload-success', (file, _response) => {
const { type, meta } = file
// First attempt to construct presigned URL here
const url = 'https://my-s3-bucket.s3.us-west-1.amazonaws.com'
const params = constructParams(meta)
const presignedUrl = `${url}/${meta.key}${params}`
console.log('presignedUrl from upload-success data: ', presignedUrl)
// Second attempt to construct presigned URL here
const id = meta.key.split(`${process.env.REACT_APP_ENV}/cache/`)[1]
getPresigned(id, type)
})
}, [uppy])
return (
<div className="MediaUploader">
<div className="Uppy__preview__wrapper">
<video
src={videoSrc || ''}
className="Uppy__preview"
controls
/>
</div>
{(!videoSrc || videoSrc === '') && (
<DragDrop
uppy={uppy}
className="UploadForm"
locale={{
strings: {
dropHereOr: 'Drop here or %{browse}',
browse: 'browse',
},
}}
/>
)}
</div>
)
}
export default MediaUploader
Both urls here come back with a SignatureDoesNotMatch error from AWS.
The manual construction of the url comes mainly from constructParams. I have two different implementations of this, the first of which takes the metadata directly from the uploaded file data in the 'upload-success' event, and then just concatenates a string to build the url. The second one uses getPresigned, which makes a request to my API, which points to a generated Shrine path that should return data for a presigned URL. API_BASE simply points to my Rails API. More info on the generated Shrine route here.
It's worth noting that everything works perfectly with the upload process that passes through Shrine, and after submitting the form, I'm able to get a presigned url for the video and play it without issue on the site. So I have no reason to believe Shrine is returning incorrectly signed urls.
I've compared the two presigned urls I'm manually generating in the form, with the url returned from Shrine after uploading. All 3 are identical in structure, but have different signatures. Here are those three urls:
presignedUrl from upload-success data:
https://my-s3-bucket.s3.us-west-1.amazonaws.com/development/cache/41b229fb17cbf21925d2cd907a59be25.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAW63AYCMFA4374OLC%2F20221210%2Fus-west-1%2Fs3%2Faws4_request&X-Amz-Date=20221210T132613Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=97aefd1ac7f3d42abd2c48fe3ad50b542742ad0717a51528c35f1159bfb15609
presignedUrl from Shrine request data:
https://my-s3-bucket.s3.us-west-1.amazonaws.com/development/cache/023592fb14c63a45f02c1ad89a49e5fd.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAW63AYCMFA4374OLC%2F20221210%2Fus-west-1%2Fs3%2Faws4_request&X-Amz-Date=20221210T132619Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=7171ac72f7db2b8871668f76d96d275aa6c53f71b683bcb6766ac972e549c2b3
presigned url displayed on site after form submission:
https://my-s3-bucket.s3.us-west-1.amazonaws.com/development/cache/41b229fb17cbf21925d2cd907a59be25.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAW63AYCMFA4374OLC%2F20221210%2Fus-west-1%2Fs3%2Faws4_request&X-Amz-Date=20221210T132734Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=9ecc98501866f9c5bd460369a7c2ce93901f94c19afa28144e0f99137cdc2aaf
The first two urls come back with SignatureDoesNotMatch, while the third url properly plays the video.
I'm aware the first and third urls have the same file name, while the second url does not. I'm not sure what to make of that, though, but the relevance of this is secondary to me, since that solution was more of a last ditch effort anyway.
I'm not at all attached to the current way I'm doing things. It's just the only solution I could come up with, due to lack of options. If there's a better way of going about this, I'm very open to suggestions.
I'm learning Django Rest Framework, everything was going well until I had to create a service to upload files to my application.As much as I read the documentation, I can't understand it.
First I want to clarify that I am not a programming expert, I am a
newbie but I am here learning more every day.
From what I've managed to understand so far:
Documents and photos are not stored in the database. These files are stored in a folder.
This is correct ?
I have a form where it allows me to upload multiple files
example:
file.txt, document.doc, photo.png etc...
My view (Frontend):
import { useState } from "react";
import axios from "axios";
const Form = () => {
const [state_files, setState_Files] = useState(null);
const UploadFiles = function (event) {
setState_Files(event);
};
const InsertFiles = async function () {
const formData = new FormData();
for (let index = 0; index < state_files.length; index++) {
formData.append("files", state_files[index]);
}
await axios
.post("http://127.0.0.1:8000/api/v1/upload/", formData)
.then((response) => {
console.log(response.data);
})
.catch((error) => {
console.log(error);
});
};
return (
<>
<input
type="file"
name="files"
multiple
onChange={() => InsertFiles(event.target.files)}
/>
<button>Upload All files</button>
</>
);
};
export default Form;
Backend
url.py
path("upload/", Storage_View.as_view(), name="storage-index"),
storage/view.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import MultiPartParser
class Storage_View(APIView):
parser_classes = [MultiPartParser]
def put(self, request, filename, format=None):
file_obj = request.data['file']
# ...
# What do I have to do here?
# ...
return Response({'received data': request.data})
Questions:
Why don't I see the button to upload files using the DFR tool? see attached files
The documentation does not explain what I have to do inside the function comment
https://www.django-rest-framework.org/api-guide/parsers/
def put(...):
How do I specify the path where you have to store the files you upload?
Please I need guidance.
1: show your models for more detail. you have to use filefield or
imagefield in your model. You can google and read more about these
fields.
2: put is for update, write your post method to save data. You don't
have to really do anything serious there. just check if
serializer.is_Valid() for request.data and if valid do
serializer.save(). the file or image will be uploaded to upload_to
folder which you define in your model and you will get the link.
3: read more about upload_to in Django. you can define this with the
model field.
I used ModelViewSet and this is how the create method looks like -
def create(self, request, format=None):
data = request.data
if isinstance(data, list): # <- is the main logic
serializer = self.get_serializer(data=request.data, many=True)
else:
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
You are passing a list from frontend and by default, Django works on dictionary, so you will have to manage this. Think of this as your go to code and write your post method.
Feel free to write more if you have doubts, Hope this helps :)
In my project I am using flask_socketio as the server and socket.io-client with react as the client. My main.py(flask server) is constantly reading a log file(console.log) that is continuously updating. When the start button is clicked in the UI, the data of the log file is displayed, but as my log file is updated, the updated data is not displayed in the UI. I have to refresh the page or click the button again to see the updated data.
I want the data of the log file to live stream on the UI with a button click. How to fix this?
flask code
from flask import Flask, jsonify
# Needed for localhost testing.
from flask_cors import CORS, cross_origin
from flask_socketio import SocketIO, emit
from time import sleep
import pdb
import json
app = Flask(__name__)
# Socket io setup.
app.config['SECRET_KEY'] = 'secret!'
# |cors_allowed_origins| is required for localhost testing.
socket = SocketIO(app, cors_allowed_origins="*")
# For localhost testing.
CORS(app)
#socket.on('event_stream', namespace='/logConsole')
def test_connect():
def generate():
fname = "./src/console.log"
with open(fname) as f:
yield f.read()
emit_data = next(generate())
socket.sleep(0)
emit('custom-server-msg', {'data': emit_data})
if __name__ == '__main__':
socket.run(app)
React code
import React from 'react'
import io from 'socket.io-client'
class App extends React.Component {
state = { startVar: true, setVar: false };
setSocketListeners() {
let socket = io.connect('ws://localhost:5000/logConsole');
socket.emit('event_stream', () => {
console.log("Websocket connected: " + socket.connected)
})
socket.on('custom-server-msg', (data) => {
console.log("Data received: " + data.data)
const setup_logs = data.data;
this.setState({ setup_logs });
})
}
render() {
return (
<div className="App">
<h1>Data from log file</h1>
<button type="button" onClick={() => this.setSocketListeners()}>Start</button>
<p>{this.state.setup_logs}</p>
</div>
);
}
}
export default App;
This is how my browser console looks like-->
And this is my backend console-->
In your flask code, if you want to stream continuously, the next() needs to be called in a loop, now either that can be done by putting a infinite loop with a sleep time,
#socket.on('event_stream')
def test_connect():
def generate():
fname = "./src/console.log"
with open(fname, "r+") as f:
yield f.read()
while True:
emit_data = next(generate())
socket.sleep(2)
emit('custom-server-msg', {'data':emit_data})
or else, if the log file is too being updated continuously, os.stat(FILE_NAME).st_mtime can be used which will check the time stamp of the file being updated, and if any change is there in the log file, next() will be called to stream it:
#socket.on('event_stream')
def test_connect():
cached_stamp = 0
def generate():
fname = "./src/console.log"
with open(fname, "r+") as f:
yield f.read()
while True:
stamp = os.stat('./src/console.log').st_mtime
if stamp != cached_stamp:
cached_stamp = stamp
emit_data = next(generate())
emit('topo-server-msg', {'data':emit_data})
I'm building an app with Next.js... we have 100k+ pages and content changes daily, so using SSR and getServerSideProps.
Some of our data is coming from a headless CMS provider that charges by the request. I'd like to cache the API responses from this server for 24hrs.
What is the best way of going about this?
Is there a common library most folks use to do this?
Just looking for suggestions of approaches I should investigate (or great examples of how to do this).
I used this npm package:
https://www.npmjs.com/package/memory-cache
And then something like this:
import cacheData from "memory-cache";
async function fetchWithCache(url, options) {
const value = cacheData.get(url);
if (value) {
return value;
} else {
const hours = 24;
const res = await fetch(url, options);
const data = await res.json();
cacheData.put(url, data, hours * 1000 * 60 * 60);
return data;
}
}
Then if you want to fetch something with using the cache just call this function. Or it can be used as a midware in the requests. It checks if the data is already in the cache and returns it, or if not - it puts the data into the cache under the key. The key can be anything, I am using the url for instance.
In addition to Tobias Lins' answer:
At least if deploying on Vercel, you can use set Cache-Control headers in getStaticProps, getServerSideProps, API routes, etc to cache responses on Vercel's edge network. This solution does not require any additional dependencies and very minimal code.
api route example - source Vercel
// pages/api/user.js
export default function handler(req, res) {
res.setHeader('Cache-Control', 's-maxage=86400');
res.status(200).json({ name: 'John Doe' });
}
Example in getServerSideProps - Source NextJS
// This value is considered fresh for ten seconds (s-maxage=10).
// If a request is repeated within the next 10 seconds, the previously
// cached value will still be fresh. If the request is repeated before 59 seconds,
// the cached value will be stale but still render (stale-while-revalidate=59).
//
// In the background, a revalidation request will be made to populate the cache
// with a fresh value. If you refresh the page, you will see the new value.
export async function getServerSideProps({ req, res }) {
res.setHeader(
'Cache-Control',
'public, s-maxage=10, stale-while-revalidate=59'
)
return {
props: {},
}
}
I believe you'd want to use:
res.setHeader('Cache-Control', 's-maxage=1440000')
Here are some other useful links for caching on Vercel:
https://vercel.com/docs/concepts/functions/edge-caching
https://vercel.com/docs/concepts/edge-network/overview
https://vercel.com/docs/concepts/edge-network/caching
https://vercel.com/docs/concepts/edge-network/headers
For your specific case, you also may want to look into using getStaticPaths with getStaticProps. You can use fallback: true on getStaticPaths to only build pages when they're visited (you can still build your post popular pages at initial build time).
https://nextjs.org/docs/basic-features/data-fetching#the-fallback-key-required
I know this is an old post, but for others googling (at least those deploying on Vercel), these solutions should help where revalidate in getStaticProps does not.
You could use getStaticProps from Next.js for SSG
They currently have a revalidate property that you can return, that defines how often the content should be re-fetched.
Take a look here:
https://nextjs.org/blog/next-9-5#stable-incremental-static-regeneration
This is how we did it without any 3rd party libraries, as in our use-case we only had to cache a relatively smaller amount of global data(header/footer menus) which was shared across the site.
The data was coming from a CMS via GraphQL.
We ran an async method getGlobalData on each page from on getStaticProps method and then returned the cached data to the page component via props.
import fs from 'fs';
import path from 'path';
// Cache files are stored inside ./next folder
const CACHE_PATH = path.join(__dirname, 'globalData.json');
export default async function getGlobalData() {
let cachedData;
// #1 - Look for cached data first
try {
cachedData = JSON.parse(fs.readFileSync(CACHE_PATH, 'utf8'));
} catch (error) {
console.log('❌ CACHE NOT INITIALIZED');
}
// #2 - Create Cache file if it doesn't exist
if (!cachedData) {
// Call your APIs to-be-cached here
const data = await fetchGlobalData();
// Store data in cache files
// this always rewrites/overwrites the previous file
try {
await fs.writeFileSync(
CACHE_PATH,
JSON.stringify(data),
err =>throw err
);
console.log('💾 CACHE FILE WRITTEN SUCCESSFULLY');
} catch (error) {
console.log('❌ ERROR WRITING MEMBERS CACHE TO FILE\n', error);
}
cachedData = data;
}
return cachedData;
}
Call getGlobalData method from getStaticProps.
export async function getStaticProps({ preview = false }) {
const globalData = await getGlobalData();
// call other page-specific/non-shared APIs here
// ...
return { props: { globalData } };
}
References
https://flaviocopes.com/nextjs-cache-data-globally/
Note if you get an error saying fs or path is unknown or invalid, then please understand that, the above code is supposed to be running or referenced "serverside" i.e only inside getStaticProps or getServerSideProps. If you import and reference it "browser-side", say somewhere inside your components or on the page (other than methods mentioned above), then you will get an error, as there is no filesystem(fs) or path modules on browser. They are only available on node.