Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tickets/PREOPS-4685: use lsst.resources for loading data in the prenight briefing #59

Merged
merged 6 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions .github/workflows/build_container.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ jobs:
run: |
mamba install --quiet --file=requirements.txt
mamba install --quiet --file=test-requirements.txt
pip install lsst.resources
mamba list rubin-scheduler | grep -v "#" | awk '{print $2}' > ${{ github.workspace }}/rs_version
echo "rs-version" `cat ${{ github.workspace }}/rs_version`
echo "rs-version=`cat ${{ github.workspace }}/rs_version`" >> $GITHUB_OUTPUT
echo "rs-version=`cat ${{ github.workspace }}/rs_version`" >> $GITHUB_OUTPUT

- name: Access rubin-sched-data cache
id: cache-rs
Expand Down Expand Up @@ -77,10 +78,3 @@ jobs:
echo Pushed ghcr.io/${{ github.repository }}:${{ steps.build.outputs.tag }}
echo Fully qualified image digest: ${{ steps.build.outputs.fully_qualified_image_digest }}
echo Tag of the image: ${{ steps.build.outputs.tag }}







5 changes: 3 additions & 2 deletions .github/workflows/test_and_build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ jobs:
run: |
mamba install --quiet --file=requirements.txt
mamba install --quiet --file=test-requirements.txt
pip install lsst.resources
mamba list rubin-scheduler | grep -v "#" | awk '{print $2}' > ${{ github.workspace }}/rs_version
echo "rs-version" `cat ${{ github.workspace }}/rs_version`
echo "rs-version=`cat ${{ github.workspace }}/rs_version`" >> $GITHUB_OUTPUT
echo "rs-version=`cat ${{ github.workspace }}/rs_version`" >> $GITHUB_OUTPUT

- name: Access rubin-sched-data cache
id: cache-rs
Expand Down Expand Up @@ -115,4 +116,4 @@ jobs:
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.SP_PYPI_UPLOADS }}
password: ${{ secrets.SP_PYPI_UPLOADS }}
4 changes: 3 additions & 1 deletion container_environment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ dependencies:
- uranography
- param
- git
- pip
- wget
- pip
- pip:
- lsst.resources
3 changes: 3 additions & 0 deletions environment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ dependencies:
- firefox
- geckodriver
- build
- pip
- pip:
- lsst.resources
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dependencies = [
"param",
"pytz",
"rubin-scheduler",
"lsst.resources",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right that lsst.resources will install from pypi, but I'm guessing that for conda-forge it will have to be lsst-resources (and I'd like to ask for a conda-forge package).
And also, lsst-resources does work for pypi as well, so maybe it'd be good to just simply use "lsst-resources" instead of lsst.resources in one place and lsst-resources in another?
(and I also agree that for pyproject.toml it makes no difference, given how pip checks .. it's entirely for looking 'the same').

"uranography >= 1.1.0 ",
]

Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ param
pytz
uranography
rubin-scheduler
pip
88 changes: 51 additions & 37 deletions schedview/app/prenight/prenight.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import argparse
import importlib.resources
import json
import logging
import os
import sys
from glob import glob
import urllib.parse

Check warning on line 6 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L6

Added line #L6 was not covered by tests
from pathlib import Path

import astropy.utils.iers
Expand All @@ -15,11 +14,13 @@
import panel as pn
import param
from astropy.time import Time
from lsst.resources import ResourcePath

Check warning on line 17 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L17

Added line #L17 was not covered by tests
from rubin_scheduler.scheduler.model_observatory import ModelObservatory
from rubin_scheduler.utils import survey_start_mjd

import schedview.collect.footprint
import schedview.collect.opsim
import schedview.collect.resources

Check warning on line 23 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L23

Added line #L23 was not covered by tests
import schedview.compute.astro
import schedview.compute.scheduler
import schedview.param
Expand All @@ -42,8 +43,11 @@
DEFAULT_MODEL_OBSERVATORY = ModelObservatory(init_load_length=1)
DEFAULT_MODEL_OBSERVATORY.sky_model.load_length = 1

PACKAGE_DATA_DIR = importlib.resources.files("schedview.data").as_posix()
USDF_DATA_DIR = "/sdf/group/rubin/web_data/sim-data/schedview"
PACKAGE_RESOURCE_URI = "resource://schedview/data"
USDF_RESOURCE_URI = "file:///sdf/group/rubin/web_data/sim-data/schedview"

Check warning on line 47 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L46-L47

Added lines #L46 - L47 were not covered by tests

# To be changed to an S3 bucket at the USDF, when it's ready
DEFAULT_RESOURCE_URI = USDF_RESOURCE_URI

Check warning on line 50 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L50

Added line #L50 was not covered by tests

astropy.utils.iers.conf.iers_degraded_accuracy = "warn"

Expand Down Expand Up @@ -278,8 +282,8 @@

self.logger.info("Starting to update visits.")
try:
if not os.path.exists(self.opsim_output_fname):
raise FileNotFoundError(f"File not found: {self.opsim_output_fname}")
if not ResourcePath(self.opsim_output_fname).exists():
raise FileNotFoundError(f"Resource not found: {self.opsim_output_fname}")

Check warning on line 286 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L286

Added line #L286 was not covered by tests

visits = schedview.collect.opsim.read_opsim(
self.opsim_output_fname,
Expand Down Expand Up @@ -523,7 +527,13 @@
self.logger.info("Starting to update reward dataframe.")

try:
reward_df = pd.read_hdf(self.rewards_fname, "reward_df")
reward_resource = ResourcePath(self.rewards_fname)

Check warning on line 530 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L530

Added line #L530 was not covered by tests
if not reward_resource.exists():
raise FileNotFoundError(f"Resource not found: {self.rewards_fname}")

Check warning on line 532 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L532

Added line #L532 was not covered by tests

with reward_resource.as_local() as local_resource:
local_fname = Path(urllib.parse.urlparse(str(local_resource)).path)
reward_df = pd.read_hdf(local_fname, "reward_df")

Check warning on line 536 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L535-L536

Added lines #L535 - L536 were not covered by tests
self.logger.info("Finished updating reward dataframe.")
except Exception as e:
self.logger.error(e)
Expand Down Expand Up @@ -584,7 +594,14 @@

self.logger.info("Starting to update obs_rewards.")
try:
obs_rewards = pd.read_hdf(self.rewards_fname, "obs_rewards")
reward_resource = ResourcePath(self.rewards_fname)

Check warning on line 597 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L597

Added line #L597 was not covered by tests
if not reward_resource.exists():
raise FileNotFoundError(f"Resource not found: {self.rewards_fname}")

Check warning on line 599 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L599

Added line #L599 was not covered by tests

with reward_resource.as_local() as local_resource:
local_fname = Path(urllib.parse.urlparse(str(local_resource)).path)
obs_rewards = pd.read_hdf(local_fname, "obs_rewards")

Check warning on line 603 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L602-L603

Added lines #L602 - L603 were not covered by tests

self._obs_rewards = obs_rewards
self.logger.info("Finished updating obs_rewards.")
except Exception as e:
Expand Down Expand Up @@ -820,15 +837,13 @@
class RestrictedInputPrenight(Prenight):
"""A pre-night dashboard that restricts the data to files in a dir."""

opsim_output_fname = schedview.param.FileSelectorWithEmptyOption(
path=f"{PACKAGE_DATA_DIR}/*opsim*.db", label="OpSim output database", default=None, allow_None=True
opsim_output_fname = param.Selector(

Check warning on line 840 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L840

Added line #L840 was not covered by tests
objects=[], label="OpSim output database", default=None, allow_None=True
)

rewards_fname = schedview.param.FileSelectorWithEmptyOption(
path=f"{PACKAGE_DATA_DIR}/*rewards*.h5", label="rewards HDF5 file", default=None, allow_None=True
)
rewards_fname = param.Selector(objects=[], label="rewards HDF5 file", default=None, allow_None=True)

Check warning on line 844 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L844

Added line #L844 was not covered by tests

def __init__(self, data_dir=None, **kwargs):
def __init__(self, resource_uri=DEFAULT_RESOURCE_URI, **kwargs):

Check warning on line 846 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L846

Added line #L846 was not covered by tests
# A few arguments (opsim_db, rewards) will be used
# later in this method to set the options for parameters, but
# are not themselves parameters. So, remove them them the
Expand All @@ -841,24 +856,24 @@
# they can be updated by key.
fname_params = {
"opsim_db": self.param["opsim_output_fname"],
"reward": self.param["rewards_fname"],
"rewards": self.param["rewards_fname"],
}

# In cases where the caller has not specified a value, set
# the paths to a glob matching the expected file name format
# for each type.
if data_dir is not None:
fname_glob = {
"opsim_db": f"{data_dir}/*opsim*.db",
"reward": f"{data_dir}/*rewards*.h5",
}
fname_patterns = {

Check warning on line 862 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L862

Added line #L862 was not covered by tests
"opsim_db": r".*opsim.*\.db",
"rewards": r".*rewards.*\.h5",
}

# Actually assign the names or globs to the path references.
# Get the resources available for each file type
for arg_name in fname_params:
if arg_name in kwargs:
fname_params[arg_name].update(path=kwargs[arg_name])
elif data_dir is not None:
fname_params[arg_name].update(path=fname_glob[arg_name])
matching_resources = [kwargs[arg_name]]

Check warning on line 870 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L870

Added line #L870 was not covered by tests
else:
matching_resources = schedview.collect.resources.find_file_resources(

Check warning on line 872 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L872

Added line #L872 was not covered by tests
resource_uri, file_filter=fname_patterns[arg_name]
)
matching_resources = [None] + matching_resources
fname_params[arg_name].objects = matching_resources

Check warning on line 876 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L875-L876

Added lines #L875 - L876 were not covered by tests


def prenight_app(*args, **kwargs):
Expand All @@ -874,9 +889,9 @@
prenight = Prenight()
else:
try:
data_dir = kwargs["data_dir"]
resource_uri = kwargs["resource_uri"]

Check warning on line 892 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L892

Added line #L892 was not covered by tests
except KeyError:
data_dir = None
resource_uri = None

Check warning on line 894 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L894

Added line #L894 was not covered by tests

specified_data_files = {}
data_args = set(["opsim_db", "rewards"]) & set(kwargs.keys())
Expand All @@ -887,10 +902,10 @@
if data_arg in kwargs:
specified_data_files[data_arg] = str(file_path)

prenight = RestrictedInputPrenight(data_dir=data_dir, **specified_data_files)
prenight = RestrictedInputPrenight(resource_uri=resource_uri, **specified_data_files)

Check warning on line 905 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L905

Added line #L905 was not covered by tests

try:
del kwargs["data_dir"]
del kwargs["resource_uri"]

Check warning on line 908 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L908

Added line #L908 was not covered by tests
except KeyError:
pass

Expand Down Expand Up @@ -928,13 +943,12 @@
help="The path to the rewards HDF5 file.",
)

default_data_dir = f"{USDF_DATA_DIR}/*" if os.path.exists(USDF_DATA_DIR) else PACKAGE_DATA_DIR
parser.add_argument(
"--data_dir",
"--resource_uri",
"-d",
type=str,
default=default_data_dir,
help="The base directory for data files.",
default=DEFAULT_RESOURCE_URI,
help="The base URI for data files.",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this still be just a directory on disk? (if so, might be helpful to say something like that .. "The base URI for data file. This could be the S3 bucket or a directory on disk." ??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will definitely take a string like file://home/someuser/data/foo. That's one of the ways I've been testing it. I think it may also take /home/someuser/data/foo, but I'm not sure.

)

parser.add_argument(
Expand Down Expand Up @@ -987,8 +1001,8 @@

args = parser.parse_args()

if len(glob(args.data_dir)) == 0 and not args.data_from_urls:
args.data_dir = PACKAGE_DATA_DIR
if not ResourcePath(args.resource_uri).exists():
args.resource_uri = PACKAGE_RESOURCE_URI

Check warning on line 1005 in schedview/app/prenight/prenight.py

View check run for this annotation

Codecov / codecov/patch

schedview/app/prenight/prenight.py#L1005

Added line #L1005 was not covered by tests

if args.night is not None:
args.night_date = Time(pd.Timestamp(args.night, tz="UTC")).datetime.date()
Expand Down
24 changes: 15 additions & 9 deletions schedview/collect/opsim.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import sqlite3
import urllib

Check warning on line 2 in schedview/collect/opsim.py

View check run for this annotation

Codecov / codecov/patch

schedview/collect/opsim.py#L2

Added line #L2 was not covered by tests

import numpy as np
import pandas as pd
from astropy.time import Time
from lsst.resources import ResourcePath

Check warning on line 7 in schedview/collect/opsim.py

View check run for this annotation

Codecov / codecov/patch

schedview/collect/opsim.py#L7

Added line #L7 was not covered by tests


def read_opsim(filename, start_time="2000-01-01", end_time="2100-01-01"):
def read_opsim(opsim_uri, start_time="2000-01-01", end_time="2100-01-01"):

Check warning on line 10 in schedview/collect/opsim.py

View check run for this annotation

Codecov / codecov/patch

schedview/collect/opsim.py#L10

Added line #L10 was not covered by tests
"""Read visits from an opsim database.

Parameters
----------
filename : `str`
The file from which to load visits
opsim_uri : `str`
The uri from which to load visits
start_time : `str`, `astropy.time.Time`
The start time for visits to be loaded
end_time : `str`, `astropy.time.Time`
Expand All @@ -25,12 +27,16 @@
start_mjd = Time(start_time).mjd
end_mjd = Time(end_time).mjd

with sqlite3.connect(filename) as sim_connection:
visits = pd.read_sql_query(
f"SELECT * FROM observations WHERE observationStartMJD BETWEEN {start_mjd} AND {end_mjd}",
sim_connection,
index_col="observationId",
)
original_resource_path = ResourcePath(opsim_uri)

Check warning on line 30 in schedview/collect/opsim.py

View check run for this annotation

Codecov / codecov/patch

schedview/collect/opsim.py#L30

Added line #L30 was not covered by tests
with original_resource_path.as_local() as local_resource_path:
filename = urllib.parse.urlparse(str(local_resource_path)).path

Check warning on line 32 in schedview/collect/opsim.py

View check run for this annotation

Codecov / codecov/patch

schedview/collect/opsim.py#L32

Added line #L32 was not covered by tests

with sqlite3.connect(filename) as sim_connection:
visits = pd.read_sql_query(

Check warning on line 35 in schedview/collect/opsim.py

View check run for this annotation

Codecov / codecov/patch

schedview/collect/opsim.py#L35

Added line #L35 was not covered by tests
f"SELECT * FROM observations WHERE observationStartMJD BETWEEN {start_mjd} AND {end_mjd}",
sim_connection,
index_col="observationId",
)

visits["start_date"] = pd.to_datetime(
visits["observationStartMJD"] + 2400000.5, origin="julian", unit="D", utc=True
Expand Down
27 changes: 27 additions & 0 deletions schedview/collect/resources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from lsst.resources import ResourcePath

Check warning on line 1 in schedview/collect/resources.py

View check run for this annotation

Codecov / codecov/patch

schedview/collect/resources.py#L1

Added line #L1 was not covered by tests


def find_file_resources(base_resource_uri, file_filter=None):

Check warning on line 4 in schedview/collect/resources.py

View check run for this annotation

Codecov / codecov/patch

schedview/collect/resources.py#L4

Added line #L4 was not covered by tests
"""Find matching files in a resource.

Parameters
----------
base_resource_uri : `str`
The uri of the resource to search
file_filter : `str` or `re.Pattern`, optional
Regex to filter out files from the list before it is returned.

Returns
-------
files : `list` of `str`
The list of matching files available at the resource.
"""
base_resource = ResourcePath(base_resource_uri)
accumulated_files = []

Check warning on line 20 in schedview/collect/resources.py

View check run for this annotation

Codecov / codecov/patch

schedview/collect/resources.py#L19-L20

Added lines #L19 - L20 were not covered by tests
for dir_path, dir_names, file_names in base_resource.walk(file_filter=file_filter):
for file_name in file_names:
qualified_file_name = dir_path.join(file_name).geturl()

Check warning on line 23 in schedview/collect/resources.py

View check run for this annotation

Codecov / codecov/patch

schedview/collect/resources.py#L23

Added line #L23 was not covered by tests
if qualified_file_name not in accumulated_files:
accumulated_files.append(qualified_file_name)

Check warning on line 25 in schedview/collect/resources.py

View check run for this annotation

Codecov / codecov/patch

schedview/collect/resources.py#L25

Added line #L25 was not covered by tests

return accumulated_files

Check warning on line 27 in schedview/collect/resources.py

View check run for this annotation

Codecov / codecov/patch

schedview/collect/resources.py#L27

Added line #L27 was not covered by tests
25 changes: 25 additions & 0 deletions tests/test_resources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import unittest
from pathlib import Path
from tempfile import TemporaryDirectory

from schedview.collect.resources import find_file_resources


class TestResources(unittest.TestCase):
def test_find_file_resources(self):
# Generate some test files
test_file_names = ["foo/bar.txt", "foo/baz.txt", "foo/qux/moo.txt"]
made_files = []
with TemporaryDirectory() as temp_dir_name:
temp_dir = Path(temp_dir_name)
for file_name in test_file_names:
file_path = temp_dir.joinpath(file_name)
file_path.parent.mkdir(parents=True, exist_ok=True)
made_files.append(file_path.as_uri())
with open(file_path, "w") as file_io:
file_io.write("Test content.")

# Verify that we found exactly the files we made
found_files = find_file_resources(temp_dir)

assert set(made_files) == set(found_files)
Loading