Image Generation with Background Tasks

Generating images using background tasks

Want more?

This lesson for enrolled students only. Join the course to unlock it!

You can see the code changes implemented in this lecture below.

If you have purchased the course in a different platform, you still have access to the code changes per lecture here on Teclado. The lecture video and lecture notes remain locked.
Join course for $30

Modified files

storeapi/tasks.py
--- 
+++ 
@@ -1,8 +1,10 @@
 import logging
+from json import JSONDecodeError

 import httpx
-
+from databases import Database
 from storeapi.config import config
+from storeapi.database import post_table

 logger = logging.getLogger(__name__)

@@ -46,3 +48,68 @@
             f" following link: {confirmation_url}"
         ),
     )
+
+
+async def _generate_cute_creature_api(prompt: str):
+    logger.debug("Generating cute creature")
+    async with httpx.AsyncClient() as client:
+        try:
+            response = await client.post(
+                "https://api.deepai.org/api/cute-creature-generator",
+                data={"text": prompt},
+                headers={"api-key": config.DEEPAI_API_KEY},
+                timeout=60,
+            )
+            logger.debug(response)
+            response.raise_for_status()
+            return response.json()
+        except httpx.HTTPStatusError as err:
+            raise APIResponseError(
+                f"API request failed with status code {err.response.status_code}"
+            ) from err
+        except (JSONDecodeError, TypeError) as err:
+            raise APIResponseError("API response parsing failed") from err
+
+
+async def generate_and_add_to_post(
+    email: str,
+    post_id: int,
+    post_url: str,
+    database: Database,
+    prompt: str = "A blue british shorthair cat is sitting on a couch",
+):
+    try:
+        response = await _generate_cute_creature_api(prompt)
+    except APIResponseError:
+        return await send_simple_email(
+            email,
+            "Error generating image",
+            (
+                f"Hi {email}! Unfortunately there was an error generating an image"
+                "for your post."
+            ),
+        )
+
+    logger.debug("Connecting to database to update post")
+
+    query = (
+        post_table.update()
+        .where(post_table.c.id == post_id)
+        .values(image_url=response["output_url"])
+    )
+
+    logger.debug(query)
+
+    await database.execute(query)
+
+    logger.debug("Database connection in background task closed")
+
+    await send_simple_email(
+        email,
+        "Image generation completed",
+        (
+            f"Hi {email}! Your image has been generated and added to your post."
+            f" Please click on the following link to view it: {post_url}"
+        ),
+    )
+    return response
storeapi/tests/test_tasks.py
--- 
+++ 
@@ -1,7 +1,13 @@
 import httpx
 import pytest
-
-from storeapi.tasks import APIResponseError, send_simple_email
+from databases import Database
+from storeapi.database import database, post_table
+from storeapi.tasks import (
+    APIResponseError,
+    _generate_cute_creature_api,
+    generate_and_add_to_post,
+    send_simple_email,
+)


 @pytest.mark.anyio
@@ -18,3 +24,60 @@

     with pytest.raises(APIResponseError):
         await send_simple_email("test@example.net", "Test Subject", "Test Body")
+
+
+@pytest.mark.anyio
+async def test_generate_cute_creature_api_success(mock_httpx_client):
+    json_data = {"output_url": "https://example.com/image.jpg"}
+
+    mock_httpx_client.post.return_value = httpx.Response(
+        status_code=200, json=json_data, request=httpx.Request("POST", "//")
+    )
+
+    result = await _generate_cute_creature_api("A cat")
+
+    assert result == json_data
+
+
+@pytest.mark.anyio
+async def test_generate_cute_creature_api_server_error(mock_httpx_client):
+    mock_httpx_client.post.return_value = httpx.Response(
+        status_code=500, content="", request=httpx.Request("POST", "//")
+    )
+
+    with pytest.raises(
+        APIResponseError, match="API request failed with status code 500"
+    ):
+        await _generate_cute_creature_api("A cat")
+
+
+@pytest.mark.anyio
+async def test_generate_cute_creature_api_json_error(mock_httpx_client):
+    mock_httpx_client.post.return_value = httpx.Response(
+        status_code=200, content="Not JSON", request=httpx.Request("POST", "//")
+    )
+
+    with pytest.raises(APIResponseError, match="API response parsing failed"):
+        await _generate_cute_creature_api("A cat")
+
+
+@pytest.mark.anyio
+async def test_generate_and_add_to_post_success(
+    mock_httpx_client, created_post: dict, confirmed_user: dict, db: Database
+):
+    json_data = {"output_url": "https://example.com/image.jpg"}
+
+    mock_httpx_client.post.return_value = httpx.Response(
+        status_code=200, json=json_data, request=httpx.Request("POST", "//")
+    )
+
+    await generate_and_add_to_post(
+        confirmed_user["email"], created_post["id"], "/post/1", db, "A cat"
+    )
+
+    # Check that after the background task runs, the post has been updated
+    query = post_table.select().where(post_table.c.id == created_post["id"])
+
+    updated_post = await database.fetch_one(query)
+
+    assert updated_post.image_url == json_data["output_url"]