Source code for pyrfu.mms.list_files
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Built-in imports
import datetime
import json
import os
import re
from typing import Mapping, Optional, Union
# 3rd party imports
import numpy as np
from pyrfu.mms.db_init import MMS_CFG_PATH
# Local imports
from pyrfu.mms.list_files_aws import _file_time
from pyrfu.pyrf.datetime642iso8601 import datetime642iso8601
from pyrfu.pyrf.iso86012datetime64 import iso86012datetime64
__author__ = "Louis Richard"
__email__ = "louis.richard@physics.ox.ac.uk"
__copyright__ = "Copyright 2020-2024"
__license__ = "MIT"
__version__ = "2.4.13"
__status__ = "Prototype"
[docs]def list_files(
tint: list[str],
mms_id: Union[str, int],
var: Mapping[str, str],
data_path: Optional[str] = "",
) -> list[str]:
r"""Find available files in the data directories for `var`.
Parameters
----------
tint : list
Time interval
mms_id : str or int
Index of the spacecraft
var : dict
Dictionary containing at least 4 keys
* var["inst"] : name of the instrument
* var["tmmode"] : data rate
* var["lev"] : data level
* var["dtype"] : data type
data_path : str, Optional
Path of MMS data. Default uses `pyrfu.mms.mms_config.py`
Returns
-------
file_names : list
List of files corresponding to the parameters in the selected time
interval
"""
# Check path
if not data_path:
# Read the current version of the MMS configuration file
with open(MMS_CFG_PATH, "r", encoding="utf-8") as fs:
config = json.load(fs)
root_path = os.path.normpath(config["local"])
else:
root_path = os.path.normpath(data_path)
# Make sure that the data path exists
assert os.path.exists(root_path), f"{root_path} doesn't exist!!"
# Check time interval
if isinstance(tint, list):
tint_array = np.array(tint)
else:
raise TypeError("tint must be a list!!")
# Convert time interval to ISO 8601
if isinstance(tint_array[0], str):
tint_iso8601 = datetime642iso8601(iso86012datetime64(tint_array))
else:
raise TypeError("Values must be in str!!")
if not isinstance(mms_id, str):
mms_id = str(mms_id)
t_start, t_end = [_file_time(re.sub(r"\D", "", t)[:14]) for t in tint_iso8601]
# directory and file name search patterns:
# - assume directories are of the form:
# (srvy, SITL): spacecraft/instrument/rate/level[/datatype]/year/month/
# (brst): spacecraft/instrument/rate/level[/datatype]/year/month/day/
# - assume file names are of the form:
# spacecraft_instrument_rate_level[_datatype]_YYYYMMDD[hhmmss]_version.cdf
file_regex = re.compile(
rf"^mms{mms_id}_{var['inst']}_{var['tmmode']}_{var['lev']}"
+ r"(?:_.*)?_([0-9]{8,14})_v(\d+)\.(\d+)\.(\d+)\.cdf$"
)
if var["dtype"] == "" or var["dtype"] is None:
level_and_dtype = [var["lev"]]
else:
level_and_dtype = [var["lev"], var["dtype"]]
# Latest version of each file, by time tag
files = {}
day = datetime.datetime.combine(t_start.date(), datetime.time())
while day < t_end:
local_dir = os.path.join(
root_path,
f"mms{mms_id}",
var["inst"],
var["tmmode"],
*level_and_dtype,
day.strftime("%Y"),
day.strftime("%m"),
)
if var["tmmode"] == "brst":
local_dir = os.path.join(local_dir, day.strftime("%d"))
for root, _, file_names in os.walk(local_dir):
for file_name in file_names:
matches = file_regex.match(file_name)
if not matches:
continue
time_tag, *version = matches.groups()
version = tuple(map(int, version))
if time_tag not in files or version > files[time_tag][0]:
files[time_tag] = (version, os.path.join(root, file_name))
day += datetime.timedelta(days=1)
# Files starting within the time interval, and the last one starting before
# (or at) its start, which covers it (as list_files_aws)
times = sorted(files, key=_file_time)
starts = [_file_time(time_tag) for time_tag in times]
i_first = max([i for i, start in enumerate(starts) if start <= t_start] or [0])
file_paths = [
files[time_tag][1]
for time_tag, start in zip(times[i_first:], starts[i_first:])
if start < t_end
]
return file_paths