Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make wfdb.rdann, wfdb.rdrecord, wfdb.rdsamp also accept pathlib.Path objects as the input record_name #346

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions wfdb/io/annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import pandas as pd
import re
import posixpath
import pathlib
import pdb
import struct
import sys
Expand Down Expand Up @@ -146,7 +147,7 @@ def __init__(self, record_name, extension, sample, symbol=None,
label_store=None, description=None, custom_labels=None,
contained_labels=None):

self.record_name = record_name
self.record_name = str(record_name)
self.extension = extension

self.sample = sample
Expand Down Expand Up @@ -1584,7 +1585,7 @@ def rdann(record_name, extension, sampfrom=0, sampto=None, shift_samps=False,

Parameters
----------
record_name : str
record_name : str or pathlib.Path
The record name of the WFDB annotation file. ie. for file '100.atr',
record_name='100'.
extension : str
Expand Down Expand Up @@ -1678,7 +1679,7 @@ def rdann(record_name, extension, sampfrom=0, sampto=None, shift_samps=False,
pass

# Create the annotation object
annotation = Annotation(record_name=os.path.split(record_name)[1],
annotation = Annotation(record_name=pathlib.Path(record_name).name,
extension=extension, sample=sample,
label_store=label_store, subtype=subtype,
chan=chan, num=num, aux_note=aux_note, fs=fs,
Expand Down Expand Up @@ -1747,7 +1748,7 @@ def load_byte_pairs(record_name, extension, pn_dir):

Parameters
----------
record_name : str
record_name : str or pathlib.Path
The record name of the WFDB annotation file. ie. for file '100.atr',
record_name='100'.
extension : str
Expand All @@ -1764,13 +1765,14 @@ def load_byte_pairs(record_name, extension, pn_dir):
The input filestream converted to an Nx2 array of unsigned bytes.

"""
file_name = pathlib.Path(record_name).with_suffix('.' + extension)
# local file
if pn_dir is None:
with open(record_name + '.' + extension, 'rb') as f:
with open(file_name, 'rb') as f:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

paths offer a .open function, too, which might be more readable than using the global one.

filebytes = np.fromfile(f, '<u1').reshape([-1, 2])
# PhysioNet file
else:
filebytes = download._stream_annotation(record_name+'.'+extension, pn_dir).reshape([-1, 2])
filebytes = download._stream_annotation(file_name, pn_dir).reshape([-1, 2])

return filebytes

Expand Down Expand Up @@ -2697,7 +2699,7 @@ def rdedfann(record_name, pn_dir=None, delete_file=True, info_only=True,
})
df_out = _format_ann_from_df(df_in)
# Remove extension from input file name
record_name = record_name.split(os.sep)[-1].split('.')[0]
record_name = pathlib.Path(record_name).stem
extension = 'atr'
fs = rec.fs
sample = (df_out['onset'].to_numpy()*fs).astype(np.int64)
Expand Down
75 changes: 61 additions & 14 deletions wfdb/io/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import re
import os
import posixpath
import pathlib
import pdb
import json

Expand Down Expand Up @@ -48,6 +49,47 @@ def set_db_index_url(db_index_url=PN_INDEX_URL):
config.db_index_url = db_index_url


def _get_url(*dir_or_file):
"""
Get the remote file url or directory url.

Parameters
----------
dir_or_file : a sequence of str or pathlib.Path,
Including the PhysioNet directory and subdirectories,
and the base file name.

Returns
-------
url : str
The full url of the file or directory.

Examples
--------
>>> url = _get_url('mitdb/1.0.0', '100.dat')
>>> url
'https://physionet.org/content/mitdb/100.dat'
>>> url = _get_url('mitdb/1.0.0/', 'x_mitdb/x_111.dat')
>>> url
'https://physionet.org/files/mitdb/1.0.0/x_mitdb/x_111.dat'
>>> url = _get_url('mitdb', '1.0.0', 'x_mitdb', 'x_111.dat')
>>> url
'https://physionet.org/files/mitdb/1.0.0/x_mitdb/x_111.dat'
>>> import pathlib
>>> pp = pathlib.PureWindowsPath('x_mitdb\\x_111.dat')
>>> url = _get_url('mitdb/1.0.0', pp)
'https://physionet.org/files/mitdb/1.0.0/x_mitdb/x_111.dat'
>>> sp = 'x_mitdb\\x_111.dat' # on a Windows machine
>>> url = _get_url('mitdb/1.0.0', sp)
'https://physionet.org/files/mitdb/1.0.0/x_mitdb/x_111.dat'

"""
url = config.db_index_url
for item in dir_or_file:
url = posixpath.join(url, pathlib.PurePosixPath(pathlib.Path(item)))
return url


def _remote_file_size(url=None, file_name=None, pn_dir=None):
"""
Get the remote file size in bytes.
Expand All @@ -57,11 +99,12 @@ def _remote_file_size(url=None, file_name=None, pn_dir=None):
url : str, optional
The full url of the file. Use this option to explicitly
state the full url.
file_name : str, optional
file_name : str or pathlib.Path, optional
The base file name. Use this argument along with pn_dir if you
want the full url to be constructed.
pn_dir : str, optional
The base file name. Use this argument along with file_name if
The PhysioNet directory where the file is located.
Use this argument along with file_name if
you want the full url to be constructed.

Returns
Expand All @@ -72,7 +115,7 @@ def _remote_file_size(url=None, file_name=None, pn_dir=None):
"""
# Option to construct the url
if file_name and pn_dir:
url = posixpath.join(config.db_index_url, pn_dir, file_name)
url = _get_url(pn_dir, file_name)

with _url.openurl(url, 'rb') as f:
remote_file_size = f.seek(0, os.SEEK_END)
Expand All @@ -86,7 +129,7 @@ def _stream_header(file_name, pn_dir):

Parameters
----------
file_name : str
file_name : str or pathlib.Path
The name of the headerr file to be read.
pn_dir : str
The PhysioNet database directory from which to find the
Expand All @@ -102,7 +145,7 @@ def _stream_header(file_name, pn_dir):

"""
# Full url of header location
url = posixpath.join(config.db_index_url, pn_dir, file_name)
url = _get_url(pn_dir, file_name)

# Get the content of the remote file
with _url.openurl(url, 'rb') as f:
Expand Down Expand Up @@ -140,7 +183,7 @@ def _stream_dat(file_name, pn_dir, byte_count, start_byte, dtype):

Parameters
----------
file_name : str
file_name : str or pathlib.Path
The name of the dat file to be read.
pn_dir : str
The PhysioNet directory where the dat file is located.
Expand All @@ -158,7 +201,7 @@ def _stream_dat(file_name, pn_dir, byte_count, start_byte, dtype):

"""
# Full url of dat file
url = posixpath.join(config.db_index_url, pn_dir, file_name)
url = _get_url(pn_dir, file_name)

# Get the content
with _url.openurl(url, 'rb', buffering=0) as f:
Expand All @@ -177,7 +220,7 @@ def _stream_annotation(file_name, pn_dir):

Parameters
----------
file_name : str
file_name : str or pathlib.Path
The name of the annotation file to be read.
pn_dir : str
The PhysioNet directory where the annotation file is located.
Expand All @@ -189,7 +232,7 @@ def _stream_annotation(file_name, pn_dir):

"""
# Full url of annotation file
url = posixpath.join(config.db_index_url, pn_dir, file_name)
url = _get_url(pn_dir, file_name)

# Get the content
with _url.openurl(url, 'rb') as f:
Expand Down Expand Up @@ -262,10 +305,14 @@ def get_record_list(db_dir, records='all'):

"""
# Full url PhysioNet database
if '/' not in db_dir:
db_url = posixpath.join(config.db_index_url, db_dir, record.get_version(db_dir))
# if '/' not in db_dir:
# db_url = posixpath.join(config.db_index_url, db_dir, record.get_version(db_dir))
# else:
# db_url = posixpath.join(config.db_index_url, db_dir)
if not re.search(record.DB_VERSION_PATTERN, db_dir):
db_url = _get_url(db_dir, record.get_version(db_dir))
else:
db_url = posixpath.join(config.db_index_url, db_dir)
db_url = _get_url(db_dir)

# Check for a RECORDS file
if records == 'all':
Expand Down Expand Up @@ -308,7 +355,7 @@ def get_annotators(db_dir, annotators):

"""
# Full url PhysioNet database
db_url = posixpath.join(config.db_index_url, db_dir)
db_url = _get_url(db_dir)

if annotators is not None:
# Check for an ANNOTATORS file
Expand Down Expand Up @@ -396,7 +443,7 @@ def dl_pn_file(inputs):
basefile, subdir, db, dl_dir, keep_subdirs, overwrite = inputs

# Full url of file
url = posixpath.join(config.db_index_url, db, subdir, basefile)
url = _get_url(db, subdir, basefile)

# Figure out where the file should be locally
if keep_subdirs:
Expand Down
Loading