2# +==== BEGIN CatFeeder =================+
5# ...............)..(.')
7# ...............\(__)|
8# Inspired by Joan Stark
9# source https://www.asciiart.eu/
13# FILE: favicon_helpers.py
14# CREATION DATE: 12-01-2026
15# LAST Modified: 22:17:14 12-01-2026
17# This is the backend server in charge of making the actual website work.
19# COPYRIGHT: (c) Cat Feeder
20# PURPOSE: File in charge of containing the functions that will help the favicon classes in the different processes.
22# +==== END CatFeeder =================+
25from typing
import List, Dict, Any, Union, Optional, TYPE_CHECKING
26from pathlib
import Path
27from display_tty
import Disp, initialise_logger
28from ..config.env_loader
import EnvLoader
29from ..image_reducer
import image_reducer_constants
as IR_CONST
30from ..http_codes
import HttpDataTypes
31from .
import favicon_constants
as FAV_CONST
36FAVICON_HELPER_DISP: Disp = initialise_logger(
37 class_name=
"FaviconHelperDisp", debug=
EnvLoader().debug
42 """Convert an ImageReducer FileFormat to an HttpDataTypes value.
45 reducer_type (IR_CONST.FileFormat): The image reducer file format.
48 HttpDataTypes: The corresponding HTTP data type.
51 f
"Converting reducer type '{reducer_type}' to HTTP data type."
53 if reducer_type == IR_CONST.FileFormat.PNG:
54 disp.log_debug(
"Matched PNG format.")
55 return HttpDataTypes.PNG
56 if reducer_type == IR_CONST.FileFormat.JPEG:
57 disp.log_debug(
"Matched JPEG format.")
58 return HttpDataTypes.JPEG
59 if reducer_type == IR_CONST.FileFormat.WEBP:
60 disp.log_debug(
"Matched WEBP format.")
61 return HttpDataTypes.WEBP
62 if reducer_type == IR_CONST.FileFormat.SVG:
63 disp.log_debug(
"Matched SVG format.")
64 return HttpDataTypes.SVG
65 disp.log_debug(
"No matching format found. Defaulting to OCTET_STREAM.")
66 return HttpDataTypes.OCTET_STREAM
70 """Generate the full image path for a favicon in the bucket.
73 filename (str): The filename of the image.
74 file_id (str): The unique identifier for the favicon.
77 str: The full path to the image in the bucket.
79 file_format = Path(filename).name.rsplit(
82 return str(Path(FAV_CONST.FAVICON_BUCKET_FOLDER_USER) / f
"{file_id}.{file_format}")
85def list_from_table(sql:
"SQL", table: str, *, title: str =
"_list_from_table", disp: Disp = FAVICON_HELPER_DISP) -> List[Dict[str, Any]]:
86 """Retrieve all rows from `table` using the provided SQL helper.
88 This wraps the low-level SQL `get_data_from_table` call and returns a
89 list of dictionaries. On SQL failure the function logs the error and
90 returns an empty list.
93 sql (SQL): SQL helper instance with `get_data_from_table`.
94 table (str): Table name to query.
95 title (str): Optional logging title.
96 disp (Disp): Optional display logger.
99 List[Dict[str, Any]]: The list of rows (possibly empty on error).
102 f
"Gathering the list of entries from table '{table}'", title
104 resp = sql.get_data_from_table(
108 if isinstance(resp, int):
110 f
"Failed to gather data for table '{table}'", title
113 "Returning []", title
117 f
"Data gathered for table '{table}':\n{resp}", title
122def extract_line_from_id(data_list: List[Dict[str, Any]], entry_id: Optional[Union[int, str]], *, disp: Disp = FAVICON_HELPER_DISP) -> Dict[str, Any]:
123 """Extract a line from a list of dictionaries based on the 'id' key.
126 data_list (List[Dict[str, Any]]): The list of dictionaries to search.
127 entry_id (Union[int,str]): The id to search for.
128 disp (Disp, optional): The display logger to use.
131 Dict[str, Any]: The dictionary with the matching id. If no matching
132 entry is found the function returns a dictionary containing only the
133 `'id'` key with the original `entry_id` value (e.g. `{'id': entry_id}`).
137 "No entry_id provided, returning empty dictionary.",
"extract_line_from_id"
139 return {
'id': entry_id}
141 f
"Extracting entry with id '{entry_id}' from data list.",
"extract_line_from_id"
143 entry_id_str: str = str(entry_id)
144 for entry
in data_list:
145 if str(entry.get(
"id")) == entry_id_str:
147 f
"Entry found: {entry}",
"extract_line_from_id"
151 f
"No entry found with id '{entry_id}'. Returning empty dictionary.",
"extract_line_from_id"
153 return {
'id': entry_id}
156def get_from_table(sql:
"SQL", table: str, item_id: Union[int, str], *, title: str =
"get_from_table", disp: Disp = FAVICON_HELPER_DISP) -> Dict[str, Any]:
157 """Retrieve a single row by id from `table`.
160 sql (SQL): SQL helper instance.
161 table (str): Table name to query.
162 item_id (Union[int, str]): The id value to look up.
163 title (str): Optional logging title.
164 disp (Disp): Optional display logger.
167 Dict[str, Any]: The found row as a dictionary. If the SQL call fails
168 the function returns a dict with `{'id': id}`. If the query succeeds
169 but no rows are found it returns an empty dict.
172 f
"Gathering a single entry from table '{table}'", title
174 resp = sql.get_data_from_table(
177 where=f
"id={item_id}",
180 if isinstance(resp, int):
182 f
"Failed to gather data for table '{table}'", title
185 "Returning empty dictionary", title
190 f
"No entries found in table '{table}'. Returning empty dictionary.", title
194 f
"Data gathered for table '{table}':\n{resp[0]}", title
200 """Check if a string is a valid hex colour code.
203 colour (str): The colour string to validate.
205 bool: True if valid hex colour, False otherwise.
207 if isinstance(colour, str):
208 return bool(FAV_CONST.FAVICON_HEX_COLOUR_RE.fullmatch(colour))
214 Normalize a hex colour to full form.
218 - #RRGGBB → #RRGGBB or #RRGGBBFF
219 - #RRGGBBAA → unchanged
222 colour (str): Hex colour
223 with_alpha (bool): If True, ensure alpha channel exists
226 str: Normalized hex colour
231 hex_part = colour[1:]
233 if len(hex_part)
in (3, 4):
235 hex_part =
''.join(c * 2
for c
in hex_part)
237 if len(hex_part) == 6
and with_alpha:
240 return '#' + hex_part.upper()
245 Compress a hex colour to shorthand when safe.
249 - Otherwise unchanged
254 hex_part = colour[1:]
256 if len(hex_part)
not in (6, 8):
262 for i
in range(0, len(hex_part), step):
263 pair = hex_part[i:i + 2]
264 if pair[0] != pair[1]:
266 compressed.append(pair[0])
268 return '#' +
''.join(compressed).upper()
Dict[str, Any] extract_line_from_id(List[Dict[str, Any]] data_list, Optional[Union[int, str]] entry_id, *, Disp disp=FAVICON_HELPER_DISP)
List[Dict[str, Any]] list_from_table("SQL" sql, str table, *, str title="_list_from_table", Disp disp=FAVICON_HELPER_DISP)
HttpDataTypes reducer_type_to_data_type(IR_CONST.FileFormat reducer_type, *, Disp disp=FAVICON_HELPER_DISP)
bool is_hex_colour_valid(str colour)
str pad_hex_colour(str colour, bool with_alpha=True)
Dict[str, Any] get_from_table("SQL" sql, str table, Union[int, str] item_id, *, str title="get_from_table", Disp disp=FAVICON_HELPER_DISP)
str unpad_hex_colour(str colour)
str generate_image_path(str filename, str file_id)