Examples of async reciever tasks
parent
6b3bee66c1
commit
dde966e650
@ -1,2 +1,4 @@
|
||||
[MASTER]
|
||||
ignore-paths=^alembic\\.*$|^alembic/.*$
|
||||
; Needed to prevent E0611: No name 'BaseModel' in module 'pydantic' (no-name-in-module)
|
||||
extension-pkg-whitelist=pydantic
|
||||
|
@ -0,0 +1,7 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class UserBody(BaseModel):
|
||||
|
||||
username: str
|
||||
email: str
|
@ -0,0 +1,122 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Celery example</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/css/bootstrap.min.css"
|
||||
integrity="sha512-XWTTruHZEYJsxV3W/lSXG1n3Q39YIWOstqvmFsdNEEQfHoZ6vm6E9GK2OrF6DSJSpIbRbi+Nn0WDPID9O7xB2Q=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4">
|
||||
<form id="your-form">
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email address</label>
|
||||
<input type="email" class="form-control" id="email" name="email">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">Username</label>
|
||||
<input type="text" class="form-control" id="username" name="username">
|
||||
</div>
|
||||
<div class="mb-3" id="messages"></div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/js/bootstrap.min.js"
|
||||
integrity="sha512-8Y8eGK92dzouwpROIppwr+0kPauu0qqtnzZZNEF8Pat5tuRNJxJXCkbQfJ0HlUG3y1HB3z18CSKmUo7i2zcPpg=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script>
|
||||
function updateProgress(yourForm, task_id, btnHtml) {
|
||||
fetch(`/users/task_status/?task_id=${task_id}`, {
|
||||
method: 'GET',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then((res) => {
|
||||
const taskStatus = res.state;
|
||||
|
||||
if (['SUCCESS', 'FAILURE'].includes(taskStatus)) {
|
||||
const msg = yourForm.querySelector('#messages');
|
||||
const submitBtn = yourForm.querySelector('button[type="submit"]');
|
||||
|
||||
if (taskStatus === 'SUCCESS') {
|
||||
msg.innerHTML = 'job succeeded';
|
||||
} else if (taskStatus === 'FAILURE') {
|
||||
// display error message on the form
|
||||
msg.innerHTML = res.error;
|
||||
}
|
||||
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerHTML = btnHtml;
|
||||
} else {
|
||||
// the task is still running
|
||||
setTimeout(function() {
|
||||
updateProgress(yourForm, task_id, btnHtml);
|
||||
}, 1000);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error)
|
||||
});
|
||||
}
|
||||
|
||||
function serialize(data) {
|
||||
let obj = {};
|
||||
for (let [key, value] of data) {
|
||||
if (obj[key] !== undefined) {
|
||||
if (!Array.isArray(obj[key])) {
|
||||
obj[key] = [obj[key]];
|
||||
}
|
||||
obj[key].push(value);
|
||||
} else {
|
||||
obj[key] = value;
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const yourForm = document.getElementById("your-form");
|
||||
yourForm.addEventListener("submit", function(event) {
|
||||
event.preventDefault();
|
||||
const submitBtn = yourForm.querySelector('button[type="submit"]');
|
||||
const btnHtml = submitBtn.innerHTML;
|
||||
const spinnerHtml = 'Processing...';
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = spinnerHtml;
|
||||
|
||||
const msg = yourForm.querySelector('#messages');
|
||||
msg.innerHTML = '';
|
||||
|
||||
// Get all field data from the form
|
||||
let data = new FormData(yourForm);
|
||||
// Convert to an object
|
||||
let formData = serialize(data);
|
||||
|
||||
fetch('/users/form/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(formData),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then((res) => {
|
||||
// after we get Celery task id, we start polling
|
||||
const task_id = res.task_id;
|
||||
updateProgress(yourForm, task_id, btnHtml);
|
||||
console.log(res);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error)
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,71 @@
|
||||
import logging
|
||||
import random
|
||||
|
||||
import requests
|
||||
from celery.result import AsyncResult
|
||||
from fastapi import Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
from . import users_router
|
||||
from .schema import UserBody
|
||||
from .tasks import sample_task, task_process_notification
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
templates = Jinja2Templates(directory="project/users/templates")
|
||||
|
||||
|
||||
def api_call(email: str): # pylint: disable=unused-argument
|
||||
# used for testing a failed api call
|
||||
if random.choice([0, 1]):
|
||||
raise Exception("random processing error")
|
||||
|
||||
# used for simulating a call to a third-party api
|
||||
requests.post("https://httpbin.org/delay/5", timeout=30)
|
||||
|
||||
|
||||
@users_router.get("/form/")
|
||||
def form_example_get(request: Request):
|
||||
return templates.TemplateResponse("form.html", {"request": request})
|
||||
|
||||
|
||||
@users_router.post("/form/")
|
||||
def form_example_post(user_body: UserBody):
|
||||
task = sample_task.delay(user_body.email)
|
||||
return JSONResponse({"task_id": task.task_id})
|
||||
|
||||
|
||||
@users_router.get("/task_status/")
|
||||
def task_status(task_id: str):
|
||||
task = AsyncResult(task_id)
|
||||
state = task.state
|
||||
|
||||
if state == "FAILURE":
|
||||
error = str(task.result)
|
||||
response = {
|
||||
"state": state,
|
||||
"error": error,
|
||||
}
|
||||
else:
|
||||
response = {
|
||||
"state": state,
|
||||
}
|
||||
return JSONResponse(response)
|
||||
|
||||
|
||||
@users_router.post("/webhook_test/")
|
||||
def webhook_test():
|
||||
if not random.choice([0, 1]):
|
||||
# mimic an error
|
||||
raise Exception()
|
||||
|
||||
# blocking process
|
||||
requests.post("https://httpbin.org/delay/5", timeout=30)
|
||||
return "pong"
|
||||
|
||||
|
||||
@users_router.post("/webhook_test_async/")
|
||||
def webhook_test_async():
|
||||
task = task_process_notification.delay()
|
||||
print(task.id)
|
||||
return "pong"
|
Loading…
Reference in New Issue