Source code for planetary_coverage.events.evf

"""EVF events file module."""

import re

from .event import AbstractEventsCollection, AbstractEventsFile
from ..html import table
from ..spice.datetime import datetime, np_datetime_str, sorted_datetimes


EVF_FMT = re.compile(r'([\w_-]+)(?:\s*\(COUNT\s*=\s*(\d*)\))?')


def evf_key(key) -> tuple:
    """Parse EVF key and count number."""
    if match := EVF_FMT.findall(key):
        return match[0][0].upper(), int(match[0][1]) if match[0][1] else None

    raise KeyError(f'Invalid EVF key: {key}')


def evf_rows(content):
    """Read EVF content to extract comments and data."""
    comments = []
    data = {}
    for line in content.splitlines():
        if line.startswith('#'):
            comments.append(line[1:].strip())
            continue

        mapps_time, key = line.split(maxsplit=1)

        # Parse time as an explicit ISO string
        time = np_datetime_str(datetime(mapps_time))

        # Parse EVF key
        keyword, count = evf_key(key)

        if keyword not in data:
            data[keyword] = [time]
        elif len(data[keyword]) + 1 == count:
            data[keyword].append(time)
        else:
            raise KeyError(f'`{keyword}` missing (COUNT = {count - 1})')

    return comments, data


[docs]class EvfEventsFile(AbstractEventsFile): """EVF event file object. EVF are in MAPPS format. Parameters ---------- fname: str or pathlib.Path EVF filename to parse. """ def __init__(self, fname): super().__init__(fname, 'name') # primary_key='name' def _repr_html_(self): rows = [ [ event.key, len(event) if isinstance(event, AbstractEventsCollection) else '-', event.start_date, event.stop_date, ] for event in self ] return table(rows, header=('event', '#', 't_start', 't_stop')) def __contains__(self, key): if isinstance(key, str): keyword, _ = evf_key(key) if keyword in self.data.keys(): return True return super().__contains__(key) def __getitem__(self, key): keyword, count = evf_key(key) if keyword not in self.data.keys(): raise KeyError(key) events = self.data[keyword] if count is None: return events if 0 < count <= len(events): return events[count - 1] raise IndexError(count) def _read_rows(self): """Read EVF rows content.""" content = self.fname.read_text(encoding='utf-8') # EVF columns self.fields = ['event time [utc]', 'name'] # Extract comments and rows content (and check COUNT values) self.comments, data = evf_rows(content) # Flatten the rows self.rows = sorted_datetimes([ (time, key) for key, times in data.items() for time in times ], index=0)