Repository: spacetelescope/jwql

Issue:
Build filename parser utility function
It would be useful to have a function in the `utils.py` module that returned the individual elements of a given filename, for example:

```python
from jwql.utils.utils import parse_filename
filename_dict = parse_filename('jw94015001001_02102_00001_nrcb1_uncal.fits')
```

where `filename_dict` is:

```
{
    'program_id' : '94015',
    'observation' : '001',
    'visit' : '001',
    'visit_group' : '02',
    'parallel_seq_id' : '1',
    'activity' : '02',
    'exposure_id' : '00001',
    'detector' : 'nrcb1',
    'suffix' : 'uncal'
}
```


Discussion:
@SaraOgaz @hover2pi @gkanarek @laurenmarietta @bhilbert4 @cracraft @Johannes-Sahlmann Do you know if anything like this already exists somewhere? 
I have written code for the web app that does this, so it would be pretty easy for me to write up a quick function to put in utils!
@laurenmarietta If it seems that this functionality doesn't already exist somewhere in JWST software land, please feel free to do this!
@jhunkeler has pointed out that use of regular expressions is a possible solution for this:  https://gist.github.com/jhunkeler/f08783ca2da7bfd1f8e9ee1d207da5ff
That module is much more efficient than what I've been doing, wow! We should definitely use that. I'll put it into `utils.py` and make a pull request for it.

Resolving patch:
diff --git a/jwql/utils/utils.py b/jwql/utils/utils.py
--- a/jwql/utils/utils.py
+++ b/jwql/utils/utils.py
@@ -3,7 +3,8 @@
 Authors
 -------
 
-    Matthew Bourque
+    - Matthew Bourque
+    - Lauren Chambers
 
 Use
 ---
@@ -12,9 +13,19 @@
 
     >>> import utils
     settings = get_config()
+
+References
+----------
+
+    Filename parser modifed from Joe Hunkeler:
+    https://gist.github.com/jhunkeler/f08783ca2da7bfd1f8e9ee1d207da5ff
 """
 
 import json
+import os
+import re
+
+__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
 
 
 def get_config():
@@ -27,7 +38,50 @@ def get_config():
         A dictionary that holds the contents of the config file.
     """
 
-    with open('config.json', 'r') as config_file:
+    with open(os.path.join(__location__, 'config.json'), 'r') as config_file:
         settings = json.load(config_file)
 
     return settings
+
+
+def filename_parser(filename):
+    """Return a dictionary that contains the properties of a given
+    JWST file (e.g. program ID, visit number, detector, etc.)
+
+    Parameters
+    ----------
+    filename : str
+        Path or name of JWST file to parse
+
+    Returns
+    -------
+    filename_dict : dict
+        Collection of file properties
+
+    Raises
+    ------
+    ValueError
+        When the provided file does not follow naming conventions
+    """
+    filename = os.path.basename(filename)
+
+    elements = \
+        re.compile(r"[a-z]+"
+                   "(?P<program_id>\d{5})"
+                   "(?P<observation>\d{3})"
+                   "(?P<visit>\d{3})"
+                   "_(?P<visit_group>\d{2})"
+                   "(?P<parallel_seq_id>\d{1})"
+                   "(?P<activity>\d{2})"
+                   "_(?P<exposure_id>\d+)"
+                   "_(?P<detector>\w+)"
+                   "_(?P<suffix>\w+).*")
+
+    jwst_file = elements.match(filename)
+
+    if jwst_file is not None:
+        filename_dict = jwst_file.groupdict()
+    else:
+        raise ValueError('Provided file {} does not follow JWST naming conventions (jw<PPPPP><OOO><VVV>_<GGSAA>_<EEEEE>_<detector>_<suffix>.fits)'.format(filename))
+
+    return filename_dict


Test patch:
diff --git a/jwql/tests/test_utils.py b/jwql/tests/test_utils.py
new file mode 100644
--- /dev/null
+++ b/jwql/tests/test_utils.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+"""Tests for the ``utils`` module.
+
+Authors
+-------
+
+    - Lauren Chambers
+
+Use
+---
+
+    These tests can be run via the command line (omit the -s to
+    suppress verbose output to stdout):
+
+    ::
+
+        pytest -s test_utils.py
+"""
+
+import pytest
+
+from jwql.utils.utils import get_config, filename_parser
+
+
+def test_get_config():
+    '''Assert that the ``get_config`` function successfuly creates a
+    dictionary.
+    '''
+    settings = get_config()
+    assert isinstance(settings, dict)
+
+
+def test_filename_parser_filename():
+    '''Generate a dictionary with parameters from a JWST filename.
+    Assert that the dictionary matches what is expected.
+    '''
+    filename = 'jw00327001001_02101_00002_nrca1_rate.fits'
+    filename_dict = filename_parser(filename)
+
+    correct_dict = {'activity': '01',
+                    'detector': 'nrca1',
+                    'exposure_id': '00002',
+                    'observation': '001',
+                    'parallel_seq_id': '1',
+                    'program_id': '00327',
+                    'suffix': 'rate',
+                    'visit': '001',
+                    'visit_group': '02'}
+
+    assert filename_dict == correct_dict
+
+
+def test_filename_parser_filepath():
+    '''Generate a dictionary with parameters from a JWST filepath
+    (not just the basename). Assert that the dictionary matches what
+    is expected.
+    '''
+    filepath = '/test/dir/to/the/file/jw90002/jw90002001001_02102_00001_nis_rateints.fits'
+    filename_dict = filename_parser(filepath)
+
+    correct_dict = {'activity': '02',
+                    'detector': 'nis',
+                    'exposure_id': '00001',
+                    'observation': '001',
+                    'parallel_seq_id': '1',
+                    'program_id': '90002',
+                    'suffix': 'rateints',
+                    'visit': '001',
+                    'visit_group': '02'}
+
+    assert filename_dict == correct_dict
+
+
+def test_filename_parser_nonJWST():
+    '''Attempt to generate a file parameter dictionary from a file
+    that is not formatted in the JWST naming convention. Ensure the
+    appropriate error is raised.
+    '''
+    with pytest.raises(ValueError,
+                       match=r'Provided file .+ does not follow JWST naming conventions \(jw<PPPPP><OOO><VVV>_<GGSAA>_<EEEEE>_<detector>_<suffix>\.fits\)'):
+        filename = 'not_a_jwst_file.fits'
+        filename_parser(filename)