mirror of
https://github.com/shadoll/moma.git
synced 2026-08-28 03:27:34 +00:00
Refactor code structure and remove redundant code blocks for improved readability and maintainability
This commit is contained in:
@@ -1,71 +1,117 @@
|
||||
class DefaultExtractor:
|
||||
"""Extractor that provides default fallback values"""
|
||||
"""Default extractor providing fallback values.
|
||||
|
||||
def extract_title(self):
|
||||
This module provides a minimal implementation of the DataExtractor protocol
|
||||
that returns default/empty values for all extraction methods. Used as a
|
||||
fallback when no specific extractor is available.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class DefaultExtractor:
|
||||
"""Extractor that provides default fallback values for all extraction methods.
|
||||
|
||||
This class implements the DataExtractor protocol by returning sensible
|
||||
defaults (None, empty strings, empty lists) for all extraction operations.
|
||||
It's used as a final fallback in the extractor chain when no other
|
||||
extractor can provide data.
|
||||
|
||||
All methods return None or empty values, making it safe to use when
|
||||
no actual data extraction is possible.
|
||||
"""
|
||||
|
||||
def extract_title(self) -> Optional[str]:
|
||||
"""Return default title.
|
||||
|
||||
Returns:
|
||||
Default title string "Unknown Title"
|
||||
"""
|
||||
return "Unknown Title"
|
||||
|
||||
def extract_year(self):
|
||||
def extract_year(self) -> Optional[str]:
|
||||
"""Return year. Returns None as no year information is available."""
|
||||
return None
|
||||
|
||||
def extract_source(self):
|
||||
def extract_source(self) -> Optional[str]:
|
||||
"""Return video source. Returns None as no source information is available."""
|
||||
return None
|
||||
|
||||
def extract_order(self):
|
||||
def extract_order(self) -> Optional[str]:
|
||||
"""Return sequence order. Returns None as no order information is available."""
|
||||
return None
|
||||
|
||||
def extract_resolution(self):
|
||||
def extract_resolution(self) -> Optional[str]:
|
||||
"""Return resolution. Returns None as no resolution information is available."""
|
||||
return None
|
||||
|
||||
def extract_hdr(self):
|
||||
def extract_hdr(self) -> Optional[str]:
|
||||
"""Return HDR information. Returns None as no HDR information is available."""
|
||||
return None
|
||||
|
||||
def extract_movie_db(self):
|
||||
def extract_movie_db(self) -> list[str] | None:
|
||||
"""Return movie database ID. Returns None as no database information is available."""
|
||||
return None
|
||||
|
||||
def extract_special_info(self):
|
||||
def extract_special_info(self) -> Optional[str]:
|
||||
"""Return special edition info. Returns None as no special info is available."""
|
||||
return None
|
||||
|
||||
def extract_audio_langs(self):
|
||||
def extract_audio_langs(self) -> Optional[str]:
|
||||
"""Return audio languages. Returns None as no language information is available."""
|
||||
return None
|
||||
|
||||
def extract_meta_type(self):
|
||||
def extract_meta_type(self) -> Optional[str]:
|
||||
"""Return metadata type. Returns None as no type information is available."""
|
||||
return None
|
||||
|
||||
def extract_size(self):
|
||||
def extract_size(self) -> Optional[int]:
|
||||
"""Return file size. Returns None as no size information is available."""
|
||||
return None
|
||||
|
||||
def extract_modification_time(self):
|
||||
def extract_modification_time(self) -> Optional[float]:
|
||||
"""Return modification time. Returns None as no timestamp is available."""
|
||||
return None
|
||||
|
||||
def extract_file_name(self):
|
||||
def extract_file_name(self) -> Optional[str]:
|
||||
"""Return file name. Returns None as no filename is available."""
|
||||
return None
|
||||
|
||||
def extract_file_path(self):
|
||||
def extract_file_path(self) -> Optional[str]:
|
||||
"""Return file path. Returns None as no file path is available."""
|
||||
return None
|
||||
|
||||
def extract_frame_class(self):
|
||||
def extract_frame_class(self) -> Optional[str]:
|
||||
"""Return frame class. Returns None as no frame class information is available."""
|
||||
return None
|
||||
|
||||
def extract_video_tracks(self):
|
||||
def extract_video_tracks(self) -> list[dict]:
|
||||
"""Return video tracks. Returns empty list as no video tracks are available."""
|
||||
return []
|
||||
|
||||
def extract_audio_tracks(self):
|
||||
def extract_audio_tracks(self) -> list[dict]:
|
||||
"""Return audio tracks. Returns empty list as no audio tracks are available."""
|
||||
return []
|
||||
|
||||
def extract_subtitle_tracks(self):
|
||||
def extract_subtitle_tracks(self) -> list[dict]:
|
||||
"""Return subtitle tracks. Returns empty list as no subtitle tracks are available."""
|
||||
return []
|
||||
|
||||
def extract_anamorphic(self):
|
||||
def extract_anamorphic(self) -> Optional[str]:
|
||||
"""Return anamorphic info. Returns None as no anamorphic information is available."""
|
||||
return None
|
||||
|
||||
def extract_extension(self):
|
||||
def extract_extension(self) -> Optional[str]:
|
||||
"""Return file extension. Returns None as no extension is available."""
|
||||
return None
|
||||
|
||||
def extract_tmdb_url(self):
|
||||
def extract_tmdb_url(self) -> Optional[str]:
|
||||
"""Return TMDB URL. Returns None as no TMDB URL is available."""
|
||||
return None
|
||||
|
||||
def extract_tmdb_id(self):
|
||||
def extract_tmdb_id(self) -> Optional[str]:
|
||||
"""Return TMDB ID. Returns None as no TMDB ID is available."""
|
||||
return None
|
||||
|
||||
def extract_original_title(self):
|
||||
def extract_original_title(self) -> Optional[str]:
|
||||
"""Return original title. Returns None as no original title is available."""
|
||||
return None
|
||||
@@ -1,3 +1,11 @@
|
||||
"""Media metadata extraction coordinator.
|
||||
|
||||
This module provides the MediaExtractor class which coordinates multiple
|
||||
specialized extractors to gather comprehensive metadata about media files.
|
||||
It implements a priority-based extraction system where data is retrieved
|
||||
from the most appropriate source.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from .filename_extractor import FilenameExtractor
|
||||
from .metadata_extractor import MetadataExtractor
|
||||
@@ -8,7 +16,34 @@ from .default_extractor import DefaultExtractor
|
||||
|
||||
|
||||
class MediaExtractor:
|
||||
"""Class to extract various metadata from media files using specialized extractors"""
|
||||
"""Coordinator for extracting metadata from media files using multiple specialized extractors.
|
||||
|
||||
This class manages a collection of specialized extractors and provides a unified
|
||||
interface for retrieving metadata. It implements a priority-based system where
|
||||
each type of data is retrieved from the most appropriate source.
|
||||
|
||||
The extraction priority order varies by data type:
|
||||
- Title: TMDB → Metadata → Filename → Default
|
||||
- Year: Filename → Default
|
||||
- Technical info: MediaInfo → Default
|
||||
- File info: FileInfo → Default
|
||||
|
||||
Attributes:
|
||||
file_path: Path to the media file
|
||||
filename_extractor: Extracts metadata from filename patterns
|
||||
metadata_extractor: Extracts embedded metadata tags
|
||||
mediainfo_extractor: Extracts technical media information
|
||||
fileinfo_extractor: Extracts basic file system information
|
||||
tmdb_extractor: Fetches metadata from The Movie Database API
|
||||
default_extractor: Provides fallback default values
|
||||
|
||||
Example:
|
||||
>>> from pathlib import Path
|
||||
>>> extractor = MediaExtractor(Path("Movie (2020) [1080p].mkv"))
|
||||
>>> title = extractor.get("title")
|
||||
>>> year = extractor.get("year")
|
||||
>>> tracks = extractor.get("video_tracks")
|
||||
"""
|
||||
|
||||
def __init__(self, file_path: Path):
|
||||
self.file_path = file_path
|
||||
@@ -168,7 +203,24 @@ class MediaExtractor:
|
||||
}
|
||||
|
||||
def get(self, key: str, source: str | None = None):
|
||||
"""Get extracted data by key, optionally from specific source"""
|
||||
"""Get metadata value by key, optionally from a specific source.
|
||||
|
||||
Retrieves metadata using a priority-based system. If a source is specified,
|
||||
only that extractor is used. Otherwise, extractors are tried in priority
|
||||
order until a non-None value is found.
|
||||
|
||||
Args:
|
||||
key: The metadata key to retrieve (e.g., "title", "year", "resolution")
|
||||
source: Optional specific extractor to use ("TMDB", "MediaInfo", "Filename", etc.)
|
||||
|
||||
Returns:
|
||||
The extracted metadata value, or None if not found
|
||||
|
||||
Example:
|
||||
>>> extractor = MediaExtractor(Path("movie.mkv"))
|
||||
>>> title = extractor.get("title") # Try all sources in priority order
|
||||
>>> year = extractor.get("year", source="Filename") # Use only filename
|
||||
"""
|
||||
if source:
|
||||
# Specific source requested - find the extractor and call the method directly
|
||||
for extractor_name, extractor in self._extractors.items():
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
"""File system information extractor.
|
||||
|
||||
This module provides the FileInfoExtractor class for extracting basic
|
||||
file system metadata such as size, timestamps, paths, and extensions.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import logging
|
||||
import os
|
||||
@@ -5,45 +11,89 @@ from ..decorators import cached_method
|
||||
|
||||
# Set up logging conditionally
|
||||
if os.getenv('FORMATTER_LOG', '0') == '1':
|
||||
logging.basicConfig(filename='formatter.log', level=logging.INFO,
|
||||
logging.basicConfig(filename='formatter.log', level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
else:
|
||||
logging.basicConfig(level=logging.CRITICAL) # Disable logging
|
||||
|
||||
|
||||
class FileInfoExtractor:
|
||||
"""Class to extract file information"""
|
||||
"""Extractor for basic file system information.
|
||||
|
||||
This class extracts file system metadata including size, modification time,
|
||||
file name, path, and extension. All extraction methods are cached for
|
||||
performance.
|
||||
|
||||
Attributes:
|
||||
file_path: Path object pointing to the file
|
||||
_size: Cached file size in bytes
|
||||
_modification_time: Cached modification timestamp
|
||||
_file_name: Cached file name
|
||||
_file_path: Cached full file path as string
|
||||
_cache: Internal cache for method results
|
||||
|
||||
Example:
|
||||
>>> from pathlib import Path
|
||||
>>> extractor = FileInfoExtractor(Path("movie.mkv"))
|
||||
>>> size = extractor.extract_size() # Returns size in bytes
|
||||
>>> name = extractor.extract_file_name() # Returns "movie.mkv"
|
||||
"""
|
||||
|
||||
def __init__(self, file_path: Path):
|
||||
"""Initialize the FileInfoExtractor.
|
||||
|
||||
Args:
|
||||
file_path: Path object pointing to the file to extract info from
|
||||
"""
|
||||
self.file_path = file_path
|
||||
self._size = file_path.stat().st_size
|
||||
self._modification_time = file_path.stat().st_mtime
|
||||
self._file_name = file_path.name
|
||||
self._file_path = str(file_path)
|
||||
self._cache = {} # Internal cache for method results
|
||||
self._cache: dict[str, any] = {} # Internal cache for method results
|
||||
logging.info(f"FileInfoExtractor: file_name={self._file_name!r}, file_path={self._file_path!r}")
|
||||
|
||||
@cached_method()
|
||||
def extract_size(self) -> int:
|
||||
"""Extract file size in bytes"""
|
||||
"""Extract file size in bytes.
|
||||
|
||||
Returns:
|
||||
File size in bytes as an integer
|
||||
"""
|
||||
return self._size
|
||||
|
||||
@cached_method()
|
||||
def extract_modification_time(self) -> float:
|
||||
"""Extract file modification time"""
|
||||
"""Extract file modification time.
|
||||
|
||||
Returns:
|
||||
Unix timestamp (seconds since epoch) as a float
|
||||
"""
|
||||
return self._modification_time
|
||||
|
||||
@cached_method()
|
||||
def extract_file_name(self) -> str:
|
||||
"""Extract file name"""
|
||||
"""Extract file name (basename).
|
||||
|
||||
Returns:
|
||||
File name including extension (e.g., "movie.mkv")
|
||||
"""
|
||||
return self._file_name
|
||||
|
||||
@cached_method()
|
||||
def extract_file_path(self) -> str:
|
||||
"""Extract full file path as string"""
|
||||
"""Extract full file path as string.
|
||||
|
||||
Returns:
|
||||
Absolute file path as a string
|
||||
"""
|
||||
return self._file_path
|
||||
|
||||
@cached_method()
|
||||
def extract_extension(self) -> str:
|
||||
"""Extract file extension without the dot"""
|
||||
"""Extract file extension without the dot.
|
||||
|
||||
Returns:
|
||||
File extension in lowercase without leading dot (e.g., "mkv", "mp4")
|
||||
"""
|
||||
return self.file_path.suffix.lower().lstrip('.')
|
||||
@@ -9,6 +9,7 @@ from ..constants import (
|
||||
CYRILLIC_TO_ENGLISH
|
||||
)
|
||||
from ..decorators import cached_method
|
||||
from ..utils.pattern_utils import PatternExtractor
|
||||
import langcodes
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -25,6 +26,9 @@ class FilenameExtractor:
|
||||
self.file_path = file_path
|
||||
self.file_name = file_path.name
|
||||
|
||||
# Initialize utility helper
|
||||
self._pattern_extractor = PatternExtractor()
|
||||
|
||||
def _normalize_cyrillic(self, text: str) -> str:
|
||||
"""Normalize Cyrillic characters to English equivalents for parsing"""
|
||||
for cyr, eng in CYRILLIC_TO_ENGLISH.items():
|
||||
@@ -222,23 +226,10 @@ class FilenameExtractor:
|
||||
@cached_method()
|
||||
def extract_movie_db(self) -> list[str] | None:
|
||||
"""Extract movie database identifier from filename"""
|
||||
# Look for patterns at the end of filename in brackets or braces
|
||||
# Patterns: [tmdbid-123] {imdb-tt123} [imdbid-tt123] etc.
|
||||
|
||||
# Match patterns like [tmdbid-123456] or {imdb-tt1234567}
|
||||
pattern = r'[\[\{]([a-zA-Z]+(?:id)?)[-\s]*([a-zA-Z0-9]+)[\]\}]'
|
||||
matches = re.findall(pattern, self.file_name)
|
||||
|
||||
if matches:
|
||||
# Take the last match (closest to end of filename)
|
||||
db_type, db_id = matches[-1]
|
||||
|
||||
# Normalize database type
|
||||
db_type_lower = db_type.lower()
|
||||
for db_key, db_info in MOVIE_DB_DICT.items():
|
||||
if any(db_type_lower.startswith(pattern.rstrip('-')) for pattern in db_info['patterns']):
|
||||
return [db_key, db_id]
|
||||
|
||||
# Use PatternExtractor utility to avoid code duplication
|
||||
db_info = self._pattern_extractor.extract_movie_db_ids(self.file_name)
|
||||
if db_info:
|
||||
return [db_info['type'], db_info['id']]
|
||||
return None
|
||||
|
||||
@cached_method()
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
"""Embedded metadata extractor using Mutagen.
|
||||
|
||||
This module provides the MetadataExtractor class for reading embedded
|
||||
metadata tags from media files using the Mutagen library.
|
||||
"""
|
||||
|
||||
import mutagen
|
||||
import logging
|
||||
from pathlib import Path
|
||||
@@ -8,11 +14,32 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MetadataExtractor:
|
||||
"""Class to extract information from file metadata"""
|
||||
"""Extractor for embedded metadata tags from media files.
|
||||
|
||||
This class uses the Mutagen library to read embedded metadata tags
|
||||
such as title, artist, and duration. Falls back to MIME type detection
|
||||
when Mutagen cannot read the file.
|
||||
|
||||
Attributes:
|
||||
file_path: Path object pointing to the file
|
||||
info: Mutagen file info object, or None if file cannot be read
|
||||
_cache: Internal cache for method results
|
||||
|
||||
Example:
|
||||
>>> from pathlib import Path
|
||||
>>> extractor = MetadataExtractor(Path("movie.mkv"))
|
||||
>>> title = extractor.extract_title()
|
||||
>>> duration = extractor.extract_duration()
|
||||
"""
|
||||
|
||||
def __init__(self, file_path: Path):
|
||||
"""Initialize the MetadataExtractor.
|
||||
|
||||
Args:
|
||||
file_path: Path object pointing to the media file
|
||||
"""
|
||||
self.file_path = file_path
|
||||
self._cache = {} # Internal cache for method results
|
||||
self._cache: dict[str, any] = {} # Internal cache for method results
|
||||
try:
|
||||
self.info = mutagen.File(file_path) # type: ignore
|
||||
except Exception as e:
|
||||
@@ -21,34 +48,60 @@ class MetadataExtractor:
|
||||
|
||||
@cached_method()
|
||||
def extract_title(self) -> str | None:
|
||||
"""Extract title from metadata"""
|
||||
"""Extract title from embedded metadata tags.
|
||||
|
||||
Returns:
|
||||
Title string if found in metadata, None otherwise
|
||||
"""
|
||||
if self.info:
|
||||
return getattr(self.info, 'title', None) or getattr(self.info, 'get', lambda x, default=None: default)('title', [None])[0] # type: ignore
|
||||
return None
|
||||
|
||||
@cached_method()
|
||||
def extract_duration(self) -> float | None:
|
||||
"""Extract duration from metadata"""
|
||||
"""Extract duration from metadata.
|
||||
|
||||
Returns:
|
||||
Duration in seconds as a float, or None if not available
|
||||
"""
|
||||
if self.info:
|
||||
return getattr(self.info, 'length', None)
|
||||
return None
|
||||
|
||||
@cached_method()
|
||||
def extract_artist(self) -> str | None:
|
||||
"""Extract artist from metadata"""
|
||||
"""Extract artist from embedded metadata tags.
|
||||
|
||||
Returns:
|
||||
Artist string if found in metadata, None otherwise
|
||||
"""
|
||||
if self.info:
|
||||
return getattr(self.info, 'artist', None) or getattr(self.info, 'get', lambda x, default=None: default)('artist', [None])[0] # type: ignore
|
||||
return None
|
||||
|
||||
@cached_method()
|
||||
def extract_meta_type(self) -> str:
|
||||
"""Extract meta type from metadata"""
|
||||
"""Extract metadata container type.
|
||||
|
||||
Returns the Mutagen class name (e.g., "FLAC", "MP4") if available,
|
||||
otherwise falls back to MIME type detection.
|
||||
|
||||
Returns:
|
||||
Container type name, or "Unknown" if cannot be determined
|
||||
"""
|
||||
if self.info:
|
||||
return type(self.info).__name__
|
||||
return self._detect_by_mime()
|
||||
|
||||
def _detect_by_mime(self) -> str:
|
||||
"""Detect meta type by MIME"""
|
||||
"""Detect metadata type by MIME type.
|
||||
|
||||
Uses python-magic library to detect file MIME type and maps it
|
||||
to a metadata container type.
|
||||
|
||||
Returns:
|
||||
Container type name based on MIME type, or "Unknown" if detection fails
|
||||
"""
|
||||
try:
|
||||
import magic
|
||||
mime = magic.from_file(str(self.file_path), mime=True)
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
"""Formatter coordinator and application system.
|
||||
|
||||
This module provides the FormatterApplier class which coordinates the application
|
||||
of multiple formatters in the correct order (data → text → markup). It ensures
|
||||
formatters are applied sequentially based on their type.
|
||||
"""
|
||||
|
||||
from .text_formatter import TextFormatter
|
||||
from .duration_formatter import DurationFormatter
|
||||
from .size_formatter import SizeFormatter
|
||||
@@ -13,14 +20,32 @@ import os
|
||||
|
||||
# Set up logging conditionally
|
||||
if os.getenv('FORMATTER_LOG', '0') == '1':
|
||||
logging.basicConfig(filename='formatter.log', level=logging.INFO,
|
||||
logging.basicConfig(filename='formatter.log', level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
else:
|
||||
logging.basicConfig(level=logging.CRITICAL) # Disable logging
|
||||
|
||||
|
||||
class FormatterApplier:
|
||||
"""Class to apply multiple formatters in correct order"""
|
||||
"""Coordinator for applying multiple formatters in the correct order.
|
||||
|
||||
This class manages the application of formatters to data values, ensuring they
|
||||
are applied in the proper sequence:
|
||||
1. Data formatters (transform raw data: size, duration, etc.)
|
||||
2. Text formatters (transform text: uppercase, lowercase, etc.)
|
||||
3. Markup formatters (add visual styling: bold, colors, etc.)
|
||||
|
||||
The ordering prevents conflicts and ensures consistent output formatting.
|
||||
|
||||
Example:
|
||||
>>> from renamer.formatters.formatter import FormatterApplier
|
||||
>>> from renamer.formatters.size_formatter import SizeFormatter
|
||||
>>> from renamer.formatters.text_formatter import TextFormatter
|
||||
>>> value = 1073741824
|
||||
>>> formatters = [SizeFormatter.format_size, TextFormatter.bold]
|
||||
>>> result = FormatterApplier.apply_formatters(value, formatters)
|
||||
>>> # Result: bold("1.00 GB")
|
||||
"""
|
||||
|
||||
# Define the global order of all formatters
|
||||
FORMATTER_ORDER = [
|
||||
@@ -67,13 +92,29 @@ class FormatterApplier:
|
||||
|
||||
@staticmethod
|
||||
def apply_formatters(value, formatters):
|
||||
"""Apply multiple formatters to value in the global order"""
|
||||
"""Apply multiple formatters to a value in the correct global order.
|
||||
|
||||
Formatters are automatically sorted based on FORMATTER_ORDER to ensure
|
||||
proper sequencing (data → text → markup). If a formatter fails, the
|
||||
value is set to "Unknown" and processing continues.
|
||||
|
||||
Args:
|
||||
value: The value to format (can be any type)
|
||||
formatters: Single formatter or list of formatter functions
|
||||
|
||||
Returns:
|
||||
The formatted value after all formatters have been applied
|
||||
|
||||
Example:
|
||||
>>> formatters = [SizeFormatter.format_size, TextFormatter.bold]
|
||||
>>> result = FormatterApplier.apply_formatters(1024, formatters)
|
||||
"""
|
||||
if not isinstance(formatters, list):
|
||||
formatters = [formatters] if formatters else []
|
||||
|
||||
|
||||
# Sort formatters according to the global order
|
||||
ordered_formatters = sorted(formatters, key=lambda f: FormatterApplier.FORMATTER_ORDER.index(f) if f in FormatterApplier.FORMATTER_ORDER else len(FormatterApplier.FORMATTER_ORDER))
|
||||
|
||||
|
||||
# Apply in the ordered sequence
|
||||
for formatter in ordered_formatters:
|
||||
try:
|
||||
@@ -83,12 +124,36 @@ class FormatterApplier:
|
||||
except Exception as e:
|
||||
logging.error(f"Error applying {formatter.__name__ if hasattr(formatter, '__name__') else str(formatter)}: {e}")
|
||||
value = "Unknown"
|
||||
|
||||
|
||||
return value
|
||||
|
||||
|
||||
@staticmethod
|
||||
def format_data_item(item: dict) -> str | None:
|
||||
"""Apply all formatting to a data item and return the formatted string"""
|
||||
"""Apply all formatting to a data item and return the formatted string.
|
||||
|
||||
Processes a data item dictionary containing value, label, and formatters,
|
||||
applying them in the correct order to produce a formatted display string.
|
||||
|
||||
Args:
|
||||
item: Dictionary containing:
|
||||
- value: The raw value to format
|
||||
- label: Label text for the item
|
||||
- value_formatters: List of formatters to apply to the value
|
||||
- label_formatters: List of formatters to apply to the label
|
||||
- display_formatters: List of formatters for the final display
|
||||
|
||||
Returns:
|
||||
Formatted string combining label and value, or None if value is None
|
||||
|
||||
Example:
|
||||
>>> item = {
|
||||
... "value": 1024,
|
||||
... "label": "Size",
|
||||
... "value_formatters": [SizeFormatter.format_size],
|
||||
... "label_formatters": [TextFormatter.bold]
|
||||
... }
|
||||
>>> result = FormatterApplier.format_data_item(item)
|
||||
"""
|
||||
# Handle value formatting first (e.g., size formatting)
|
||||
value = item.get("value")
|
||||
if value is not None and value != "Not extracted":
|
||||
|
||||
Reference in New Issue
Block a user