Examples of async reciever tasks
parent
6b3bee66c1
commit
dde966e650
@ -1,2 +1,4 @@
|
|||||||
[MASTER]
|
[MASTER]
|
||||||
ignore-paths=^alembic\\.*$|^alembic/.*$
|
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