openrvdas.logger.transforms.regex_parse_transform
RegexParseTransform - a thin wrapper around RegexParser.
This transform parses text records into DASRecord objects using regular expressions. It delegates all parsing, device-aware processing, and metadata injection to the underlying RegexParser.
The architecture parallels ParseTransform, which wraps RecordParser.
1#!/usr/bin/env python3 2""" 3RegexParseTransform - a thin wrapper around RegexParser. 4 5This transform parses text records into DASRecord objects using regular 6expressions. It delegates all parsing, device-aware processing, and metadata 7injection to the underlying RegexParser. 8 9The architecture parallels ParseTransform, which wraps RecordParser. 10""" 11 12from typing import Union, Dict, List 13 14 15 16from logger.utils.das_record import DASRecord # noqa: E402 17from logger.transforms.transform import Transform # noqa: E402 18from logger.utils import regex_parser # noqa: E402 19 20# Optional: for generic field conversion via 'fields' parameter 21try: 22 from logger.transforms.convert_fields_transform import ConvertFieldsTransform 23except ImportError: 24 ConvertFieldsTransform = None 25 26 27class RegexParseTransform(Transform): 28 r""" 29 Parses a string record into a DASRecord using regular expressions, 30 with optional field type conversion. 31 32 This is a thin wrapper around RegexParser. All parsing logic, device-aware 33 field processing, and metadata injection are handled by the parser. 34 35 **Example Configuration:** 36 37 .. code-block:: yaml 38 39 - class: RegexParseTransform 40 module: logger.transforms.regex_parse_transform 41 kwargs: 42 data_id: gnsspo112593 # Overrides or fills in data_id 43 field_patterns: 44 GPZDA: '^\WGPZDA,(?P<utc_time>\d+\.\d+),...' 45 GPGGA: '^\WGPGGA,(?P<utc_position_fix>\d+\.\d+),...' 46 47 Or using device definitions: 48 49 .. code-block:: yaml 50 51 - class: RegexParseTransform 52 module: logger.transforms.regex_parse_transform 53 kwargs: 54 definition_path: 'local/devices/*.yaml' 55 metadata_interval: 60 56 """ 57 58 def __init__(self, 59 # --- Parsing Arguments (passed to RegexParser) --- 60 record_format: str = None, 61 field_patterns: Union[List, Dict] = None, 62 data_id: str = None, 63 definition_path: str = None, 64 metadata: Dict = None, 65 metadata_interval: float = None, 66 # --- Additional Transform Arguments --- 67 fields: Dict = None, 68 delete_source_fields: bool = False, 69 delete_unconverted_fields: bool = False, 70 **kwargs): 71 """ 72 Args: 73 record_format (str): A regex string to match the record envelope 74 (timestamp, data_id). Defaults to regex_parser.DEFAULT_RECORD_FORMAT. 75 76 field_patterns (list/dict): 77 - A list of regex patterns to match the field body. 78 - A dict of {message_type: pattern}. 79 If None, patterns are loaded from definition_path. 80 81 data_id (str): If specified, this string is used as the data_id 82 for all records, overriding any data_id extracted from the 83 source record. 84 85 definition_path (str): Wildcarded path matching YAML definitions 86 for devices. Used only if 'field_patterns' is None. 87 Defaults to regex_parser.DEFAULT_DEFINITION_PATH. 88 89 metadata (dict): If field_patterns is not None, the metadata to 90 send along with data records. 91 92 metadata_interval (float): If not None, include the description, 93 units and other metadata pertaining to each field in the 94 returned record if those data haven't been returned in the 95 last metadata_interval seconds. 96 97 fields (dict): Mapping of field names to target types 98 (e.g., {'temp': 'float'}). If provided, ConvertFieldsTransform 99 is applied after parsing. 100 101 delete_source_fields (bool): Remove original fields after conversion. 102 103 delete_unconverted_fields (bool): Remove fields that were not converted. 104 """ 105 super().__init__(**kwargs) # processes 'quiet' and type hints 106 107 # Create the parser with all configuration 108 self.parser = regex_parser.RegexParser( 109 record_format=record_format, 110 field_patterns=field_patterns, 111 data_id=data_id, 112 definition_path=definition_path, 113 metadata=metadata, 114 metadata_interval=metadata_interval, 115 quiet=self.quiet 116 ) 117 118 # Optional generic field converter (for 'fields' parameter) 119 self.converter = None 120 if fields and ConvertFieldsTransform: 121 self.converter = ConvertFieldsTransform( 122 fields=fields, 123 delete_source_fields=delete_source_fields, 124 delete_unconverted_fields=delete_unconverted_fields, 125 quiet=self.quiet 126 ) 127 128 ############################ 129 def transform(self, record: str) -> Union[DASRecord, List[DASRecord], None]: 130 """Parse record and return DASRecord.""" 131 # See if it's something we can process, and if not, try digesting 132 if not self.can_process_record(record): # inherited from BaseModule 133 return self.digest_record(record) # inherited from BaseModule 134 135 # Delegate to parser 136 parsed_record = self.parser.parse_record(record) 137 138 if not parsed_record: 139 return None 140 141 # Apply optional generic converter 142 if self.converter: 143 parsed_record = self.converter.transform(parsed_record) 144 145 return parsed_record 146 147 148# Alias for backward compatibility 149RegexTransform = RegexParseTransform
28class RegexParseTransform(Transform): 29 r""" 30 Parses a string record into a DASRecord using regular expressions, 31 with optional field type conversion. 32 33 This is a thin wrapper around RegexParser. All parsing logic, device-aware 34 field processing, and metadata injection are handled by the parser. 35 36 **Example Configuration:** 37 38 .. code-block:: yaml 39 40 - class: RegexParseTransform 41 module: logger.transforms.regex_parse_transform 42 kwargs: 43 data_id: gnsspo112593 # Overrides or fills in data_id 44 field_patterns: 45 GPZDA: '^\WGPZDA,(?P<utc_time>\d+\.\d+),...' 46 GPGGA: '^\WGPGGA,(?P<utc_position_fix>\d+\.\d+),...' 47 48 Or using device definitions: 49 50 .. code-block:: yaml 51 52 - class: RegexParseTransform 53 module: logger.transforms.regex_parse_transform 54 kwargs: 55 definition_path: 'local/devices/*.yaml' 56 metadata_interval: 60 57 """ 58 59 def __init__(self, 60 # --- Parsing Arguments (passed to RegexParser) --- 61 record_format: str = None, 62 field_patterns: Union[List, Dict] = None, 63 data_id: str = None, 64 definition_path: str = None, 65 metadata: Dict = None, 66 metadata_interval: float = None, 67 # --- Additional Transform Arguments --- 68 fields: Dict = None, 69 delete_source_fields: bool = False, 70 delete_unconverted_fields: bool = False, 71 **kwargs): 72 """ 73 Args: 74 record_format (str): A regex string to match the record envelope 75 (timestamp, data_id). Defaults to regex_parser.DEFAULT_RECORD_FORMAT. 76 77 field_patterns (list/dict): 78 - A list of regex patterns to match the field body. 79 - A dict of {message_type: pattern}. 80 If None, patterns are loaded from definition_path. 81 82 data_id (str): If specified, this string is used as the data_id 83 for all records, overriding any data_id extracted from the 84 source record. 85 86 definition_path (str): Wildcarded path matching YAML definitions 87 for devices. Used only if 'field_patterns' is None. 88 Defaults to regex_parser.DEFAULT_DEFINITION_PATH. 89 90 metadata (dict): If field_patterns is not None, the metadata to 91 send along with data records. 92 93 metadata_interval (float): If not None, include the description, 94 units and other metadata pertaining to each field in the 95 returned record if those data haven't been returned in the 96 last metadata_interval seconds. 97 98 fields (dict): Mapping of field names to target types 99 (e.g., {'temp': 'float'}). If provided, ConvertFieldsTransform 100 is applied after parsing. 101 102 delete_source_fields (bool): Remove original fields after conversion. 103 104 delete_unconverted_fields (bool): Remove fields that were not converted. 105 """ 106 super().__init__(**kwargs) # processes 'quiet' and type hints 107 108 # Create the parser with all configuration 109 self.parser = regex_parser.RegexParser( 110 record_format=record_format, 111 field_patterns=field_patterns, 112 data_id=data_id, 113 definition_path=definition_path, 114 metadata=metadata, 115 metadata_interval=metadata_interval, 116 quiet=self.quiet 117 ) 118 119 # Optional generic field converter (for 'fields' parameter) 120 self.converter = None 121 if fields and ConvertFieldsTransform: 122 self.converter = ConvertFieldsTransform( 123 fields=fields, 124 delete_source_fields=delete_source_fields, 125 delete_unconverted_fields=delete_unconverted_fields, 126 quiet=self.quiet 127 ) 128 129 ############################ 130 def transform(self, record: str) -> Union[DASRecord, List[DASRecord], None]: 131 """Parse record and return DASRecord.""" 132 # See if it's something we can process, and if not, try digesting 133 if not self.can_process_record(record): # inherited from BaseModule 134 return self.digest_record(record) # inherited from BaseModule 135 136 # Delegate to parser 137 parsed_record = self.parser.parse_record(record) 138 139 if not parsed_record: 140 return None 141 142 # Apply optional generic converter 143 if self.converter: 144 parsed_record = self.converter.transform(parsed_record) 145 146 return parsed_record
Parses a string record into a DASRecord using regular expressions, with optional field type conversion.
This is a thin wrapper around RegexParser. All parsing logic, device-aware field processing, and metadata injection are handled by the parser.
Example Configuration:
- class: RegexParseTransform
module: logger.transforms.regex_parse_transform
kwargs:
data_id: gnsspo112593 # Overrides or fills in data_id
field_patterns:
GPZDA: '^\WGPZDA,(?P<utc_time>\d+\.\d+),...'
GPGGA: '^\WGPGGA,(?P<utc_position_fix>\d+\.\d+),...'
Or using device definitions:
- class: RegexParseTransform
module: logger.transforms.regex_parse_transform
kwargs:
definition_path: 'local/devices/*.yaml'
metadata_interval: 60
59 def __init__(self, 60 # --- Parsing Arguments (passed to RegexParser) --- 61 record_format: str = None, 62 field_patterns: Union[List, Dict] = None, 63 data_id: str = None, 64 definition_path: str = None, 65 metadata: Dict = None, 66 metadata_interval: float = None, 67 # --- Additional Transform Arguments --- 68 fields: Dict = None, 69 delete_source_fields: bool = False, 70 delete_unconverted_fields: bool = False, 71 **kwargs): 72 """ 73 Args: 74 record_format (str): A regex string to match the record envelope 75 (timestamp, data_id). Defaults to regex_parser.DEFAULT_RECORD_FORMAT. 76 77 field_patterns (list/dict): 78 - A list of regex patterns to match the field body. 79 - A dict of {message_type: pattern}. 80 If None, patterns are loaded from definition_path. 81 82 data_id (str): If specified, this string is used as the data_id 83 for all records, overriding any data_id extracted from the 84 source record. 85 86 definition_path (str): Wildcarded path matching YAML definitions 87 for devices. Used only if 'field_patterns' is None. 88 Defaults to regex_parser.DEFAULT_DEFINITION_PATH. 89 90 metadata (dict): If field_patterns is not None, the metadata to 91 send along with data records. 92 93 metadata_interval (float): If not None, include the description, 94 units and other metadata pertaining to each field in the 95 returned record if those data haven't been returned in the 96 last metadata_interval seconds. 97 98 fields (dict): Mapping of field names to target types 99 (e.g., {'temp': 'float'}). If provided, ConvertFieldsTransform 100 is applied after parsing. 101 102 delete_source_fields (bool): Remove original fields after conversion. 103 104 delete_unconverted_fields (bool): Remove fields that were not converted. 105 """ 106 super().__init__(**kwargs) # processes 'quiet' and type hints 107 108 # Create the parser with all configuration 109 self.parser = regex_parser.RegexParser( 110 record_format=record_format, 111 field_patterns=field_patterns, 112 data_id=data_id, 113 definition_path=definition_path, 114 metadata=metadata, 115 metadata_interval=metadata_interval, 116 quiet=self.quiet 117 ) 118 119 # Optional generic field converter (for 'fields' parameter) 120 self.converter = None 121 if fields and ConvertFieldsTransform: 122 self.converter = ConvertFieldsTransform( 123 fields=fields, 124 delete_source_fields=delete_source_fields, 125 delete_unconverted_fields=delete_unconverted_fields, 126 quiet=self.quiet 127 )
Args: record_format (str): A regex string to match the record envelope (timestamp, data_id). Defaults to regex_parser.DEFAULT_RECORD_FORMAT.
field_patterns (list/dict):
- A list of regex patterns to match the field body.
- A dict of {message_type: pattern}.
If None, patterns are loaded from definition_path.
data_id (str): If specified, this string is used as the data_id
for all records, overriding any data_id extracted from the
source record.
definition_path (str): Wildcarded path matching YAML definitions
for devices. Used only if 'field_patterns' is None.
Defaults to regex_parser.DEFAULT_DEFINITION_PATH.
metadata (dict): If field_patterns is not None, the metadata to
send along with data records.
metadata_interval (float): If not None, include the description,
units and other metadata pertaining to each field in the
returned record if those data haven't been returned in the
last metadata_interval seconds.
fields (dict): Mapping of field names to target types
(e.g., {'temp': 'float'}). If provided, ConvertFieldsTransform
is applied after parsing.
delete_source_fields (bool): Remove original fields after conversion.
delete_unconverted_fields (bool): Remove fields that were not converted.
130 def transform(self, record: str) -> Union[DASRecord, List[DASRecord], None]: 131 """Parse record and return DASRecord.""" 132 # See if it's something we can process, and if not, try digesting 133 if not self.can_process_record(record): # inherited from BaseModule 134 return self.digest_record(record) # inherited from BaseModule 135 136 # Delegate to parser 137 parsed_record = self.parser.parse_record(record) 138 139 if not parsed_record: 140 return None 141 142 # Apply optional generic converter 143 if self.converter: 144 parsed_record = self.converter.transform(parsed_record) 145 146 return parsed_record
Parse record and return DASRecord.