Coverage for fastblocks/adapters/images/imagekit.py: 58%
50 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-09 00:47 -0700
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-09 00:47 -0700
1"""ImageKit image adapter implementation."""
3from contextlib import suppress
4from typing import Any
5from uuid import UUID
7from acb.config import Settings
8from acb.depends import depends
10from ._base import ImagesBase
13class ImageKitSettings(Settings): # type: ignore[misc]
14 """ImageKit-specific settings."""
16 public_key: str
17 private_key: str
18 endpoint_url: str
19 upload_folder: str = "fastblocks"
22class ImageKitAdapter(ImagesBase):
23 """ImageKit image adapter implementation."""
25 # Required ACB 0.19.0+ metadata
26 MODULE_ID: UUID = UUID("01937d86-4f2a-7b3c-8d9e-f3b4d3c2b2a2") # Static UUID7
27 MODULE_STATUS = "stable"
29 def __init__(self) -> None:
30 """Initialize ImageKit adapter."""
31 super().__init__()
32 self.settings = ImageKitSettings()
34 # Register with ACB dependency system
35 with suppress(Exception):
36 depends.set(self)
38 async def upload_image(self, file_data: bytes, filename: str) -> str:
39 """Upload image to ImageKit and return file_id."""
40 # Basic implementation - would integrate with imagekit library
41 # For now, return a mock file_id based on filename
42 file_id = filename.rsplit(".", 1)[0] # Remove extension
43 return f"{self.settings.upload_folder}/{file_id}"
45 async def get_image_url(
46 self, image_id: str, transformations: dict[str, Any] | None = None
47 ) -> str:
48 """Generate ImageKit URL with optional transformations."""
49 base_url = self.settings.endpoint_url.rstrip("/")
51 if transformations:
52 # Build transformation string (ImageKit format)
53 transform_parts = []
54 for key, value in transformations.items():
55 if key == "width":
56 transform_parts.append(f"w-{value}")
57 elif key == "height":
58 transform_parts.append(f"h-{value}")
59 elif key == "crop":
60 transform_parts.append(f"c-{value}")
61 elif key == "quality":
62 transform_parts.append(f"q-{value}")
63 elif key == "format":
64 transform_parts.append(f"f-{value}")
66 if transform_parts:
67 transform_str = ",".join(transform_parts)
68 return f"{base_url}/tr:{transform_str}/{image_id}"
70 return f"{base_url}/{image_id}"
72 def get_img_tag(self, image_id: str, alt: str, **attributes: Any) -> str:
73 """Generate complete img tag with ImageKit URL."""
74 url = self.get_image_url(image_id, attributes.pop("transformations", None))
76 # Build attributes string
77 attr_parts = [f'src="{url}"', f'alt="{alt}"']
79 for key, value in attributes.items():
80 if key in ("width", "height", "class", "id", "style"):
81 attr_parts.append(f'{key}="{value}"')
83 # Add lazy loading by default
84 if "loading" not in attributes:
85 attr_parts.append('loading="lazy"')
87 return f"<img {' '.join(attr_parts)}>"