# Licensed under a 3-clause BSD style license - see LICENSE.rst
import html
import re
import astropy.units as u
import astropy.io.ascii as asciitable
from ..query import BaseQuery
from ..utils import async_to_sync, prepend_docstr_nosections
from . import conf
from ..exceptions import TableParseError
__all__ = ['Nist', 'NistClass']
def _strip_blanks(table):
"""
Remove blank lines from table (included for "human readability" but
useless to us...
returns a single string joined by \n newlines
Parameters
----------
table : str
table to strip as a string
Returns
-------
single string joined by newlines.
"""
numbersletters = re.compile("[0-9A-Za-z]")
if isinstance(table, str):
table = table.split('\n')
table = [line for line in table if numbersletters.search(line)]
return "\n".join(table)
[docs]@async_to_sync
class NistClass(BaseQuery):
URL = conf.server
TIMEOUT = conf.timeout
unit_code = {'Angstrom': 0,
'nm': 1,
'um': 2}
energy_level_code = {'cm-1': 0, 'invcm': 0, 'cm': 0,
'ev': 1, 'eV': 1, 'EV': 1, 'electronvolt': 1,
'R': 2, 'Rydberg': 2, 'rydberg': 2}
order_out_code = {'wavelength': 0,
'multiplet': 1}
wavelength_unit_code = {'vacuum': 3,
'vac+air': 4}
def _args_to_payload(self, *args, **kwargs):
"""
Serves the same purpose as `~NistClass.query` but returns
the raw HTTP response rather than a `~astropy.table.Table` object.
Parameters
----------
minwav : `astropy.units.Quantity` object
The lower wavelength for the spectrum in appropriate units.
maxwav : `astropy.units.Quantity` object
The upper wavelength for the spectrum in appropriate units.
linename : str, optional
The spectrum to fetch. Defaults to "H I"
energy_level_unit : str, optional
The energy level units must be one of the following:
'R', 'Rydberg', 'rydberg', 'cm', 'cm-1', 'EV', 'eV',
'electronvolt', 'ev', 'invcm' Defaults to 'eV'.
output_order : str, optional
Decide ordering of output. Must be one of following:
['wavelength', 'multiplet']. Defaults to 'wavelength'.
wavelength_type : str, optional
Must be one of 'vacuum' or 'vac+air'. Defaults to 'vacuum'.
get_query_payload : bool, optional
If true then returns the dictionary of query parameters, posted to
remote server. Defaults to `False`.
Returns
-------
request_payload : dict
The dictionary of parameters sent with the HTTP request
"""
request_payload = {}
request_payload["spectra"] = kwargs['linename']
(min_wav, max_wav, wav_unit) = _parse_wavelength(args[0], args[1])
request_payload["low_wl"] = min_wav
request_payload["upp_wl"] = max_wav
request_payload["unit"] = wav_unit
request_payload["submit"] = "Retrieve Data"
request_payload["format"] = 1 # ascii
request_payload["line_out"] = 0 # All lines
request_payload["en_unit"] = Nist.energy_level_code[
kwargs["energy_level_unit"]]
request_payload["output"] = 0 # entirely rather than pagewise
request_payload["bibrefs"] = 1
request_payload["show_obs_wl"] = 1
request_payload["show_calc_wl"] = 1
request_payload["order_out"] = Nist.order_out_code[
kwargs['output_order']]
request_payload["max_low_enrg"] = ""
request_payload["show_av"] = Nist.wavelength_unit_code[
kwargs['wavelength_type']]
request_payload["max_upp_enrg"] = ""
request_payload["tsb_value"] = 0
request_payload["min_str"] = ""
request_payload["A_out"] = 0
request_payload["f_out"] = "on"
request_payload["intens_out"] = "on"
request_payload["max_str"] = ""
request_payload["allowed_out"] = 1
request_payload["forbid_out"] = 1
request_payload["min_accur"] = ""
request_payload["min_intens"] = ""
request_payload["conf_out"] = "on"
request_payload["term_out"] = "on"
request_payload["enrg_out"] = "on"
request_payload["J_out"] = "on"
request_payload["page_size"] = 15
request_payload["remove_js"] = "on"
request_payload["show_wn"] = 1
return request_payload
[docs] @prepend_docstr_nosections("\n" + _args_to_payload.__doc__)
def query_async(self, minwav, maxwav, linename="H I",
energy_level_unit='eV', output_order='wavelength',
wavelength_type='vacuum', get_query_payload=False):
"""
Returns
-------
response : `requests.Response` object
The response of the HTTP request.
"""
request_payload = self._args_to_payload(
minwav, maxwav, linename=linename,
energy_level_unit=energy_level_unit, output_order=output_order,
wavelength_type=wavelength_type)
if get_query_payload:
return request_payload
response = self._request("GET", url=Nist.URL, params=request_payload,
timeout=Nist.TIMEOUT)
return response
def _parse_result(self, response, verbose=False):
"""
Parses the results from the HTTP response to `astropy.table.Table`.
Parameters
----------
response : `requests.Response`
The HTTP response object
Returns
-------
table : `astropy.table.Table`
"""
pre_re = re.compile("<pre>(.*)</pre>", flags=re.DOTALL)
links_re = re.compile(r"<a.*?>\s*(\w+)\s*</a>")
content = str(response.text)
try:
pre = pre_re.findall(content)[0]
except IndexError:
raise Exception("Result did not contain a table")
try:
table = _strip_blanks(pre)
table = links_re.sub(r'\1', table)
table = html.unescape(table)
table = asciitable.read(table, Reader=asciitable.FixedWidth,
data_start=3, delimiter='|')
return table
except Exception as ex:
self.response = response
self.table_parse_error = ex
raise TableParseError("Failed to parse asciitable! The raw "
"response can be found in self.response, "
"and the error in self.table_parse_error.")
Nist = NistClass()
def _parse_wavelength(min_wav, max_wav):
"""
Helper function to return wavelength and units in form accepted by NIST
Parameters
----------
min_wav : `astropy.units.Quantity` object
The lower wavelength in proper units.
max_wav : `astropy.units.Quantity` object
The upper wavelength in proper units.
Returns
-------
tuple : (number, number, number)
The value of lower, upper wavelength and their unit.
"""
max_wav = max_wav.to(min_wav.unit)
wav_unit = min_wav.unit.to_string()
if wav_unit not in Nist.unit_code:
max_wav = max_wav.to(u.angstrom).value
min_wav = min_wav.to(u.angstrom).value
wav_unit = Nist.unit_code['Angstrom']
else:
max_wav = max_wav.value
min_wav = min_wav.value
wav_unit = Nist.unit_code[wav_unit]
return (min_wav, max_wav, wav_unit)