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
class RegexParseTransform(logger.transforms.transform.Transform):
 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
RegexParseTransform( record_format: str = None, field_patterns: Union[List, Dict] = None, data_id: str = None, definition_path: str = None, metadata: Dict = None, metadata_interval: float = None, fields: Dict = None, delete_source_fields: bool = False, delete_unconverted_fields: bool = False, **kwargs)
 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.
parser
converter
def transform( self, record: str) -> Union[logger.utils.das_record.DASRecord, List[logger.utils.das_record.DASRecord], NoneType]:
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.

RegexTransform = <class 'RegexParseTransform'>