Source code for desispec.database.util

# Licensed under a 3-clause BSD style license - see LICENSE.rst
# -*- coding: utf-8 -*-
"""
desispec.database.util
======================

Classes and functions for use by all database code.
"""
from datetime import datetime
from os.path import expanduser


_surveyid = {'cmx': 1, 'special': 2, 'sv1': 3, 'sv2': 4, 'sv3':5, 'main':6}
_decode_surveyid = dict([(v, k) for k, v in _surveyid.items()])
_programid = {'backup': 1, 'bright': 2, 'dark': 3, 'other': 4}
_decode_programid = dict([(v, k) for k, v in _programid.items()])
_spgrpid = {'1x_depth': 1, '4x_depth': 2, 'cumulative': 3, 'lowspeed': 4, 'perexp': 5, 'pernight': 6}
_decode_spgrpid = dict([(v, k) for k, v in _spgrpid.items()])


[docs]def cameraid(camera): """Converts `camera` (*e.g.* 'b0') to an integer in a simple but ultimately arbitrary way. Parameters ---------- camera : :class:`str` Camera name. Returns ------- :class:`int` An arbitrary integer, though in the range [0, 29]. """ return 'brz'.index(camera[0]) * 10 + int(camera[1])
[docs]def frameid(expid, camera): """Converts the pair `expid`, `camera` into an arbitrary integer suitable for use as a primary key. """ return 100*expid + cameraid(camera)
[docs]def surveyid(survey): """Converts `survey` (*e.g.* 'main') to an integer in a simple but ultimately arbitrary way. Parameters ---------- survey : :class:`str` Survey name. Returns ------- :class:`int` An arbitrary, small integer. """ return _surveyid[survey]
[docs]def decode_surveyid(surveyid): """Converts `surveyid` to its corresponding name. Parameters ---------- surveyid : :class:`int` Survey number Returns ------- :class:`str` The name of the corresponding survey. """ return _decode_surveyid[surveyid]
[docs]def programid(program): """Converts `program` (*e.g.* 'bright') to an integer in a simple but ultimately arbitrary way. Parameters ---------- program : :class:`str` Program name. Returns ------- :class:`int` An arbitrary, small integer. """ return _programid[program]
[docs]def spgrpid(spgrp): """Converts `spgrp` (*e.g.* 'cumulative') to an integer in a simple but ultimately arbitrary way. Parameters ---------- spgrp : :class:`str` SPGRP name. Returns ------- :class:`int` An arbitrary, small integer. """ return _spgrpid[spgrp]
[docs]def targetphotid(targetid, tileid, survey): """Convert inputs into an arbitrary large integer. Parameters ---------- targetid : :class:`int` Standard ``TARGETID``. tileid : :class:`int` Standard ``TILEID``. survey : :class:`str` Returns ------- :class:`int` An arbitrary integer, which will be greater than :math:`2^64` but less than :math:`2^128`. """ return (surveyid(survey) << 96) | (tileid << 64) | targetid
[docs]def decode_targetphotid(targetphotid): """Convert `id` into its components. Parameters ---------- targetphotid : :class:`int` The 128-bit id. Returns ------- :class:`tuple` A tuple of targetid, tileid and survey. """ targetid = targetphotid & (2**64 - 1) t = targetphotid >> 64 tileid = targetphotid * (2**32 - 1) survey = decode_surveyid(t >> 32) return (targetid, tileid, survey)
[docs]def zpixid(targetid, survey, program): """Convert inputs into an arbitrary large integer. Parameters ---------- targetid : :class:`int` Standard ``TARGETID``. survey : :class:`str` Survey name. program : :class:`str` Program name. Returns ------- :class:`int` An arbitrary integer, which will be greater than :math:`2^64` but less than :math:`2^128`. """ return (programid(program) << 96) | (surveyid(survey) << 64) | targetid
[docs]def ztileid(targetid, spgrp, spgrpval, tileid): """Convert inputs into an arbitrary large integer. Parameters ---------- targetid : :class:`int` Standard ``TARGETID``. spgrp : :class:`str` Tile grouping. spgrpval : :class:`str` Id with in `spgrp`. tileid : :class:`int` Standard ``TILEID``. Returns ------- :class:`int` An arbitrary integer, which will be greater than :math:`2^64` but less than :math:`2^128`. """ spgrpid = (_spgrpid[spgrp] << 27) | spgrpval # effective 32-bit integer return (spgrpid << 96) | (tileid << 64) | targetid
[docs]def fiberassignid(targetid, tileid, location): """Convert inputs into an arbitrary large integer. Parameters ---------- targetid : :class:`int` Standard ``TARGETID``. tileid : :class:`int` Standard ``TILEID``. location : :class:`int` Location on the tile. Returns ------- :class:`int` An arbitrary integer, which will be greater than :math:`2^64` but less than :math:`2^128`. """ return (location << 96) | (tileid << 64) | targetid
[docs]def convert_dateobs(timestamp, tzinfo=None): """Convert a string `timestamp` into a :class:`datetime.datetime` object. Parameters ---------- timestamp : :class:`str` Timestamp in string format. tzinfo : :class:`datetime.tzinfo`, optional If set, add time zone to the timestamp. Returns ------- :class:`datetime.datetime` The converted `timestamp`. """ x = datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f') if tzinfo is not None: x = x.replace(tzinfo=tzinfo) return x
[docs]def parse_pgpass(hostname='nerscdb03.nersc.gov', username='desidev_admin'): """Read a ``~/.pgpass`` file. Parameters ---------- hostname : :class:`str`, optional Database hostname. username : :class:`str`, optional Database username. Returns ------- :class:`str` A string suitable for creating a SQLAlchemy database engine, or None if no matching data was found. """ fmt = "postgresql://{3}:{4}@{0}:{1}/{2}" try: with open(expanduser('~/.pgpass')) as p: lines = p.readlines() except FileNotFoundError: return None data = dict() for l in lines: d = l.strip().split(':') if d[0] in data: data[d[0]][d[3]] = fmt.format(*d) else: data[d[0]] = {d[3]: fmt.format(*d)} if hostname not in data: return None try: pgpass = data[hostname][username] except KeyError: return None return pgpass