# Virus Health Check: a validation tool for HETDEX/VIRUS data
# Copyright (C) 2015, 2016, 2017, 2018 "The HETDEX collaboration"
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Module implementing the recipes entry points
"""
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import os
import re
import pyhetdex.doc.docstring as doc
from pyhetdex.tools import six_ext
import pyhetdex.tools.files.file_tools as ft
import libvhc.loaders as vhcload
import libvhc.utils as vhcutils
import libvhc.exceptions as vhcexcept
# utilities
RECIPE_FILE = "v_recipe.txt"
"Name of the recipe file"
[docs]def get_recipe(path, log):
"""Load and try to read the recipe name or infer it from the files in path
Parameters
----------
path : string
path where to look for files if the recipe file does not exists
log : :class:`logging.Logger`
logger to use
"""
recipe_names = vhcload.load_recipes(log)
try: # read the recipe
with open(recipe_file(path)) as f:
recipe = f.read().strip("\n")
except six_ext.FileOpenError:
log.warning("No recipe file found")
recipe = make_recipe(path, log)
# check that the recipe is among the loaded ones
if recipe not in recipe_names:
msg = ("The recipe '{}' is not known. Make sure that the package"
" providing it is correctly installed.")
raise vhcexcept.VHCRecipeError(msg.format(msg))
return recipe
[docs]def recipe_file(path):
"""Return the name of the recipe file
Parameters
----------
path : string
path where the recipe file leaves
Returns
-------
string
name of the recipe file
"""
return os.path.join(path, RECIPE_FILE)
[docs]@doc.format_docstring(RECIPE_FILE)
def make_recipe(path, log):
"""Infer the recipe name of the first virus fits file found.
If the recipe is known, write it to the '{}' file.
Parameters
----------
path : string
path provided to the vhc executable.
log : :class:`logging.Logger`
logger to use
Returns
-------
recipe : string
recipe name
"""
try:
fname = next(ft.scan_files(path, matches=vhcutils.generic_virus_match,
exclude_dirs=['gp', 'wfs']))
recipe = "unknown"
for k, v in vhcutils.recipe_match.items():
if re.search(ft.wildcards_to_regex(v), fname) is not None:
recipe = k
break
except StopIteration:
log.critical("No fits file found.")
recipe = "unknown"
if recipe == "unknown":
msg = "Impossible to retrieve the recipe from the file names"
raise vhcexcept.VHCRecipeError(msg)
with open(recipe_file(path), 'w') as f:
f.write(recipe + '\n')
log.info("The recipe '%s' has be written to file", recipe)
return recipe
# recipe definitions
common_drivers = ["common:n_files",
"common:n_pixels",
"common:bitstat",
"common:headerkeys",
"common:nullpix",
# "common:check_header",
"common:check_overscan",
# "common:check_line_overscan",
"common:exptime",
"common:dettemp",
"common:saturation",
"common:repeat",
]
[docs]def recipe_protopype(name): # pragma: no cover
"""Signature for a function used to register a recipe.
Parameters
----------
name : string
name under which the recipe is advertised
Returns
-------
string
file name template to use to collect all the relevant file or to guess
the recipe name from the files; e.g. for the ``flat`` recipe
``[0-9]*flt.fits`` is returned
list of strings
list of default drivers to run for the recipe, if no ``v_driver.txt``
file is found; e.g. ``['common:n_files', 'common:saturation']``
"""
pass
[docs]def flat_recipe(name):
"""File names and drivers available for the flat recipe"""
recipe_match = "*[0-9]*flt.fits"
default_drivers = common_drivers + [
"flat:min_flux",
"flat:n_fibers",
"flat:check_throughput",
"flat:check_distortion"]
return recipe_match, default_drivers
[docs]def bias_recipe(name):
"""File names and drivers available for the bias recipe"""
recipe_match = "*[0-9]*zro.fits"
default_drivers = common_drivers + ["bias:compare",
"bias:flat",
"bias:linestddev",
"bias:fft"]
return recipe_match, default_drivers
[docs]def arc_recipe(name):
"""File names and drivers available for the arc recipe"""
recipe_match = "*[0-9]*cmp.fits"
default_drivers = common_drivers + ["arc:find_peaks"]
return recipe_match, default_drivers
[docs]def hetdex_dither_recipe(name):
"""File names and drivers available for the hetdex_dither recipe"""
recipe_match = "*[0-9]*sci.fits"
default_drivers = common_drivers + ["hetdex_dithers:n_dithers",
"hetdex_dithers:col_cte",
"hetdex_dithers:sky_level"]
return recipe_match, default_drivers
[docs]def twilight_recipe(name):
"""File names and drivers available for the twilight recipe"""
recipe_match = "*[0-9]*twi.fits"
default_drivers = common_drivers + [
"flat:min_flux",
"flat:n_fibers",
"flat:check_throughput",
"flat:check_distortion",
"flat:col_cte"]
return recipe_match, default_drivers
[docs]def dark_recipe(name):
"""File names and drivers available for the dark recipe"""
recipe_match = "*[0-9]*drk.fits"
default_drivers = common_drivers + ["bias:compare", "bias:flat"]
return recipe_match, default_drivers
[docs]def engineering_recipe(name):
"""File names and drivers available for the engineering recipe"""
recipe_match = "*[0-9]*eng.fits"
default_drivers = common_drivers
return recipe_match, default_drivers