diff --git a/.editorconfig b/.editorconfig index 5349964..a01cb17 100644 --- a/.editorconfig +++ b/.editorconfig @@ -20,7 +20,7 @@ insert_final_newline = true # Code documentation [*.rst] -indent_style = tab +indent_style = space indent_size = 4 trim_trailing_whitespace = true insert_final_newline = true diff --git a/flows/__init__.py b/flows/__init__.py index af45bd1..e8fc4ed 100644 --- a/flows/__init__.py +++ b/flows/__init__.py @@ -1,12 +1,9 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# flake8: noqa - +""" +FLOWS pipeline package +""" from .photometry import photometry from .catalogs import download_catalog from .visibility import visibility -from .config import load_config - from .version import get_version __version__ = get_version(pep440=False) diff --git a/flows/aadc_db.py b/flows/aadc_db.py index a4ce7c4..8d25ab9 100644 --- a/flows/aadc_db.py +++ b/flows/aadc_db.py @@ -13,7 +13,7 @@ import psycopg2 as psql from psycopg2.extras import DictCursor import getpass -from .config import load_config +from tendrils.utils import load_config # -------------------------------------------------------------------------------------------------- diff --git a/flows/api/__init__.py b/flows/api/__init__.py deleted file mode 100644 index 8ad4638..0000000 --- a/flows/api/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# flake8: noqa - -from .targets import get_targets, get_target, add_target -from .datafiles import get_datafile, get_datafiles -from .catalogs import get_catalog, get_catalog_missing -from .sites import get_site, get_all_sites -from .photometry_api import get_photometry, upload_photometry -from .set_photometry_status import set_photometry_status, cleanup_photometry_status -from .filters import get_filters -from .lightcurves import get_lightcurve diff --git a/flows/api/catalogs.py b/flows/api/catalogs.py deleted file mode 100644 index 20714b9..0000000 --- a/flows/api/catalogs.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" - -.. codeauthor:: Rasmus Handberg -""" - -import astropy.units as u -from astropy.table import Table -from astropy.time import Time -import requests -from functools import lru_cache -from ..config import load_config - - -# -------------------------------------------------------------------------------------------------- -@lru_cache(maxsize=10) -def get_catalog(target, radius=None, output='table'): - """ - - Parameters: - target (int or str): - radius (float, optional): Radius around target in degrees to return targets for. - outout (str, optional): Desired output format. Choises are 'table', 'dict', 'json'. - Default='table'. - - Returns: - dict: Dictionary with three members: - - 'target': Information about target. - - 'references': Table with information about reference stars close to target. - - 'avoid': Table with stars close to target which should be avoided in FOV selection. - - .. codeauthor:: Rasmus Handberg - """ - - assert output in ('table', 'json', 'dict'), "Invalid output format" - - # Get API token from config file: - config = load_config() - token = config.get('api', 'token', fallback=None) - if token is None: - raise RuntimeError("No API token has been defined") - - # - r = requests.get('https://flows.phys.au.dk/api/reference_stars.php', params={'target': target}, - headers={'Authorization': 'Bearer ' + token}) - r.raise_for_status() - jsn = r.json() - - # Convert timestamps to actual Time objects: - jsn['target']['inserted'] = Time(jsn['target']['inserted'], scale='utc') - if jsn['target']['discovery_date'] is not None: - jsn['target']['discovery_date'] = Time(jsn['target']['discovery_date'], scale='utc') - - if output in ('json', 'dict'): - return jsn - - dict_tables = {} - - tab = Table(names=( - 'targetid', 'target_name', 'target_status', 'ra', 'decl', 'redshift', 'redshift_error', 'discovery_mag', - 'catalog_downloaded', 'pointing_model_created', 'inserted', 'discovery_date', 'project', 'host_galaxy', - 'ztf_id', 'sntype'), dtype=( - 'int32', 'str', 'str', 'float64', 'float64', 'float32', 'float32', 'float32', 'bool', 'bool', 'object', - 'object', 'str', 'str', 'str', 'str'), rows=[jsn['target']]) - - tab['ra'].description = 'Right ascension' - tab['ra'].unit = u.deg - tab['decl'].description = 'Declination' - tab['decl'].unit = u.deg - dict_tables['target'] = tab - - for table_name in ('references', 'avoid'): - tab = Table(names=( - 'starid', 'ra', 'decl', 'pm_ra', 'pm_dec', 'gaia_mag', 'gaia_bp_mag', 'gaia_rp_mag', 'gaia_variability', - 'B_mag', 'V_mag', 'H_mag', 'J_mag', 'K_mag', 'u_mag', 'g_mag', 'r_mag', 'i_mag', 'z_mag', 'distance'), - dtype=('int64', 'float64', 'float64', 'float32', 'float32', 'float32', 'float32', 'float32', 'int32', - 'float32', 'float32', 'float32', 'float32', 'float32', 'float32', 'float32', 'float32', 'float32', - 'float32', 'float64'), rows=jsn[table_name]) - - tab['starid'].description = 'Unique identifier in REFCAT2 catalog' - tab['ra'].description = 'Right ascension' - tab['ra'].unit = u.deg - tab['decl'].description = 'Declination' - tab['decl'].unit = u.deg - tab['pm_ra'].description = 'Proper motion in right ascension' - tab['pm_ra'].unit = u.mas / u.yr - tab['pm_dec'].description = 'Proper motion in declination' - tab['pm_dec'].unit = u.mas / u.yr - tab['distance'].description = 'Distance from object to target' - tab['distance'].unit = u.deg - - tab['gaia_mag'].description = 'Gaia G magnitude' - tab['gaia_bp_mag'].description = 'Gaia Bp magnitude' - tab['gaia_rp_mag'].description = 'Gaia Rp magnitude' - tab['gaia_variability'].description = 'Gaia variability classification' - tab['B_mag'].description = 'Johnson B magnitude' - tab['V_mag'].description = 'Johnson V magnitude' - tab['H_mag'].description = '2MASS H magnitude' - tab['J_mag'].description = '2MASS J magnitude' - tab['K_mag'].description = '2MASS K magnitude' - tab['u_mag'].description = 'u magnitude' - tab['g_mag'].description = 'g magnitude' - tab['r_mag'].description = 'r magnitude' - tab['i_mag'].description = 'i magnitude' - tab['z_mag'].description = 'z magnitude' - - # Add some meta-data to the table as well: - tab.meta['targetid'] = int(dict_tables['target']['targetid']) - - dict_tables[table_name] = tab - - return dict_tables - - -# -------------------------------------------------------------------------------------------------- -def get_catalog_missing(): - # Get API token from config file: - config = load_config() - token = config.get('api', 'token', fallback=None) - if token is None: - raise Exception("No API token has been defined") - - # - r = requests.get('https://flows.phys.au.dk/api/catalog_missing.php', headers={'Authorization': 'Bearer ' + token}) - r.raise_for_status() - return r.json() diff --git a/flows/api/datafiles.py b/flows/api/datafiles.py deleted file mode 100644 index 5e0c614..0000000 --- a/flows/api/datafiles.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" - -.. codeauthor:: Rasmus Handberg -""" - -import requests -from datetime import datetime -from functools import lru_cache -from ..config import load_config - - -# -------------------------------------------------------------------------------------------------- -@lru_cache(maxsize=10) -def get_datafile(fileid): - # Get API token from config file: - config = load_config() - token = config.get('api', 'token', fallback=None) - if token is None: - raise RuntimeError("No API token has been defined") - - r = requests.get('https://flows.phys.au.dk/api/datafiles.php', params={'fileid': fileid}, - headers={'Authorization': 'Bearer ' + token}) - r.raise_for_status() - jsn = r.json() - - # Parse some of the fields to Python objects: - jsn['inserted'] = datetime.strptime(jsn['inserted'], '%Y-%m-%d %H:%M:%S.%f') - jsn['lastmodified'] = datetime.strptime(jsn['lastmodified'], '%Y-%m-%d %H:%M:%S.%f') - - return jsn - - -# -------------------------------------------------------------------------------------------------- -def get_datafiles(targetid=None, filt=None, minversion=None): - """ - Get list of data file IDs to be processed. - - Parameters: - targetid (int, optional): Target ID to process. - filt (str, optional): Filter the returned list: - - ``missing``: Return only data files that have not yet been processed. - - ``'all'``: Return all data files. - minversion (str, optional): Special filter matching files not processed at least with - the specified version (defined internally in API for now). - - Returns: - list: List of data files the can be processed. - - .. codeauthor:: Rasmus Handberg - """ - - # Validate input: - if filt is None: - filt = 'missing' - if filt not in ('missing', 'all', 'error'): - raise ValueError("Invalid filter specified: '%s'" % filt) - - # Get API token from config file: - config = load_config() - token = config.get('api', 'token', fallback=None) - if token is None: - raise RuntimeError("No API token has been defined") - - params = {} - if targetid is not None: - params['targetid'] = targetid - if minversion is not None: - params['minversion'] = minversion - params['filter'] = filt - - r = requests.get('https://flows.phys.au.dk/api/datafiles.php', params=params, - headers={'Authorization': 'Bearer ' + token}) - r.raise_for_status() - jsn = r.json() - - return jsn diff --git a/flows/api/filters.py b/flows/api/filters.py deleted file mode 100644 index 49afcef..0000000 --- a/flows/api/filters.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" - -.. codeauthor:: Rasmus Handberg -""" - -import requests -from functools import lru_cache -import astropy.units as u -from ..config import load_config - - -# -------------------------------------------------------------------------------------------------- -@lru_cache(maxsize=10) -def get_filters(): - # Get API token from config file: - config = load_config() - token = config.get('api', 'token', fallback=None) - if token is None: - raise RuntimeError("No API token has been defined") - - r = requests.get('https://flows.phys.au.dk/api/filters.php', headers={'Authorization': 'Bearer ' + token}) - r.raise_for_status() - jsn = r.json() - - # Add units: - for f, val in jsn.items(): - if val.get('wavelength_center'): - val['wavelength_center'] *= u.nm - if val.get('wavelength_width'): - val['wavelength_width'] *= u.nm - - return jsn diff --git a/flows/api/lightcurves.py b/flows/api/lightcurves.py deleted file mode 100644 index c89154e..0000000 --- a/flows/api/lightcurves.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Fetch current lightcurve from Flows API. - -.. codeauthor:: Rasmus Handberg -""" - -import requests -import os.path -import tempfile -from astropy.table import Table -from ..config import load_config - - -# -------------------------------------------------------------------------------------------------- -def get_lightcurve(target): - """ - Retrieve lightcurve from Flows server. - - Parameters: - target (int): Target to download lightcurve for. - - Returns: - :class:`astropy.table.Table`: Table containing lightcurve. - - TODO: - - Enable caching of files. - - .. codeauthor:: Rasmus Handberg - """ - - # Get API token from config file: - config = load_config() - token = config.get('api', 'token', fallback=None) - if token is None: - raise RuntimeError("No API token has been defined") - - # Send query to the Flows API: - params = {'target': target} - r = requests.get('https://flows.phys.au.dk/api/lightcurve.php', params=params, - headers={'Authorization': 'Bearer ' + token}) - r.raise_for_status() - - # Create tempory directory and save the file into there, - # then open the file as a Table: - with tempfile.TemporaryDirectory() as tmpdir: - tmpfile = os.path.join(tmpdir, 'table.ecsv') - with open(tmpfile, 'w') as fid: - fid.write(r.text) - - tab = Table.read(tmpfile, format='ascii.ecsv') - - return tab diff --git a/flows/api/photometry_api.py b/flows/api/photometry_api.py deleted file mode 100644 index 89629b5..0000000 --- a/flows/api/photometry_api.py +++ /dev/null @@ -1,177 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Upload photometry results to Flows server. - -.. codeauthor:: Rasmus Handberg -""" - -import logging -import os -import zipfile -import requests -import tempfile -import shutil -import glob -from tqdm import tqdm -from astropy.table import Table -from .. import api -from ..config import load_config -from ..utilities import get_filehash - - -# -------------------------------------------------------------------------------------------------- -def get_photometry(photid): - """ - Retrieve lightcurve from Flows server. - - Please note that it can significantly speed up repeated calls to this function - to specify a cache directory in the config-file under api -> photometry_cache. - This will download the files only once and store them in this local cache for - use in subsequent calls. - - Parameters: - photid (int): Fileid for the photometry file. - - Returns: - :class:`astropy.table.Table`: Table containing photometry. - - .. codeauthor:: Rasmus Handberg - """ - - # Get API token from config file: - config = load_config() - token = config.get('api', 'token', fallback=None) - if token is None: - raise RuntimeError("No API token has been defined") - - # Determine where to store the downloaded file: - photcache = config.get('api', 'photometry_cache', fallback=None) - tmpdir = None - if photcache is not None: - photcache = os.path.abspath(photcache) - if not os.path.isdir(photcache): - raise FileNotFoundError(f"Photometry cache directory does not exist: {photcache}") - else: - tmpdir = tempfile.TemporaryDirectory(prefix='flows-api-get_photometry-') - photcache = tmpdir.name - - # Construct path to the photometry file in the cache: - photfile = os.path.join(photcache, f'photometry-{photid:d}.ecsv') - - if not os.path.isfile(photfile): - # Send query to the Flows API: - params = {'fileid': photid} - r = requests.get('https://flows.phys.au.dk/api/download_photometry.php', params=params, - headers={'Authorization': 'Bearer ' + token}) - r.raise_for_status() - - # Create tempory directory and save the file into there, - # then open the file as a Table: - with open(photfile, 'w') as fid: - fid.write(r.text) - - # Read the photometry file: - tab = Table.read(photfile, format='ascii.ecsv') - - # Explicitly cleanup the tempoary directory if it was created: - if tmpdir: - tmpdir.cleanup() - - return tab - - -# -------------------------------------------------------------------------------------------------- -def upload_photometry(fileid, delete_completed=False): - """ - Upload photometry results to Flows server. - - This will make the uploaded photometry the active/newest/best photometry and - be used in plots and shown on the website. - - Parameters: - fileid (int): File ID of photometry to upload to server. - delete_completed (bool, optional): Delete the photometry from the local - working directory if the upload was successful. Default=False. - - .. codeauthor:: Rasmus Handberg - """ - - logger = logging.getLogger(__name__) - tqdm_settings = {'disable': None if logger.isEnabledFor(logging.INFO) else True} - - # Use API to get the datafile information: - datafile = api.get_datafile(fileid) - - # Get API token from config file: - config = load_config() - token = config.get('api', 'token', fallback=None) - if token is None: - raise RuntimeError("No API token has been defined") - photdir_root = config.get('photometry', 'output', fallback='.') - - # Find the photometry output directory for this fileid: - photdir = os.path.join(photdir_root, datafile['target_name'], f'{fileid:05d}') - if not os.path.isdir(photdir): - # Do a last check, to ensure that we have not just added the wrong number of zeros - # to the directory name: - found_photdir = [] - for d in os.listdir(os.path.join(photdir_root, datafile['target_name'])): - if d.isdigit() and int(d) == fileid and os.path.isdir(d): - found_photdir.append(os.path.join(photdir_root, datafile['target_name'], d)) - # If we only found one, use it, otherwise throw an exception: - if len(found_photdir) == 1: - photdir = found_photdir[0] - elif len(found_photdir) > 1: - raise RuntimeError(f"Several photometry output found for fileid={fileid}. \ - You need to do a cleanup of the photometry output directories.") - else: - raise FileNotFoundError(photdir) - - # Make sure required files are actually there: - photdir = os.path.abspath(photdir) - files_existing = os.listdir(photdir) - if 'photometry.ecsv' not in files_existing: - raise FileNotFoundError(os.path.join(photdir, 'photometry.ecsv')) - if 'photometry.log' not in files_existing: - raise FileNotFoundError(os.path.join(photdir, 'photometry.log')) - - # Create list of files to be uploaded: - files = [os.path.join(photdir, 'photometry.ecsv'), os.path.join(photdir, 'photometry.log')] - files += glob.glob(os.path.join(photdir, '*.png')) - - # Create ZIP file: - with tempfile.TemporaryDirectory(prefix='flows-upload-') as tmpdir: - # Create ZIP-file within the temp directory: - fpath_zip = os.path.join(tmpdir, f'{fileid:05d}.zip') - - # Create ZIP file with all the files: - with zipfile.ZipFile(fpath_zip, 'w', allowZip64=True) as z: - for f in tqdm(files, desc=f'Zipping {fileid:d}', **tqdm_settings): - logger.debug('Zipping %s', f) - z.write(f, os.path.basename(f)) - - # Change the name of the uploaded file to contain the file hash: - fhash = get_filehash(fpath_zip) - fname_zip = f'{fileid:05d}-{fhash:s}.zip' - - # Send file to the API: - logger.info("Uploading to server...") - with open(fpath_zip, 'rb') as fid: - r = requests.post('https://flows.phys.au.dk/api/upload_photometry.php', params={'fileid': fileid}, - files={'file': (fname_zip, fid, 'application/zip')}, - headers={'Authorization': 'Bearer ' + token}) - - # Check the returned data from the API: - if r.text.strip() != 'OK': - logger.error(r.text) - raise RuntimeError("An error occurred while uploading photometry: " + r.text) - r.raise_for_status() - - # If we have made it this far, the upload must have been a success: - if delete_completed: - if set([os.path.basename(f) for f in files]) == set(os.listdir(photdir)): - logger.info("Deleting photometry from workdir: '%s'", photdir) - shutil.rmtree(photdir, ignore_errors=True) - else: - logger.warning("Not deleting photometry from workdir: '%s'", photdir) diff --git a/flows/api/set_photometry_status.py b/flows/api/set_photometry_status.py deleted file mode 100644 index 2f6dcb2..0000000 --- a/flows/api/set_photometry_status.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" - -.. codeauthor:: Rasmus Handberg -""" - -import logging -import requests -from ..config import load_config - - -# -------------------------------------------------------------------------------------------------- -def set_photometry_status(fileid, status): - """ - Set photometry status. - - Parameters: - fileid (int): - status (str): Choises are 'running', 'error' or 'done'. - - .. codeauthor:: Rasmus Handberg - """ - # Validate the input: - logger = logging.getLogger(__name__) - if status not in ('running', 'error', 'abort', 'ingest', 'done'): - raise ValueError('Invalid status') - - # Get API token from config file: - config = load_config() - i_am_pipeline = config.getboolean('api', 'pipeline', fallback=False) - if not i_am_pipeline: - logger.debug("Not setting status since user is not pipeline") - return False - - # Get API token from config file: - token = config.get('api', 'token', fallback=None) - if token is None: - raise RuntimeError("No API token has been defined") - - # Send HTTP request to FLOWS server: - r = requests.get('https://flows.phys.au.dk/api/set_photometry_status.php', - params={'fileid': fileid, 'status': status}, headers={'Authorization': 'Bearer ' + token}) - r.raise_for_status() - res = r.text.strip() - - if res != 'OK': - raise RuntimeError(res) - - return True - - -# -------------------------------------------------------------------------------------------------- -def cleanup_photometry_status(): - """ - Perform a cleanup of the photometry status indicator. - - This will change all processes still marked as "running" - to "abort" if they have been running for more than a day. - - .. codeauthor:: Rasmus Handberg - """ - # Validate the input: - logger = logging.getLogger(__name__) - - # Get API token from config file: - config = load_config() - i_am_pipeline = config.getboolean('api', 'pipeline', fallback=False) - if not i_am_pipeline: - logger.debug("Not setting status since user is not pipeline") - return False - - # Get API token from config file: - token = config.get('api', 'token', fallback=None) - if token is None: - raise RuntimeError("No API token has been defined") - - # Send HTTP request to FLOWS server: - r = requests.get('https://flows.phys.au.dk/api/cleanup_photometry_status.php', - headers={'Authorization': 'Bearer ' + token}) - r.raise_for_status() - res = r.text.strip() - - if res != 'OK': - raise RuntimeError(res) - - return True diff --git a/flows/api/sites.py b/flows/api/sites.py deleted file mode 100644 index f18e955..0000000 --- a/flows/api/sites.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" - -.. codeauthor:: Rasmus Handberg -""" - -import requests -from functools import lru_cache -import astropy.units as u -from astropy.coordinates import EarthLocation -from ..config import load_config - - -# -------------------------------------------------------------------------------------------------- -@lru_cache(maxsize=10) -def get_site(siteid): - # Get API token from config file: - config = load_config() - token = config.get('api', 'token', fallback=None) - if token is None: - raise RuntimeError("No API token has been defined") - - r = requests.get('https://flows.phys.au.dk/api/sites.php', params={'siteid': siteid}, - headers={'Authorization': 'Bearer ' + token}) - r.raise_for_status() - jsn = r.json() - - # Special derived objects: - jsn['EarthLocation'] = EarthLocation(lat=jsn['latitude'] * u.deg, lon=jsn['longitude'] * u.deg, - height=jsn['elevation'] * u.m) - - return jsn - - -# -------------------------------------------------------------------------------------------------- -@lru_cache(maxsize=1) -def get_all_sites(): - # Get API token from config file: - config = load_config() - token = config.get('api', 'token', fallback=None) - if token is None: - raise RuntimeError("No API token has been defined") - - r = requests.get('https://flows.phys.au.dk/api/sites.php', headers={'Authorization': 'Bearer ' + token}) - r.raise_for_status() - jsn = r.json() - - # Special derived objects: - for site in jsn: - site['EarthLocation'] = EarthLocation(lat=site['latitude'] * u.deg, lon=site['longitude'] * u.deg, - height=site['elevation'] * u.m) - - return jsn diff --git a/flows/api/targets.py b/flows/api/targets.py deleted file mode 100644 index 8dd58fb..0000000 --- a/flows/api/targets.py +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Get information about targets in Flows. - -.. codeauthor:: Rasmus Handberg -""" - -import re -from datetime import datetime -import pytz -from astropy.time import Time -import requests -from functools import lru_cache -from ..config import load_config - - -# -------------------------------------------------------------------------------------------------- -@lru_cache(maxsize=10) -def get_target(target): - # Get API token from config file: - config = load_config() - token = config.get('api', 'token', fallback=None) - if token is None: - raise RuntimeError("No API token has been defined") - - r = requests.get('https://flows.phys.au.dk/api/targets.php', params={'target': target}, - headers={'Authorization': 'Bearer ' + token}) - r.raise_for_status() - jsn = r.json() - - # Parse some of the fields to Python objects: - jsn['inserted'] = datetime.strptime(jsn['inserted'], '%Y-%m-%d %H:%M:%S.%f') - if jsn['discovery_date']: - jsn['discovery_date'] = Time(jsn['discovery_date'], format='iso', scale='utc') - - return jsn - - -# -------------------------------------------------------------------------------------------------- -@lru_cache(maxsize=1) -def get_targets(): - # Get API token from config file: - config = load_config() - token = config.get('api', 'token', fallback=None) - if token is None: - raise RuntimeError("No API token has been defined") - - r = requests.get('https://flows.phys.au.dk/api/targets.php', headers={'Authorization': 'Bearer ' + token}) - r.raise_for_status() - jsn = r.json() - - # Parse some of the fields to Python objects: - for tgt in jsn: - tgt['inserted'] = datetime.strptime(tgt['inserted'], '%Y-%m-%d %H:%M:%S.%f') - if tgt['discovery_date']: - tgt['discovery_date'] = Time(tgt['discovery_date'], format='iso', scale='utc') - - return jsn - - -# -------------------------------------------------------------------------------------------------- -def add_target(name, coord, redshift=None, redshift_error=None, discovery_date=None, discovery_mag=None, - host_galaxy=None, ztf=None, sntype=None, status='candidate', project='flows'): - """ - Add new candidate or target. - - Coordinates are specified using an Astropy SkyCoord object, which can be - created in the following way: - - coord = SkyCoord(ra=19.1, dec=89.00001, unit='deg', frame='icrs') - - The easiest way is to specify ``discovery_date`` as an Astropy Time object: - - discovery_date = Time('2020-01-02 00:00:00', format='iso', scale='utc') - - Alternatively, you can also specify it as a :class:`datetime.datetime` object, - but some care has to be taken with specifying the correct timezone: - - discovery_date = datetime.strptime('2020-01-02 00:00:00', '%Y-%m-%d %H:%M:%S%f') - discovery_date = pytz.timezone('America/New_York').localize(ddate) - - Lastly, it can be given as a simple date-string of the following form, - but here the data has to be given in UTC: - - discovery_date = '2020-01-02 23:56:02.123' - - Parameters: - name (str): Name of target. Must be of the form "YYYYxyz", where YYYY is the year, - and xyz are letters. - coord (:class:ʼastropy.coordinates.SkyCoordʼ): Sky coordinates of target. - redshift (float, optional): Redshift. - redshift_error (float, optional): Uncertainty on redshift. - discovery_date (:class:`astropy.time.Time`, :class:`datetime.datetime` or str, optional): - discovery_mag (float, optional): Magnitude at time of discovery. - host_galaxy (str, optional): Host galaxy name. - sntype (str, optional): Supernovae type (e.g. Ia, Ib, II). - ztf (str, optional): ZTF identifier. - status (str, optional): - project (str, optional): - - Returns: - int: New target identifier in Flows system. - - .. codeauthor:: Rasmus Handberg - """ - # Check and convert input: - if not re.match(r'^[12]\d{3}([A-Z]|[a-z]{2,4})$', name.strip()): - raise ValueError("Invalid target name.") - - if redshift is None and redshift_error is not None: - raise ValueError("Redshift error specified without redshift value") - - if isinstance(discovery_date, Time): - discovery_date = discovery_date.utc.iso - elif isinstance(discovery_date, datetime): - discovery_date = discovery_date.astimezone(pytz.timezone('UTC')) - discovery_date = discovery_date.strftime('%Y-%m-%d %H:%M:%S%f') - elif isinstance(discovery_date, str): - discovery_date = datetime.strptime(discovery_date, '%Y-%m-%d %H:%M:%S%f') - discovery_date = discovery_date.strftime('%Y-%m-%d %H:%M:%S%f') - - if status not in ('candidate', 'target'): - raise ValueError("Invalid target status.") - - # Get API token from config file: - config = load_config() - token = config.get('api', 'token', fallback=None) - if token is None: - raise RuntimeError("No API token has been defined") - - # Gather parameters to be sent to API: - params = {'targetid': 0, 'target_name': name.strip(), 'ra': coord.icrs.ra.deg, 'decl': coord.icrs.dec.deg, - 'redshift': redshift, 'redshift_error': redshift_error, 'discovery_date': discovery_date, - 'discovery_mag': discovery_mag, 'host_galaxy': host_galaxy, 'project': project, 'ztf_id': ztf, - 'target_status': status, 'sntype': sntype} - - # Post the request to the API: - r = requests.post('https://flows.phys.au.dk/api/targets_add.php', data=params, - headers={'Authorization': 'Bearer ' + token}) - r.raise_for_status() - jsn = r.json() - - # Check for errors: - if jsn['errors'] is not None: - raise RuntimeError(f"Adding target '{name}' resulted in an error: {jsn['errors']}") - - return int(jsn['targetid']) diff --git a/flows/catalogs.py b/flows/catalogs.py index 2d7ca98..a25b7e0 100644 --- a/flows/catalogs.py +++ b/flows/catalogs.py @@ -20,9 +20,8 @@ from astropy.table import Table, MaskedColumn from astroquery.sdss import SDSS from astroquery.simbad import Simbad -from .config import load_config +from tendrils.utils import load_config, query_ztf_id from .aadc_db import AADC_DB -from .ztf import query_ztf_id # -------------------------------------------------------------------------------------------------- diff --git a/flows/config.py b/flows/config.py deleted file mode 100644 index 846b890..0000000 --- a/flows/config.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" - -.. codeauthor:: Rasmus Handberg -""" - -import os.path -import configparser -from functools import lru_cache - - -# -------------------------------------------------------------------------------------------------- -@lru_cache(maxsize=1) -def load_config(): - """ - Load configuration file. - - Returns: - ``configparser.ConfigParser``: Configuration file. - - .. codeauthor:: Rasmus Handberg - """ - - config_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'config.ini') - if not os.path.isfile(config_file): - raise FileNotFoundError("config.ini file not found") - - config = configparser.ConfigParser() - config.read(config_file) - return config diff --git a/flows/load_image.py b/flows/load_image.py index ab06bc8..b6edd35 100644 --- a/flows/load_image.py +++ b/flows/load_image.py @@ -14,7 +14,7 @@ from astropy.io import fits from astropy.time import Time from astropy.wcs import WCS, FITSFixedWarning -from . import api +from tendrils import api # -------------------------------------------------------------------------------------------------- diff --git a/flows/photometry.py b/flows/photometry.py index f6d89e8..173bb6a 100644 --- a/flows/photometry.py +++ b/flows/photometry.py @@ -26,6 +26,8 @@ from astropy.wcs.utils import proj_plane_pixel_area, fit_wcs_from_points from astropy.time import Time import sep +from tendrils import api +from tendrils.utils import load_config warnings.simplefilter('ignore', category=AstropyDeprecationWarning) from photutils import CircularAperture, CircularAnnulus, aperture_photometry # noqa: E402 @@ -33,9 +35,7 @@ from photutils import Background2D, SExtractorBackground, MedianBackground # noqa: E402 from photutils.utils import calc_total_error # noqa: E402 -from . import api # noqa: E402 from . import reference_cleaning as refclean # noqa: E402 -from .config import load_config # noqa: E402 from .plots import plt, plot_image # noqa: E402 from .version import get_version # noqa: E402 from .load_image import load_image # noqa: E402 diff --git a/flows/run_imagematch.py b/flows/run_imagematch.py index 1096291..418b696 100644 --- a/flows/run_imagematch.py +++ b/flows/run_imagematch.py @@ -17,10 +17,9 @@ import re from astropy.io import fits from astropy.wcs.utils import proj_plane_pixel_area -# from setuptools import Distribution -# from setuptools.command.install import install +from tendrils import api from .load_image import load_image -from . import api + # -------------------------------------------------------------------------------------------------- diff --git a/flows/tns.py b/flows/tns.py index 004de12..9b4811e 100644 --- a/flows/tns.py +++ b/flows/tns.py @@ -1,33 +1,29 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ TNS API FUNCTIONS -Pre-provided helper functions for the TNS API +Pre-provided helper functions for the TNS API, type annotations added Obtained from https://wis-tns.weizmann.ac.il/content/tns-getting-started - -.. codeauthor:: Emir Karamehmetoglu -.. codeauthor:: Rasmus Handberg """ - +from __future__ import annotations import logging from astropy.table import Table import astropy.units as u +from astropy.coordinates import SkyCoord import requests import json import datetime -from .config import load_config +from tendrils.utils import load_config +from typing import Optional, Union url_tns_api = 'https://www.wis-tns.org/api/get' url_tns_search = 'https://www.wis-tns.org/search' +DateType = Union[datetime.datetime, str] -# -------------------------------------------------------------------------------------------------- class TNSConfigError(RuntimeError): pass -# -------------------------------------------------------------------------------------------------- -def _load_tns_config(): +def _load_tns_config() -> dict[str, str]: logger = logging.getLogger(__name__) config = load_config() @@ -52,8 +48,8 @@ def _load_tns_config(): return {'api-key': api_key, 'user-agent': user_agent} -# -------------------------------------------------------------------------------------------------- -def tns_search(coord=None, radius=3 * u.arcsec, objname=None, internal_name=None): +def tns_search(coord: Optional[SkyCoord] = None, radius: u.Quantity = 3 * u.arcsec, objname: Optional[str] = None, + internal_name: Optional[str] = None) -> Optional[dict]: """ Cone-search TNS for object near coordinate. @@ -92,8 +88,7 @@ def tns_search(coord=None, radius=3 * u.arcsec, objname=None, internal_name=None return None -# -------------------------------------------------------------------------------------------------- -def tns_get_obj(name): +def tns_get_obj(name:str) -> Optional[dict]: """ Search TNS for object by name. @@ -130,8 +125,9 @@ def tns_get_obj(name): return None -# -------------------------------------------------------------------------------------------------- -def tns_getnames(months=None, date_begin=None, date_end=None, zmin=None, zmax=None, objtype=[3, 104]): +def tns_getnames(months: Optional[int] = None, date_begin: Optional[DateType] = None, + date_end: Optional[DateType] = None, zmin: Optional[float] = None, + zmax: Optional[float] = None, objtype: tuple[int] = (3, 104)) -> list[str]: """ Get SN names from TNS. diff --git a/flows/visibility.py b/flows/visibility.py index f18162c..2736b6b 100644 --- a/flows/visibility.py +++ b/flows/visibility.py @@ -16,7 +16,7 @@ from datetime import datetime from astropy.coordinates import SkyCoord, AltAz, get_sun, get_moon from astropy.visualization import quantity_support -from . import api +from tendrils import api # -------------------------------------------------------------------------------------------------- diff --git a/flows/ztf.py b/flows/ztf.py index c532ce2..75ad66c 100644 --- a/flows/ztf.py +++ b/flows/ztf.py @@ -1,11 +1,6 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ Query ZTF target information using ALeRCE API. https://alerceapi.readthedocs.io/ - -.. codeauthor:: Emir Karamehmetoglu -.. codeauthor:: Rasmus Handberg """ import numpy as np @@ -15,7 +10,7 @@ from astropy.time import Time import datetime import requests -from . import api +from tendrils import api # -------------------------------------------------------------------------------------------------- diff --git a/requirements.txt b/requirements.txt index e2f445b..6e5a42c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ flake8-tabs >= 2.3.2 flake8-builtins flake8-logging-format numpy >= 1.16 -scipy == 1.5.4 +scipy >= 1.5.4 astropy == 4.1 photutils == 1.1.0; python_version >= '3.7' Bottleneck == 1.3.2 @@ -17,7 +17,7 @@ PyYAML psycopg2-binary jplephem vtk -scikit-image == 0.17.2 +scikit-image >= 0.17.2 tqdm pytz git+https://github.com/obscode/imagematch.git@photutils#egg=imagematch @@ -25,3 +25,4 @@ sep astroalign > 2.3 networkx astroquery >= 0.4.2 +tendrils >= 0.1.5 \ No newline at end of file diff --git a/run_catalogs.py b/run_catalogs.py index 49fd13f..5e283d1 100644 --- a/run_catalogs.py +++ b/run_catalogs.py @@ -1,29 +1,37 @@ -# -*- coding: utf-8 -*- """ - -.. codeauthor:: Rasmus Handberg +Runner to add target catalog, if catalog info is missing. Print output. """ - import argparse import logging -from flows import api, download_catalog +from tendrils import api +from flows import download_catalog -if __name__ == '__main__': - # Parse command line arguments: + +def parse(): + """ + # Parse command line arguments: + """ parser = argparse.ArgumentParser(description='Run catalog.') parser.add_argument('-d', '--debug', help='Print debug messages.', action='store_true') parser.add_argument('-q', '--quiet', help='Only report warnings and errors.', action='store_true') parser.add_argument('-t', '--target', type=str, help='Target to print catalog for.', nargs='?', default=None) - args = parser.parse_args() + return parser.parse_args() +def set_logging_level(args): # Set logging level: logging_level = logging.INFO if args.quiet: logging_level = logging.WARNING elif args.debug: logging_level = logging.DEBUG + return logging_level + +def main(): + # Parse command line arguments: + args = parse() # Setup logging: + logging_level = set_logging_level(args) formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') console = logging.StreamHandler() console.setFormatter(formatter) @@ -32,16 +40,19 @@ logger.addHandler(console) logger.setLevel(logging_level) + # Get missing for target in api.get_catalog_missing(): logger.info("Downloading catalog for target=%s...", target) - download_catalog(target) + download_catalog(target) # @TODO: refactor to Tendrils + # download target catalog for printing if args.target is not None: cat = api.get_catalog(args.target) - print("Target:") - cat['target'].pprint_all() - print("\nReferences:") - cat['references'].pprint_all() - print("\nAvoid:") - cat['avoid'].pprint_all() + print(f"Target:{cat['target'].pprint_all()} " + f"\nReferences: {cat['references'].pprint_all()} " + f"\nAvoid:cat['avoid'].pprint_all()") + + +if __name__ == '__main__': + main() diff --git a/run_download_ztf.py b/run_download_ztf.py index c996f46..459a859 100644 --- a/run_download_ztf.py +++ b/run_download_ztf.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ Download ZTF photometry from ALERCE API. - https://alerceapi.readthedocs.io/ """ @@ -10,11 +8,11 @@ import logging import os import numpy as np -from flows import ztf, api, load_config -from flows.plots import plt +import matplotlib.pyplot as plt +from tendrils import api +from tendrils.utils import load_config, ztf -# -------------------------------------------------------------------------------------------------- def main(): # Parse command line arguments: parser = argparse.ArgumentParser(description='Download ZTF photometry.') diff --git a/run_ingest.py b/run_ingest.py index 9e4cf9c..a271b0c 100644 --- a/run_ingest.py +++ b/run_ingest.py @@ -5,7 +5,7 @@ This code is obviously only meant to run on the central Flows systems, and will not work outside of that environment. - +@TODO: Refactor Database out of this script. .. codeauthor:: Rasmus Handberg """ diff --git a/run_photometry.py b/run_photometry.py index 134f85e..f7042ae 100644 --- a/run_photometry.py +++ b/run_photometry.py @@ -1,9 +1,5 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ -Run Flows photometry. - -.. codeauthor:: Rasmus Handberg +Run Flows photometry. Allows multithreaded operations to be run """ import argparse @@ -13,10 +9,10 @@ import shutil import functools import multiprocessing -from flows import api, photometry, load_config +from tendrils import api, utils +from flows import photometry -# -------------------------------------------------------------------------------------------------- def process_fileid(fid, output_folder_root=None, attempt_imagematch=True, autoupload=False, keep_diff_fixed=False, cm_timeout=None): logger = logging.getLogger('flows') @@ -76,7 +72,6 @@ def process_fileid(fid, output_folder_root=None, attempt_imagematch=True, autoup return photfile -# -------------------------------------------------------------------------------------------------- def main(): # Parse command line arguments: parser = argparse.ArgumentParser(description='Run photometry pipeline.') @@ -152,7 +147,7 @@ def main(): fileids = list(set(fileids)) # Ask the config where we should store the output: - config = load_config() + config = utils.load_config() output_folder_root = config.get('photometry', 'output', fallback='.') # Create function wrapper: @@ -180,6 +175,5 @@ def main(): process_fileid_wrapper(fid) -# -------------------------------------------------------------------------------------------------- if __name__ == '__main__': main() diff --git a/run_plotlc.py b/run_plotlc.py index 938be88..c058e6b 100644 --- a/run_plotlc.py +++ b/run_plotlc.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ Plot photometry for target, loaded from local photometry working directory. - +@TODO:Refactor out of FLOWS pipeline into flows-tools .. codeauthor:: Emir K .. codeauthor:: Rasmus Handberg """ @@ -14,12 +14,11 @@ from astropy.table import Table from astropy.time import Time from flows.plots import plt, plots_interactive -from flows import api, load_config +from tendrils import api, utils import mplcursors import seaborn as sns -# -------------------------------------------------------------------------------------------------- def main(): # All available filters: all_filters = list(api.get_filters().keys()) @@ -57,7 +56,7 @@ def main(): snname = api.get_datafile(datafiles[0])['target_name'] # Change to directory, raise if it does not exist - config = load_config() + config = utils.load_config() workdir_root = config.get('photometry', 'output', fallback='.') sndir = os.path.join(workdir_root, snname) if not os.path.isdir(sndir): diff --git a/run_querytns.py b/run_querytns.py index e837adc..728a4e7 100644 --- a/run_querytns.py +++ b/run_querytns.py @@ -4,7 +4,7 @@ Query TNS for new targets and upload to candidate marshal. https://wis-tns.weizmann.ac.il/ TNS bot apikey must exist in config - +@TODO: Move to flows API .. codeauthor:: Emir Karamehmetoglu .. codeauthor:: Rasmus Handberg """ @@ -16,7 +16,8 @@ from astropy.coordinates import SkyCoord from astropy.time import Time from datetime import datetime, timedelta, timezone -from flows import api, tns +from tendrils import api +from tendrils.utils import load_tns_config, tns_getnames, TNSConfigError, tns_get_obj # -------------------------------------------------------------------------------------------------- @@ -57,8 +58,8 @@ def main(): # Try to load TNS config - only used for early stopping try: - tns._load_tns_config() - except tns.TNSConfigError: + load_tns_config() + except TNSConfigError: parser.error("Error in TNS configuration.") return @@ -70,7 +71,7 @@ def main(): # Query TNS for SN names logger.info('Querying TNS for all targets, this may take awhile') - nms = tns.tns_getnames(months=args.limit_months, # pre-limit TNS search to candidates reported in the last X months + nms = tns_getnames(months=args.limit_months, # pre-limit TNS search to candidates reported in the last X months date_begin=date_begin, date_end=date_end, zmin=args.zmin, zmax=args.zmax, objtype=args.objtype# Relevant TNS SN Ia subtypes. ) @@ -97,7 +98,7 @@ def main(): logger.debug('querying TNS for: %s', sn) # make GET request to TNS via API - reply = tns.tns_get_obj(sn) + reply = tns_get_obj(sn) # Parse output if reply: diff --git a/run_upload_photometry.py b/run_upload_photometry.py index 1480242..f3aa757 100644 --- a/run_upload_photometry.py +++ b/run_upload_photometry.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- """ Upload photometry results to Flows server. @@ -8,10 +6,9 @@ import argparse import logging -from flows import api +from tendrils import api -# -------------------------------------------------------------------------------------------------- def main(): # Parse command line arguments: parser = argparse.ArgumentParser(description='Upload photometry.') diff --git a/tests/conftest.py b/tests/conftest.py index ea9d2db..d6d8345 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,7 +9,6 @@ import pytest import sys import os -# import shutil import configparser import subprocess import shlex diff --git a/tests/test_api.py b/tests/test_api.py deleted file mode 100644 index 3fac1a9..0000000 --- a/tests/test_api.py +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Test API calls. - -.. codeauthor:: Rasmus Handberg -""" - -import pytest -import os.path -import tempfile -import numpy as np -from astropy.coordinates import EarthLocation -from astropy.table import Table -import conftest # noqa: F401 -from flows import api, load_config - - -# -------------------------------------------------------------------------------------------------- -def test_api_get_targets(SETUP_CONFIG): - tab = api.get_targets() - print(tab) - - assert isinstance(tab, list) - assert len(tab) > 0 - for target in tab: - assert isinstance(target, dict) - assert 'target_name' in target - assert 'targetid' in target - assert 'ra' in target - assert 'decl' in target - assert 'target_status' in target - - -# -------------------------------------------------------------------------------------------------- -def test_api_get_target(SETUP_CONFIG): - tab = api.get_target(2) - print(tab) - - assert isinstance(tab, dict) - assert tab['target_name'] == '2019yvr' - assert tab['targetid'] == 2 - assert tab['target_status'] == 'target' - assert tab['ztf_id'] == 'ZTF20aabqkxs' - - -# -------------------------------------------------------------------------------------------------- -def test_api_get_datafiles(SETUP_CONFIG): - tab = api.get_datafiles(targetid=2, filt='all') - print(tab) - assert isinstance(tab, list) - assert len(tab) > 0 - for fid in tab: - assert isinstance(fid, int) - - fileid = tab[0] - tab = api.get_datafile(fileid) - print(tab) - assert tab['fileid'] == fileid - assert tab['targetid'] == 2 - - -# -------------------------------------------------------------------------------------------------- -def test_api_get_filters(SETUP_CONFIG): - tab = api.get_filters() - print(tab) - assert isinstance(tab, dict) - for key, value in tab.items(): - assert isinstance(value, dict) - assert value['photfilter'] == key - assert 'wavelength_center' in value - - -# -------------------------------------------------------------------------------------------------- -def test_api_get_sites(SETUP_CONFIG): - tab = api.get_all_sites() - print(tab) - assert isinstance(tab, list) - assert len(tab) > 0 - for site in tab: - assert isinstance(site, dict) - assert isinstance(site['siteid'], int) - assert 'sitename' in site - assert isinstance(site['EarthLocation'], EarthLocation) - - site0 = tab[0] - print(site0) - tab = api.get_site(site0['siteid']) - print(tab) - assert isinstance(tab, dict) - assert tab == site0 - - -# -------------------------------------------------------------------------------------------------- -def test_api_get_catalog(SETUP_CONFIG): - cat = api.get_catalog(2, output='table') - print(cat) - - assert isinstance(cat, dict) - - target = cat['target'] - assert isinstance(target, Table) - assert len(target) == 1 - assert target['targetid'] == 2 - assert target['target_name'] == '2019yvr' - - ref = cat['references'] - assert isinstance(ref, Table) - - avoid = cat['avoid'] - assert isinstance(avoid, Table) - - -# -------------------------------------------------------------------------------------------------- -def test_api_get_lightcurve(SETUP_CONFIG): - tab = api.get_lightcurve(2) - print(tab) - - assert isinstance(tab, Table) - assert len(tab) > 0 - assert 'time' in tab.colnames - assert 'mag_raw' in tab.colnames - - -# -------------------------------------------------------------------------------------------------- -def test_api_get_photometry(SETUP_CONFIG): - with tempfile.TemporaryDirectory() as tmpdir: - # Set cache to the temporary directory: - # FIXME: There is a potential race condition here! - config = load_config() - config.set('api', 'photometry_cache', tmpdir) - print(config) - - # The cache file should NOT exists: - assert not os.path.isfile(os.path.join(tmpdir, 'photometry-499.ecsv')), "Cache file already exists" - - # Download a photometry from API: - tab = api.get_photometry(499) - print(tab) - - # Basic tests of table: - assert isinstance(tab, Table) - assert len(tab) > 0 - assert 'starid' in tab.colnames - assert 'ra' in tab.colnames - assert 'decl' in tab.colnames - assert 'mag' in tab.colnames - assert 'mag_error' in tab.colnames - assert np.sum(tab['starid'] == 0) == 1, "There should be one starid=0" - - # Meta-information: - assert tab.meta['targetid'] == 2 - assert tab.meta['fileid'] == 179 - assert tab.meta['photfilter'] == 'B' - - # The cache file should now exists: - assert os.path.isfile(os.path.join(tmpdir, 'photometry-499.ecsv')), "Cache file does not exist" - - # Asking for the same photometry should now load from cache: - tab2 = api.get_photometry(499) - print(tab2) - - # The two tables should be identical: - assert tab2.meta == tab.meta - assert tab2.colnames == tab.colnames - for col in tab.colnames: - np.testing.assert_allclose(tab2[col], tab[col], equal_nan=True) - - -# -------------------------------------------------------------------------------------------------- -if __name__ == '__main__': - pytest.main([__file__]) diff --git a/tests/test_load_image.py b/tests/test_load_image.py index 2d11c21..53aafb3 100644 --- a/tests/test_load_image.py +++ b/tests/test_load_image.py @@ -1,9 +1,5 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ Test loading of images. - -.. codeauthor:: Rasmus Handberg """ import pytest @@ -13,7 +9,7 @@ from astropy.coordinates import SkyCoord import os.path import conftest # noqa: F401 -from flows.api import get_filters +from tendrils import api from flows.load_image import load_image @@ -30,7 +26,7 @@ ]) def test_load_image(fpath, siteid): # Get list of all available filters: - all_filters = set(get_filters().keys()) + all_filters = set(api.get_filters().keys()) # The test input directory containing the test-images: INPUT_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'input') diff --git a/tests/test_ztf.py b/tests/test_ztf.py index 0c1621f..61ae68d 100644 --- a/tests/test_ztf.py +++ b/tests/test_ztf.py @@ -14,7 +14,7 @@ import tempfile import os from conftest import capture_cli -from flows import ztf +from tendrils.utils import ztf # --------------------------------------------------------------------------------------------------