diff --git a/notebooks/1_too_models.ipynb b/notebooks/1_too_models.ipynb deleted file mode 100644 index 8096ce2..0000000 --- a/notebooks/1_too_models.ipynb +++ /dev/null @@ -1,663 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "d331f783", - "metadata": {}, - "outputs": [], - "source": [ - "import logging\n", - "from winterapi import WinterAPI\n", - "from wintertoo.models import SummerFieldToO, SummerRaDecToO, WinterFieldToO, WinterRaDecToO" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "68657fec", - "metadata": {}, - "outputs": [], - "source": [ - "logging.getLogger(\"winterapi\").setLevel(\"DEBUG\")" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "75c8a14e", - "metadata": {}, - "outputs": [], - "source": [ - "winter = WinterAPI()" - ] - }, - { - "cell_type": "markdown", - "id": "bd4d474c", - "metadata": {}, - "source": [ - "Our credentials from the previous notebook will still be active!" - ] - }, - { - "cell_type": "markdown", - "id": "eb5f0374", - "metadata": {}, - "source": [ - "# Setting up a ToO" - ] - }, - { - "cell_type": "markdown", - "id": "e6a15b09", - "metadata": {}, - "source": [ - "Now we are going to set up a ToO. There are two ways to trigger a ToO:\n", - "* Using a ra/dec value\n", - "* Using a field value\n", - "\n", - "There are also two types of instruments to trigger: WINTER or SUMMER. \n", - "\n", - "That makes **4 different ToO objects to choose from*.\n", - "\n", - "Let's go through the procedure for a Summer ToO using a field." - ] - }, - { - "cell_type": "markdown", - "id": "504be7f4", - "metadata": {}, - "source": [ - "## Step 1: What can go into a ToO request?" - ] - }, - { - "cell_type": "markdown", - "id": "3a212a51", - "metadata": {}, - "source": [ - "For `winterapi`, we handle data using `pydantic`. We specify what types of data can be provided, what the conditions are, and what the default values are.\n", - "\n", - "You can see all options in a human-readable way by converting the data model to a json schema:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "15e67d82", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\n", - " \"title\": \"SummerFieldToO\",\n", - " \"description\": \"Summer ToO Request with field\",\n", - " \"type\": \"object\",\n", - " \"properties\": {\n", - " \"field_id\": {\n", - " \"title\": \"Field ID\",\n", - " \"minimum\": 1,\n", - " \"type\": \"integer\"\n", - " },\n", - " \"filters\": {\n", - " \"title\": \"Filters\",\n", - " \"default\": [\n", - " \"u\",\n", - " \"g\",\n", - " \"r\",\n", - " \"i\"\n", - " ],\n", - " \"type\": \"array\",\n", - " \"items\": {\n", - " \"enum\": [\n", - " \"u\",\n", - " \"g\",\n", - " \"r\",\n", - " \"i\"\n", - " ],\n", - " \"type\": \"string\"\n", - " }\n", - " },\n", - " \"target_priority\": {\n", - " \"title\": \"Priority for target\",\n", - " \"default\": 50.0,\n", - " \"minimum\": 0.0,\n", - " \"type\": \"number\"\n", - " },\n", - " \"t_exp\": {\n", - " \"title\": \"Individual exposure time (s)\",\n", - " \"default\": 30.0,\n", - " \"minimum\": 1.0,\n", - " \"maximum\": 300,\n", - " \"type\": \"number\"\n", - " },\n", - " \"n_exp\": {\n", - " \"title\": \"Number of dither sets\",\n", - " \"default\": 1,\n", - " \"minimum\": 1,\n", - " \"type\": \"integer\"\n", - " },\n", - " \"n_dither\": {\n", - " \"title\": \"Number of dithers\",\n", - " \"default\": 1,\n", - " \"minimum\": 1,\n", - " \"type\": \"integer\"\n", - " },\n", - " \"dither_distance\": {\n", - " \"title\": \"dither distance (arcsec)\",\n", - " \"default\": 15.0,\n", - " \"minimum\": 0.0,\n", - " \"type\": \"number\"\n", - " },\n", - " \"start_time_mjd\": {\n", - " \"title\": \"ToO validity start (MJD)\",\n", - " \"default\": 60103.72062578728,\n", - " \"type\": \"number\"\n", - " },\n", - " \"end_time_mjd\": {\n", - " \"title\": \"ToO validity end (MJD)\",\n", - " \"default\": 60104.71062579434,\n", - " \"minimum\": 60103.71062579612,\n", - " \"type\": \"number\"\n", - " },\n", - " \"max_airmass\": {\n", - " \"title\": \"Allowed airmass range\",\n", - " \"default\": 2.0,\n", - " \"minimum\": 1,\n", - " \"maximum\": 5,\n", - " \"type\": \"number\"\n", - " }\n", - " },\n", - " \"required\": [\n", - " \"field_id\"\n", - " ],\n", - " \"additionalProperties\": false\n", - "}\n" - ] - } - ], - "source": [ - "print(SummerFieldToO.schema_json(indent=1))" - ] - }, - { - "cell_type": "markdown", - "id": "56fceaf1", - "metadata": {}, - "source": [ - "There is a lot of information, but the most important line is right at the bottom. The only thing that is required is `field_id`. Everything else is optional.\n", - "\n", - "We can initialise the ToO using a random field number:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "3dcfcdae", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SummerFieldToO(field_id=55, filters=['u', 'g', 'r', 'i'], target_priority=50.0, t_exp=30.0, n_exp=1, n_dither=1, dither_distance=15.0, start_time_mjd=60103.72062578728, end_time_mjd=60104.71062579434, max_airmass=2.0)" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "too = SummerFieldToO(field_id=55)\n", - "too" - ] - }, - { - "cell_type": "markdown", - "id": "43a49362", - "metadata": {}, - "source": [ - "As you can see, we got a full ToO request with sensible defaults. If we only wanted one filter rather than 4, we could specify this:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "6605d149", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SummerFieldToO(field_id=55, filters=['u'], target_priority=50.0, t_exp=30.0, n_exp=1, n_dither=1, dither_distance=15.0, start_time_mjd=60103.72062578728, end_time_mjd=60104.71062579434, max_airmass=2.0)" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "too = SummerFieldToO(field_id=55, filters=[\"u\"])\n", - "too" - ] - }, - { - "cell_type": "markdown", - "id": "54ef13e4", - "metadata": {}, - "source": [ - "Pydantic also validates the data we enter, to make sure things are sensible. Here are some examples of things you can't do:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "217812b9", - "metadata": {}, - "outputs": [ - { - "ename": "ValidationError", - "evalue": "1 validation error for SummerFieldToO\nfilters -> 0\n unexpected value; permitted: 'u', 'g', 'r', 'i' (type=value_error.const; given=V; permitted=('u', 'g', 'r', 'i'))", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValidationError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[7], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# Names a filter that isn't avbailable\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m too \u001b[38;5;241m=\u001b[39m \u001b[43mSummerFieldToO\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfield_id\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m55\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfilters\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mV\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3\u001b[0m too\n", - "File \u001b[0;32m~/anaconda3/envs/winterapi/lib/python3.11/site-packages/pydantic/main.py:341\u001b[0m, in \u001b[0;36mpydantic.main.BaseModel.__init__\u001b[0;34m()\u001b[0m\n", - "\u001b[0;31mValidationError\u001b[0m: 1 validation error for SummerFieldToO\nfilters -> 0\n unexpected value; permitted: 'u', 'g', 'r', 'i' (type=value_error.const; given=V; permitted=('u', 'g', 'r', 'i'))" - ] - } - ], - "source": [ - "# Names a filter that isn't avbailable\n", - "too = SummerFieldToO(field_id=55, filters=[\"V\"])\n", - "too" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "00b582dd", - "metadata": {}, - "outputs": [ - { - "ename": "ValidationError", - "evalue": "1 validation error for SummerFieldToO\nend_time_mjd\n ensure this value is greater than or equal to 60103.71062579612 (type=value_error.number.not_ge; limit_value=60103.71062579612)", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValidationError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[8], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# end time in the past\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m too \u001b[38;5;241m=\u001b[39m \u001b[43mSummerFieldToO\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfield_id\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m55\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mend_time_mjd\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m58000.\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3\u001b[0m too\n", - "File \u001b[0;32m~/anaconda3/envs/winterapi/lib/python3.11/site-packages/pydantic/main.py:341\u001b[0m, in \u001b[0;36mpydantic.main.BaseModel.__init__\u001b[0;34m()\u001b[0m\n", - "\u001b[0;31mValidationError\u001b[0m: 1 validation error for SummerFieldToO\nend_time_mjd\n ensure this value is greater than or equal to 60103.71062579612 (type=value_error.number.not_ge; limit_value=60103.71062579612)" - ] - } - ], - "source": [ - "# end time in the past\n", - "too = SummerFieldToO(field_id=55, end_time_mjd=58000.)\n", - "too" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "7133e738", - "metadata": {}, - "outputs": [ - { - "ename": "ValidationError", - "evalue": "1 validation error for SummerFieldToO\nn_dither\n ensure this value is greater than or equal to 1 (type=value_error.number.not_ge; limit_value=1)", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValidationError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[9], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# Chooses a nonsensical n_dither number\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m too \u001b[38;5;241m=\u001b[39m \u001b[43mSummerFieldToO\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfield_id\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m55\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn_dither\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3\u001b[0m too\n", - "File \u001b[0;32m~/anaconda3/envs/winterapi/lib/python3.11/site-packages/pydantic/main.py:341\u001b[0m, in \u001b[0;36mpydantic.main.BaseModel.__init__\u001b[0;34m()\u001b[0m\n", - "\u001b[0;31mValidationError\u001b[0m: 1 validation error for SummerFieldToO\nn_dither\n ensure this value is greater than or equal to 1 (type=value_error.number.not_ge; limit_value=1)" - ] - } - ], - "source": [ - "# Chooses a nonsensical n_dither number\n", - "too = SummerFieldToO(field_id=55, n_dither=0)\n", - "too" - ] - }, - { - "cell_type": "markdown", - "id": "1371556c", - "metadata": {}, - "source": [ - "## Step 2 Choosing a ToO using ra/dec" - ] - }, - { - "cell_type": "markdown", - "id": "7614d0d0", - "metadata": {}, - "source": [ - "If you know the field to observe, then you have all the information you need, because each grid has a unique ra/dec value for that field.\n", - "The API will look that value up, so you do not need to worry about it.\n", - "\n", - "However, more often, if you are performing a ToO, it's because you know the ra/dec of a specific object.\n", - "\n", - "In that case, we can use a Ra/Dec model. Here's one with Summer:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "8f56c276", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\n", - " \"title\": \"SummerRaDecToO\",\n", - " \"description\": \"Summer ToO Request with Ra/Dec\",\n", - " \"type\": \"object\",\n", - " \"properties\": {\n", - " \"ra_deg\": {\n", - " \"title\": \"Right ascension in decimal degrees\",\n", - " \"minimum\": 0.0,\n", - " \"maximum\": 360.0,\n", - " \"example\": 180.0,\n", - " \"type\": \"number\"\n", - " },\n", - " \"dec_deg\": {\n", - " \"title\": \"Declination in decimal degrees\",\n", - " \"minimum\": -90.0,\n", - " \"maximum\": 90.0,\n", - " \"example\": 0.0,\n", - " \"type\": \"number\"\n", - " },\n", - " \"use_field_grid\": {\n", - " \"title\": \"boolean whether to select nearest field in grid for central ra/dec\",\n", - " \"default\": true,\n", - " \"type\": \"boolean\"\n", - " },\n", - " \"filters\": {\n", - " \"title\": \"Filters\",\n", - " \"default\": [\n", - " \"u\",\n", - " \"g\",\n", - " \"r\",\n", - " \"i\"\n", - " ],\n", - " \"type\": \"array\",\n", - " \"items\": {\n", - " \"enum\": [\n", - " \"u\",\n", - " \"g\",\n", - " \"r\",\n", - " \"i\"\n", - " ],\n", - " \"type\": \"string\"\n", - " }\n", - " },\n", - " \"target_priority\": {\n", - " \"title\": \"Priority for target\",\n", - " \"default\": 50.0,\n", - " \"minimum\": 0.0,\n", - " \"type\": \"number\"\n", - " },\n", - " \"t_exp\": {\n", - " \"title\": \"Individual exposure time (s)\",\n", - " \"default\": 30.0,\n", - " \"minimum\": 1.0,\n", - " \"maximum\": 300,\n", - " \"type\": \"number\"\n", - " },\n", - " \"n_exp\": {\n", - " \"title\": \"Number of dither sets\",\n", - " \"default\": 1,\n", - " \"minimum\": 1,\n", - " \"type\": \"integer\"\n", - " },\n", - " \"n_dither\": {\n", - " \"title\": \"Number of dithers\",\n", - " \"default\": 1,\n", - " \"minimum\": 1,\n", - " \"type\": \"integer\"\n", - " },\n", - " \"dither_distance\": {\n", - " \"title\": \"dither distance (arcsec)\",\n", - " \"default\": 15.0,\n", - " \"minimum\": 0.0,\n", - " \"type\": \"number\"\n", - " },\n", - " \"start_time_mjd\": {\n", - " \"title\": \"ToO validity start (MJD)\",\n", - " \"default\": 60103.72062578728,\n", - " \"type\": \"number\"\n", - " },\n", - " \"end_time_mjd\": {\n", - " \"title\": \"ToO validity end (MJD)\",\n", - " \"default\": 60104.71062579434,\n", - " \"minimum\": 60103.71062579612,\n", - " \"type\": \"number\"\n", - " },\n", - " \"max_airmass\": {\n", - " \"title\": \"Allowed airmass range\",\n", - " \"default\": 2.0,\n", - " \"minimum\": 1,\n", - " \"maximum\": 5,\n", - " \"type\": \"number\"\n", - " }\n", - " },\n", - " \"required\": [\n", - " \"ra_deg\",\n", - " \"dec_deg\"\n", - " ],\n", - " \"additionalProperties\": false\n", - "}\n" - ] - } - ], - "source": [ - "print(SummerRaDecToO.schema_json(indent=1))" - ] - }, - { - "cell_type": "markdown", - "id": "cc4aba3a", - "metadata": {}, - "source": [ - "In this case, we only need to provide the ra and dec:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "266c0940", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SummerRaDecToO(ra_deg=300.0, dec_deg=51.0, use_field_grid=True, filters=['u', 'g', 'r', 'i'], target_priority=50.0, t_exp=30.0, n_exp=1, n_dither=1, dither_distance=15.0, start_time_mjd=60103.72062578728, end_time_mjd=60104.71062579434, max_airmass=2.0)" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "too = SummerRaDecToO(ra_deg=300., dec_deg=51.)\n", - "too" - ] - }, - { - "cell_type": "markdown", - "id": "5bdf48c2", - "metadata": {}, - "source": [ - "**One important note: by default, a schedule with an ra/dec will be matched to the nearest field, and the exposure will be centered on that field RA/dec**\n", - " \n", - "You can disable this behaviour by setting `use_field_grid` to false! " - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "42a4c48c", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SummerRaDecToO(ra_deg=300.0, dec_deg=51.0, use_field_grid=False, filters=['u', 'g', 'r', 'i'], target_priority=50.0, t_exp=30.0, n_exp=1, n_dither=1, dither_distance=15.0, start_time_mjd=60103.72062578728, end_time_mjd=60104.71062579434, max_airmass=2.0)" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "too = SummerRaDecToO(ra_deg=300., dec_deg=51., use_field_grid=False)\n", - "too" - ] - }, - { - "cell_type": "markdown", - "id": "13798041", - "metadata": {}, - "source": [ - "# Step 3: Choosing Summer or Winter" - ] - }, - { - "cell_type": "markdown", - "id": "5e192a5b", - "metadata": {}, - "source": [ - "Creating a Winter ToO is exactly the same as the examples above, though the validation rules are different because the telescopes are different:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "1a28ca82", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "WinterFieldToO(field_id=55, filters=['Y', 'J', 'Hs'], target_priority=50.0, t_exp=30.0, n_exp=1, n_dither=1, dither_distance=15.0, start_time_mjd=60103.72062578728, end_time_mjd=60104.71062579434, max_airmass=2.0)" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# A field ToO\n", - "too = WinterFieldToO(field_id=55)\n", - "too" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "337d7cec", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "WinterRaDecToO(ra_deg=300.0, dec_deg=51.0, use_field_grid=True, filters=['Y', 'J', 'Hs'], target_priority=50.0, t_exp=30.0, n_exp=1, n_dither=1, dither_distance=15.0, start_time_mjd=60103.72062578728, end_time_mjd=60104.71062579434, max_airmass=2.0)" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# A ra/dec ToO\n", - "too = WinterRaDecToO(ra_deg=300., dec_deg=51.)\n", - "too" - ] - }, - { - "cell_type": "markdown", - "id": "386d383e", - "metadata": {}, - "source": [ - "**Remember, WINTER has different filters!**" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "6c2e061d", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "field_id=55 filters=['u'] target_priority=50.0 t_exp=30.0 n_exp=1 n_dither=1 dither_distance=15.0 start_time_mjd=60103.72062578728 end_time_mjd=60104.71062579434 max_airmass=2.0\n" - ] - }, - { - "ename": "ValidationError", - "evalue": "1 validation error for WinterFieldToO\nfilters -> 0\n unexpected value; permitted: 'Y', 'J', 'Hs' (type=value_error.const; given=u; permitted=('Y', 'J', 'Hs'))", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValidationError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[15], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m too \u001b[38;5;241m=\u001b[39m SummerFieldToO(field_id\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m55\u001b[39m, filters\u001b[38;5;241m=\u001b[39m[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mu\u001b[39m\u001b[38;5;124m\"\u001b[39m])\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(too)\n\u001b[0;32m----> 3\u001b[0m too \u001b[38;5;241m=\u001b[39m \u001b[43mWinterFieldToO\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfield_id\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m55\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfilters\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mu\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28mprint\u001b[39m(too)\n", - "File \u001b[0;32m~/anaconda3/envs/winterapi/lib/python3.11/site-packages/pydantic/main.py:341\u001b[0m, in \u001b[0;36mpydantic.main.BaseModel.__init__\u001b[0;34m()\u001b[0m\n", - "\u001b[0;31mValidationError\u001b[0m: 1 validation error for WinterFieldToO\nfilters -> 0\n unexpected value; permitted: 'Y', 'J', 'Hs' (type=value_error.const; given=u; permitted=('Y', 'J', 'Hs'))" - ] - } - ], - "source": [ - "too = SummerFieldToO(field_id=55, filters=[\"u\"])\n", - "print(too)\n", - "too = WinterFieldToO(field_id=55, filters=[\"u\"])\n", - "print(too)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c7fec6d4", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "winterapi", - "language": "python", - "name": "winterapi" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/Example_1_Setting_Up_Credentials.ipynb b/notebooks/Example_1_Setting_Up_Credentials.ipynb new file mode 100644 index 0000000..1c9b031 --- /dev/null +++ b/notebooks/Example_1_Setting_Up_Credentials.ipynb @@ -0,0 +1,165 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "3fb54b95", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "import logging\n", + "from winterapi import WinterAPI\n", + "from winterapi.endpoints import BASE_URL" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "84869822", + "metadata": {}, + "outputs": [], + "source": [ + "logging.getLogger(\"winterapi\").setLevel(\"DEBUG\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "b7024bc4", + "metadata": {}, + "outputs": [], + "source": [ + "winter = WinterAPI()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "25197a54", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using server http://winter.caltech.edu:82\n", + "Pinging server: True\n" + ] + } + ], + "source": [ + "print(f\"Using server {BASE_URL}\") # Should show the URL of the API\n", + "print(f\"Pinging server: {winter.ping()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "88d1d907", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "User is rdstein@caltech.edu\n" + ] + } + ], + "source": [ + "try:\n", + " print(f\"User is {winter.get_user()}\")\n", + "except KeyError:\n", + " print(\"No user credentials found. Please add these first!\")\n", + " winter.add_user_details(overwrite=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f0dc0b3f", + "metadata": {}, + "outputs": [], + "source": [ + "# Uncomment to add new credentials\n", + "# winter.add_user_details(user=x, password=y, overwrite=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "64797375", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found the following saved programs: ['2024A001']\n" + ] + } + ], + "source": [ + "print(f\"Found the following saved programs: {winter.get_programs()}\")\n", + "if len(winter.get_programs()) == 0:\n", + " print(\"No programs found. Add one to get started:\")\n", + " winter.add_program(overwrite=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42f4d81b", + "metadata": {}, + "outputs": [], + "source": [ + "# Uncomment to add additional programs\n", + "winter.add_program(overwrite=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b16a0900", + "metadata": {}, + "outputs": [], + "source": [ + "# A fake local program I have created\n", + "winter.get_program_details(\"202XA000\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1023561f", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "winterapi", + "language": "python", + "name": "winterapi" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/Example_2_Submitting_a_ToO.ipynb b/notebooks/Example_2_Submitting_a_ToO.ipynb new file mode 100644 index 0000000..0ce1144 --- /dev/null +++ b/notebooks/Example_2_Submitting_a_ToO.ipynb @@ -0,0 +1,514 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "f91a536f", + "metadata": {}, + "outputs": [], + "source": [ + "import logging\n", + "from winterapi import WinterAPI\n", + "from wintertoo.models import WinterRaDecToO" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "5e92d4e2", + "metadata": {}, + "outputs": [], + "source": [ + "logging.getLogger(\"winterapi\").setLevel(\"DEBUG\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "ba9823d9", + "metadata": {}, + "outputs": [], + "source": [ + "winter = WinterAPI()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "c68cfda7", + "metadata": {}, + "outputs": [], + "source": [ + "# Program details\n", + "program = \"2024A001\"" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "513407c4", + "metadata": {}, + "outputs": [], + "source": [ + "# Target details\n", + "ra_deg = 249.1311789\n", + "dec_deg = 47.858962\n", + "# Target name is a useful field for querying data, so put something useful!\n", + "# If you use Skyportal, we strongly advise you to use that name\n", + "target_name = \"ZTF23aaxeacr\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "031d4780", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "WinterRaDecToO(ra_deg=249.1311789, dec_deg=47.858962, use_field_grid=False, filters=['Y', 'J'], target_priority=50.0, target_name='ZTF23aaxeacr', t_exp=960.0, n_exp=1, n_dither=8, dither_distance=30.0, start_time_mjd=60346.16884958786, end_time_mjd=60347.15884959191, max_airmass=2.0)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# A ToO with the same dither combo for Y/J\n", + "too_1 = WinterRaDecToO(ra_deg=ra_deg, dec_deg=dec_deg, n_dither=8, t_exp=960., filters=['Y', 'J'], target_name=target_name)\n", + "too_1" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "7c3daafe", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "WinterRaDecToO(ra_deg=249.1311789, dec_deg=47.858962, use_field_grid=False, filters=['Hs'], target_priority=50.0, target_name='ZTF23aaxeacr', t_exp=960.0, n_exp=1, n_dither=15, dither_distance=30.0, start_time_mjd=60346.16884958786, end_time_mjd=60347.15884959191, max_airmass=2.0)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# A second ToO, with a different dither number for Hs\n", + "too_2 = WinterRaDecToO(ra_deg=ra_deg, dec_deg=dec_deg, n_dither=15, t_exp=960., filters=[\"Hs\"], target_name=target_name)\n", + "too_2" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "b7be6f9f", + "metadata": {}, + "outputs": [], + "source": [ + "too_list = [too_1, too_2]" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "555800e8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Available programs: ['2024A001', '2023A000', '2023A001', '2023A002']\n" + ] + } + ], + "source": [ + "program_list = winter.get_programs()\n", + "print(f\"Available programs: {program_list}\")\n", + "assert program in program_list, f\"program {program} not found! Add this program first.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "ec20aa42", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
targNameraDegdecDegfieldIDfiltervisitExpTimepriorityprogPIprogNameprogIDvalidStartvalidStopobservedmaxAirmassditherNumberditherStepSizeobsHistID
0ZTF23aaxeacr249.13117947.858962999999999Y960.050.0RStein2024A001360346.1688560347.15885False2.0830.00
1ZTF23aaxeacr249.13117947.858962999999999J960.050.0RStein2024A001360346.1688560347.15885False2.0830.01
2ZTF23aaxeacr249.13117947.858962999999999Hs960.050.0RStein2024A001360346.1688560347.15885False2.01530.02
\n", + "
" + ], + "text/plain": [ + " targName raDeg decDeg fieldID filter visitExpTime \\\n", + "0 ZTF23aaxeacr 249.131179 47.858962 999999999 Y 960.0 \n", + "1 ZTF23aaxeacr 249.131179 47.858962 999999999 J 960.0 \n", + "2 ZTF23aaxeacr 249.131179 47.858962 999999999 Hs 960.0 \n", + "\n", + " priority progPI progName progID validStart validStop observed \\\n", + "0 50.0 RStein 2024A001 3 60346.16885 60347.15885 False \n", + "1 50.0 RStein 2024A001 3 60346.16885 60347.15885 False \n", + "2 50.0 RStein 2024A001 3 60346.16885 60347.15885 False \n", + "\n", + " maxAirmass ditherNumber ditherStepSize obsHistID \n", + "0 2.0 8 30.0 0 \n", + "1 2.0 8 30.0 1 \n", + "2 2.0 15 30.0 2 " + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Schedule has three observations in total\n", + "local_schedule = winter.build_schedule_locally(\n", + " program_name=program,\n", + " data=too_list\n", + ")\n", + "local_schedule" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "2a6462c6", + "metadata": {}, + "outputs": [], + "source": [ + "# If submit_trigger is False, the API will just check the schedule but not put in a ToO\n", + "# Set this to True to actually trigger!\n", + "api_res, api_schedule = winter.submit_too(\n", + " program_name=program,\n", + " data=too_list,\n", + " submit_trigger=False\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "edc16188", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Request passed validation! Triggering was set to False. Schedule was not saved to disk and has no assigned name.'" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Let's see what message the API sent back\n", + "api_res.json()[\"msg\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "1758d4f7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Request passed validation! Triggering was set to True. Schedule name is 'request_2024A001_2024_02_05_19_49_26' .\"" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Uncomment to trigger for real\n", + "api_res, api_schedule = winter.submit_too(\n", + " program_name=program,\n", + " data=too_list,\n", + " submit_trigger=True\n", + ")\n", + "api_res.json()[\"msg\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "dfebd472", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
targNameraDegdecDegfieldIDfiltervisitExpTimepriorityprogPIprogNameprogIDvalidStartvalidStopobservedmaxAirmassditherNumberditherStepSizeobsHistID
0ZTF23aaxeacr249.13117947.858962999999999Y960.050.0RStein2024A001360346.1688560347.15885False2.0830.00
1ZTF23aaxeacr249.13117947.858962999999999J960.050.0RStein2024A001360346.1688560347.15885False2.0830.01
2ZTF23aaxeacr249.13117947.858962999999999Hs960.050.0RStein2024A001360346.1688560347.15885False2.01530.02
\n", + "
" + ], + "text/plain": [ + " targName raDeg decDeg fieldID filter visitExpTime \\\n", + "0 ZTF23aaxeacr 249.131179 47.858962 999999999 Y 960.0 \n", + "1 ZTF23aaxeacr 249.131179 47.858962 999999999 J 960.0 \n", + "2 ZTF23aaxeacr 249.131179 47.858962 999999999 Hs 960.0 \n", + "\n", + " priority progPI progName progID validStart validStop observed \\\n", + "0 50.0 RStein 2024A001 3 60346.16885 60347.15885 False \n", + "1 50.0 RStein 2024A001 3 60346.16885 60347.15885 False \n", + "2 50.0 RStein 2024A001 3 60346.16885 60347.15885 False \n", + "\n", + " maxAirmass ditherNumber ditherStepSize obsHistID \n", + "0 2.0 8 30.0 0 \n", + "1 2.0 8 30.0 1 \n", + "2 2.0 15 30.0 2 " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# This is the schedule sent back by the API. Reassuringly, it matches the local one\n", + "api_schedule" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba9f510f", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "winterapi", + "language": "python", + "name": "winterapi" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/Example_3_Interacting_with_Observatory_Queue.ipynb b/notebooks/Example_3_Interacting_with_Observatory_Queue.ipynb new file mode 100644 index 0000000..f164336 --- /dev/null +++ b/notebooks/Example_3_Interacting_with_Observatory_Queue.ipynb @@ -0,0 +1,505 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "f91a536f", + "metadata": {}, + "outputs": [], + "source": [ + "import logging\n", + "from winterapi import WinterAPI\n", + "from wintertoo.models import WinterRaDecToO" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "5e92d4e2", + "metadata": {}, + "outputs": [], + "source": [ + "logging.getLogger(\"winterapi\").setLevel(\"DEBUG\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "ba9823d9", + "metadata": {}, + "outputs": [], + "source": [ + "winter = WinterAPI()" + ] + }, + { + "cell_type": "markdown", + "id": "3370c4d8", + "metadata": {}, + "source": [ + "# Getting a summary of your queued ToOs" + ] + }, + { + "cell_type": "markdown", + "id": "db3b7f84", + "metadata": {}, + "source": [ + "After you've submitted your ToOs, you might wonder how they are doing in the queue. You can find this out using the API." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "202aa614", + "metadata": {}, + "outputs": [], + "source": [ + "program_name = \"2024A001\"" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "03095da8", + "metadata": {}, + "outputs": [], + "source": [ + "res, queue = winter.get_observatory_queue(program_name=program_name)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "38524494", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Returning schedule summary for 2024A001'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res.json()[\"msg\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "1621a452", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
prog_nameobserved_fractoo_schedule_nametarget_namesn_entriestotal_time_hourswindow_startwindow_end
02024A0010.0request_2024A001_2024_02_05_19_49_26[ZTF23aaxeacr]30.82024-02-06 04:03:08.6042024-02-07 03:48:44.605
\n", + "
" + ], + "text/plain": [ + " prog_name observed_frac too_schedule_name \\\n", + "0 2024A001 0.0 request_2024A001_2024_02_05_19_49_26 \n", + "\n", + " target_names n_entries total_time_hours window_start \\\n", + "0 [ZTF23aaxeacr] 3 0.8 2024-02-06 04:03:08.604 \n", + "\n", + " window_end \n", + "0 2024-02-07 03:48:44.605 " + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "queue" + ] + }, + { + "cell_type": "markdown", + "id": "e6e88fea", + "metadata": {}, + "source": [ + "# Checking an individual ToO" + ] + }, + { + "cell_type": "markdown", + "id": "b48e76c5", + "metadata": {}, + "source": [ + "You can see when you submitted a ToO request, since the date is in the `too_schedule_name` name. Nonethless, let's imagine you have forgotten what request you submitted, and want to see what is in that schedule. We can use the schedule_details function to ask the API for this." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "877ed48e", + "metadata": {}, + "outputs": [], + "source": [ + "schedule_name = \"request_2024A001_2024_02_05_19_49_26\"" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "463289d3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Returning details of schedule request_2024A001_2024_02_05_19_49_26'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res, too_request = winter.get_too_details(\n", + " too_schedule_name=schedule_name,\n", + " program_name=program_name\n", + ")\n", + "res.json()[\"msg\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "48b9dd11", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
targNameraDegdecDegfieldIDfiltervisitExpTimepriorityprogPIprogNameprogIDvalidStartvalidStopobservedmaxAirmassditherNumberditherStepSizeobsHistID
0ZTF23aaxeacr249.13117947.858962999999999Y960.050.0RStein2024A001360346.1688560347.1588502.0830.00
1ZTF23aaxeacr249.13117947.858962999999999J960.050.0RStein2024A001360346.1688560347.1588502.0830.01
2ZTF23aaxeacr249.13117947.858962999999999Hs960.050.0RStein2024A001360346.1688560347.1588502.01530.02
\n", + "
" + ], + "text/plain": [ + " targName raDeg decDeg fieldID filter visitExpTime \\\n", + "0 ZTF23aaxeacr 249.131179 47.858962 999999999 Y 960.0 \n", + "1 ZTF23aaxeacr 249.131179 47.858962 999999999 J 960.0 \n", + "2 ZTF23aaxeacr 249.131179 47.858962 999999999 Hs 960.0 \n", + "\n", + " priority progPI progName progID validStart validStop observed \\\n", + "0 50.0 RStein 2024A001 3 60346.16885 60347.15885 0 \n", + "1 50.0 RStein 2024A001 3 60346.16885 60347.15885 0 \n", + "2 50.0 RStein 2024A001 3 60346.16885 60347.15885 0 \n", + "\n", + " maxAirmass ditherNumber ditherStepSize obsHistID \n", + "0 2.0 8 30.0 0 \n", + "1 2.0 8 30.0 1 \n", + "2 2.0 15 30.0 2 " + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Here we see that it's the request we submitted earlier!\n", + "too_request" + ] + }, + { + "cell_type": "markdown", + "id": "15cf523b", + "metadata": {}, + "source": [ + "# Deleting Requests" + ] + }, + { + "cell_type": "markdown", + "id": "43b972b0", + "metadata": {}, + "source": [ + "Now let's imagine that you have made a horrible mistake! The dec of your target is actually 28 degrees, not 48.\n", + "\n", + "You need to urgently need to delete this request! Fornuately, there is a function for this too." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "14666fd9", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Deleted request_2024A001_2024_02_05_19_49_26'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res = winter.delete_too_request(\n", + " too_schedule_name=schedule_name,\n", + " program_name=program_name\n", + ")\n", + "res.json()[\"msg\"]" + ] + }, + { + "cell_type": "markdown", + "id": "72477640", + "metadata": {}, + "source": [ + "Let's just double check to make see there is no request left. **You may need to wait up to 1 minute before changes take appear.**" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "525f3d16", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "No active schedules found for 2024A001\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: []\n", + "Index: []" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res, queue = winter.get_observatory_queue(program_name=program_name)\n", + "print(res.json()[\"msg\"])\n", + "queue" + ] + }, + { + "cell_type": "markdown", + "id": "15c58dd6", + "metadata": {}, + "source": [ + "Fortunately the request has been removed, and the queue is empty!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13e087c7", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "winterapi", + "language": "python", + "name": "winterapi" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/Example_4_Finding_Observatory_Data.ipynb b/notebooks/Example_4_Finding_Observatory_Data.ipynb new file mode 100644 index 0000000..e9f05eb --- /dev/null +++ b/notebooks/Example_4_Finding_Observatory_Data.ipynb @@ -0,0 +1,1935 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "f91a536f", + "metadata": {}, + "outputs": [], + "source": [ + "import logging\n", + "from winterapi import WinterAPI\n", + "from wintertoo.models import ProgramImageQuery" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "5e92d4e2", + "metadata": {}, + "outputs": [], + "source": [ + "logging.getLogger(\"winterapi\").setLevel(\"DEBUG\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "ba9823d9", + "metadata": {}, + "outputs": [], + "source": [ + "winter = WinterAPI()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "13e087c7", + "metadata": {}, + "outputs": [], + "source": [ + "program_name = \"2023A002\"\n", + "start_date = \"20230721\"\n", + "end_date = \"20230730\"" + ] + }, + { + "cell_type": "markdown", + "id": "179e69d0", + "metadata": {}, + "source": [ + "# Query by program" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "b8c16182", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Querying images for 2023A002 between 20230721 and 20230730 of type 'exposure'\n", + "program_name='2023A002' start_date=20230721 end_date=20230730 kind='exposure'\n" + ] + } + ], + "source": [ + "res, images = winter.query_images_by_program(program_name, image_type=\"exposure\", start_date=start_date, end_date=end_date)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "0af1415d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Request passed validation! Found 67 images.'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res.json()[\"msg\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "68525bf4", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
prognamenightdatetargnameradecfidutctimefieldidimage_typesavepath
02023A0022023-07-29None211.67483854.02388122023-07-29T11:14:15.923000+00:003944exposureNone
12023A0022023-07-29None211.70302753.97347532023-07-29T11:32:21.242000+00:003944exposureNone
22023A0022023-07-29None211.64170254.03539932023-07-29T11:34:28.476000+00:003944exposureNone
32023A0022023-07-29None211.60224754.04900512023-07-29T10:58:33.601000+00:003944exposureNone
42023A0022023-07-28None211.55191954.05705632023-07-28T12:18:36.003000+00:003944exposureNone
.................................
622023A0022023-07-27SN2023ixf211.56401454.00001822023-07-27T10:53:57.778000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
632023A0022023-07-27SN2023ixf211.60393253.98076322023-07-27T10:56:04.654000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
642023A0022023-07-27SN2023ixf211.60096153.95311922023-07-27T10:58:11.748000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
652023A0022023-07-27SN2023ixf211.48456353.95908522023-07-27T11:00:18.851000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
662023A0022023-07-27SN2023ixf211.46375053.98928322023-07-27T11:02:25.732000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
\n", + "

67 rows × 10 columns

\n", + "
" + ], + "text/plain": [ + " progname nightdate targname ra dec fid \\\n", + "0 2023A002 2023-07-29 None 211.674838 54.023881 2 \n", + "1 2023A002 2023-07-29 None 211.703027 53.973475 3 \n", + "2 2023A002 2023-07-29 None 211.641702 54.035399 3 \n", + "3 2023A002 2023-07-29 None 211.602247 54.049005 1 \n", + "4 2023A002 2023-07-28 None 211.551919 54.057056 3 \n", + ".. ... ... ... ... ... ... \n", + "62 2023A002 2023-07-27 SN2023ixf 211.564014 54.000018 2 \n", + "63 2023A002 2023-07-27 SN2023ixf 211.603932 53.980763 2 \n", + "64 2023A002 2023-07-27 SN2023ixf 211.600961 53.953119 2 \n", + "65 2023A002 2023-07-27 SN2023ixf 211.484563 53.959085 2 \n", + "66 2023A002 2023-07-27 SN2023ixf 211.463750 53.989283 2 \n", + "\n", + " utctime fieldid image_type \\\n", + "0 2023-07-29T11:14:15.923000+00:00 3944 exposure \n", + "1 2023-07-29T11:32:21.242000+00:00 3944 exposure \n", + "2 2023-07-29T11:34:28.476000+00:00 3944 exposure \n", + "3 2023-07-29T10:58:33.601000+00:00 3944 exposure \n", + "4 2023-07-28T12:18:36.003000+00:00 3944 exposure \n", + ".. ... ... ... \n", + "62 2023-07-27T10:53:57.778000+00:00 3944 exposure \n", + "63 2023-07-27T10:56:04.654000+00:00 3944 exposure \n", + "64 2023-07-27T10:58:11.748000+00:00 3944 exposure \n", + "65 2023-07-27T11:00:18.851000+00:00 3944 exposure \n", + "66 2023-07-27T11:02:25.732000+00:00 3944 exposure \n", + "\n", + " savepath \n", + "0 None \n", + "1 None \n", + "2 None \n", + "3 None \n", + "4 None \n", + ".. ... \n", + "62 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "63 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "64 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "65 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "66 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "\n", + "[67 rows x 10 columns]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "images" + ] + }, + { + "cell_type": "markdown", + "id": "2cddf5b7", + "metadata": {}, + "source": [ + "# Query by program and target name" + ] + }, + { + "cell_type": "markdown", + "id": "653daae7", + "metadata": {}, + "source": [ + "If you are triggering lots of requests, you might want to restrict yourself to a specific target name.\n", + "**This will only work if you specified a target name in the ToO request though**. \n", + "So it's a good idea to do that!" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "72816e21", + "metadata": {}, + "outputs": [], + "source": [ + "target_name = \"SN2023ixf\"" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "bb347283", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Querying images for 2023A002 between 20230721 and 20230730 of type 'exposure', with name SN2023ixf\n", + "program_name='2023A002' start_date=20230721 end_date=20230730 kind='exposure' target_name='SN2023ixf'\n" + ] + } + ], + "source": [ + "res, images = winter.query_images_by_target_name(program_name, image_type=\"exposure\", target_name=target_name, start_date=start_date, end_date=end_date)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "8d323f95", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Request passed validation! Found 5 images.'" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res.json()[\"msg\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "a54e729c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
prognamenightdatetargnameradecfidutctimefieldidimage_typesavepath
02023A0022023-07-27SN2023ixf211.56401454.00001822023-07-27T10:53:57.778000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
12023A0022023-07-27SN2023ixf211.60393253.98076322023-07-27T10:56:04.654000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
22023A0022023-07-27SN2023ixf211.60096153.95311922023-07-27T10:58:11.748000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
32023A0022023-07-27SN2023ixf211.48456353.95908522023-07-27T11:00:18.851000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
42023A0022023-07-27SN2023ixf211.46375053.98928322023-07-27T11:02:25.732000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
\n", + "
" + ], + "text/plain": [ + " progname nightdate targname ra dec fid \\\n", + "0 2023A002 2023-07-27 SN2023ixf 211.564014 54.000018 2 \n", + "1 2023A002 2023-07-27 SN2023ixf 211.603932 53.980763 2 \n", + "2 2023A002 2023-07-27 SN2023ixf 211.600961 53.953119 2 \n", + "3 2023A002 2023-07-27 SN2023ixf 211.484563 53.959085 2 \n", + "4 2023A002 2023-07-27 SN2023ixf 211.463750 53.989283 2 \n", + "\n", + " utctime fieldid image_type \\\n", + "0 2023-07-27T10:53:57.778000+00:00 3944 exposure \n", + "1 2023-07-27T10:56:04.654000+00:00 3944 exposure \n", + "2 2023-07-27T10:58:11.748000+00:00 3944 exposure \n", + "3 2023-07-27T11:00:18.851000+00:00 3944 exposure \n", + "4 2023-07-27T11:02:25.732000+00:00 3944 exposure \n", + "\n", + " savepath \n", + "0 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "1 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "2 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "3 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "4 /Users/robertstein/Data/winter/20230726/raw/WI... " + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "images" + ] + }, + { + "cell_type": "markdown", + "id": "3a2c19cd", + "metadata": {}, + "source": [ + "# Query by program and position (cone)" + ] + }, + { + "cell_type": "markdown", + "id": "2bc01c56", + "metadata": {}, + "source": [ + "In this case, I suspect there are more images. Maybe I forgot to tag all ToO requests with the appropriate name.\n", + "\n", + "You can instead search in a cone around a known position, which may pick up dome extra images." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "6a832f90", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Querying images for 2023A002 between 20230721 and 20230730 of type 'exposure', with a radius of 1.0 degrees around 211.564014, 53.953119\n", + "program_name='2023A002' start_date=20230721 end_date=20230730 kind='exposure' ra=211.564014 dec=53.953119 radius_deg=1.0\n" + ] + } + ], + "source": [ + "res, images = winter.query_images_by_cone(program_name, image_type=\"exposure\", ra_deg=211.564014, dec_deg=53.953119, start_date=start_date, end_date=end_date)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "f3b1a443", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Request passed validation! Found 26 images.'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res.json()[\"msg\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "1e96a188", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
prognamenightdatetargnameradecfidutctimefieldidimage_typesavepath
02023A0022023-07-29None211.67483854.02388122023-07-29T11:14:15.923000+00:003944exposureNone
12023A0022023-07-29None211.70302753.97347532023-07-29T11:32:21.242000+00:003944exposureNone
22023A0022023-07-29None211.64170254.03539932023-07-29T11:34:28.476000+00:003944exposureNone
32023A0022023-07-29None211.60224754.04900512023-07-29T10:58:33.601000+00:003944exposureNone
42023A0022023-07-28None211.55191954.05705632023-07-28T12:18:36.003000+00:003944exposureNone
52023A0022023-07-29None211.57969354.00765422023-07-29T11:12:08.915000+00:003944exposureNone
62023A0022023-07-28None211.57981754.01148232023-07-28T12:20:44.046000+00:003944exposureNone
72023A0022023-07-29None211.56398754.00001212023-07-29T10:54:19.249000+00:003944exposureNone
82023A0022023-07-29None211.56397154.00002232023-07-29T11:30:13.813000+00:003944exposureNone
92023A0022023-07-28None211.56396354.00001612023-07-28T10:54:32.773000+00:003944exposureNone
102023A0022023-07-29None211.56396854.00000322023-07-29T11:05:46.324000+00:003944exposureNone
112023A0022023-07-28None211.56393553.99998932023-07-28T12:16:28.921000+00:003944exposureNone
122023A0022023-07-29None211.68292953.95296012023-07-29T11:02:47.879000+00:003944exposureNone
132023A0022023-07-29None211.61956153.97694732023-07-29T11:38:43.505000+00:003944exposureNone
142023A0022023-07-28None211.59261653.98677732023-07-28T12:24:58.300000+00:003944exposureNone
152023A0022023-07-29None211.52539653.98662012023-07-29T11:00:41.100000+00:003944exposureNone
162023A0022023-07-29None211.51312553.98213212023-07-29T10:56:26.509000+00:003944exposureNone
172023A0022023-07-28None211.50804453.97571932023-07-28T12:22:51.710000+00:003944exposureNone
182023A0022023-07-29None211.46930254.00704522023-07-29T11:10:02.560000+00:003944exposureNone
192023A0022023-07-29None211.48591553.98870132023-07-29T11:36:35.974000+00:003944exposureNone
202023A0022023-07-29None211.50243653.91698222023-07-29T11:07:54.564000+00:003944exposureNone
212023A0022023-07-27SN2023ixf211.56401454.00001822023-07-27T10:53:57.778000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
222023A0022023-07-27SN2023ixf211.60393253.98076322023-07-27T10:56:04.654000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
232023A0022023-07-27SN2023ixf211.60096153.95311922023-07-27T10:58:11.748000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
242023A0022023-07-27SN2023ixf211.48456353.95908522023-07-27T11:00:18.851000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
252023A0022023-07-27SN2023ixf211.46375053.98928322023-07-27T11:02:25.732000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
\n", + "
" + ], + "text/plain": [ + " progname nightdate targname ra dec fid \\\n", + "0 2023A002 2023-07-29 None 211.674838 54.023881 2 \n", + "1 2023A002 2023-07-29 None 211.703027 53.973475 3 \n", + "2 2023A002 2023-07-29 None 211.641702 54.035399 3 \n", + "3 2023A002 2023-07-29 None 211.602247 54.049005 1 \n", + "4 2023A002 2023-07-28 None 211.551919 54.057056 3 \n", + "5 2023A002 2023-07-29 None 211.579693 54.007654 2 \n", + "6 2023A002 2023-07-28 None 211.579817 54.011482 3 \n", + "7 2023A002 2023-07-29 None 211.563987 54.000012 1 \n", + "8 2023A002 2023-07-29 None 211.563971 54.000022 3 \n", + "9 2023A002 2023-07-28 None 211.563963 54.000016 1 \n", + "10 2023A002 2023-07-29 None 211.563968 54.000003 2 \n", + "11 2023A002 2023-07-28 None 211.563935 53.999989 3 \n", + "12 2023A002 2023-07-29 None 211.682929 53.952960 1 \n", + "13 2023A002 2023-07-29 None 211.619561 53.976947 3 \n", + "14 2023A002 2023-07-28 None 211.592616 53.986777 3 \n", + "15 2023A002 2023-07-29 None 211.525396 53.986620 1 \n", + "16 2023A002 2023-07-29 None 211.513125 53.982132 1 \n", + "17 2023A002 2023-07-28 None 211.508044 53.975719 3 \n", + "18 2023A002 2023-07-29 None 211.469302 54.007045 2 \n", + "19 2023A002 2023-07-29 None 211.485915 53.988701 3 \n", + "20 2023A002 2023-07-29 None 211.502436 53.916982 2 \n", + "21 2023A002 2023-07-27 SN2023ixf 211.564014 54.000018 2 \n", + "22 2023A002 2023-07-27 SN2023ixf 211.603932 53.980763 2 \n", + "23 2023A002 2023-07-27 SN2023ixf 211.600961 53.953119 2 \n", + "24 2023A002 2023-07-27 SN2023ixf 211.484563 53.959085 2 \n", + "25 2023A002 2023-07-27 SN2023ixf 211.463750 53.989283 2 \n", + "\n", + " utctime fieldid image_type \\\n", + "0 2023-07-29T11:14:15.923000+00:00 3944 exposure \n", + "1 2023-07-29T11:32:21.242000+00:00 3944 exposure \n", + "2 2023-07-29T11:34:28.476000+00:00 3944 exposure \n", + "3 2023-07-29T10:58:33.601000+00:00 3944 exposure \n", + "4 2023-07-28T12:18:36.003000+00:00 3944 exposure \n", + "5 2023-07-29T11:12:08.915000+00:00 3944 exposure \n", + "6 2023-07-28T12:20:44.046000+00:00 3944 exposure \n", + "7 2023-07-29T10:54:19.249000+00:00 3944 exposure \n", + "8 2023-07-29T11:30:13.813000+00:00 3944 exposure \n", + "9 2023-07-28T10:54:32.773000+00:00 3944 exposure \n", + "10 2023-07-29T11:05:46.324000+00:00 3944 exposure \n", + "11 2023-07-28T12:16:28.921000+00:00 3944 exposure \n", + "12 2023-07-29T11:02:47.879000+00:00 3944 exposure \n", + "13 2023-07-29T11:38:43.505000+00:00 3944 exposure \n", + "14 2023-07-28T12:24:58.300000+00:00 3944 exposure \n", + "15 2023-07-29T11:00:41.100000+00:00 3944 exposure \n", + "16 2023-07-29T10:56:26.509000+00:00 3944 exposure \n", + "17 2023-07-28T12:22:51.710000+00:00 3944 exposure \n", + "18 2023-07-29T11:10:02.560000+00:00 3944 exposure \n", + "19 2023-07-29T11:36:35.974000+00:00 3944 exposure \n", + "20 2023-07-29T11:07:54.564000+00:00 3944 exposure \n", + "21 2023-07-27T10:53:57.778000+00:00 3944 exposure \n", + "22 2023-07-27T10:56:04.654000+00:00 3944 exposure \n", + "23 2023-07-27T10:58:11.748000+00:00 3944 exposure \n", + "24 2023-07-27T11:00:18.851000+00:00 3944 exposure \n", + "25 2023-07-27T11:02:25.732000+00:00 3944 exposure \n", + "\n", + " savepath \n", + "0 None \n", + "1 None \n", + "2 None \n", + "3 None \n", + "4 None \n", + "5 None \n", + "6 None \n", + "7 None \n", + "8 None \n", + "9 None \n", + "10 None \n", + "11 None \n", + "12 None \n", + "13 None \n", + "14 None \n", + "15 None \n", + "16 None \n", + "17 None \n", + "18 None \n", + "19 None \n", + "20 None \n", + "21 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "22 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "23 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "24 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "25 /Users/robertstein/Data/winter/20230726/raw/WI... " + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "images" + ] + }, + { + "cell_type": "markdown", + "id": "f5e13dd1", + "metadata": {}, + "source": [ + "We see that we obtain a subset of the images in our program query, but more than before! Specifically those centered at our chosen position, with a default search radius of 1 deg." + ] + }, + { + "cell_type": "markdown", + "id": "1cc70aa8", + "metadata": {}, + "source": [ + "# Query by program and position (rectangle)" + ] + }, + { + "cell_type": "markdown", + "id": "11ae3977", + "metadata": {}, + "source": [ + "You can also query with a rectangle" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "882eb586", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Querying images for 2023A002 between 20230701 and 20240206 of type 'stack', with RA between 32.0 and 33.0 and Dec between -3.0 and -2.0\n", + "program_name='2023A002' start_date=20230701 end_date=20240206 kind='stack' ra_min=32.0 ra_max=33.0 dec_min=-3.0 dec_max=-2.0\n" + ] + } + ], + "source": [ + "res, images = winter.query_images_by_rectangle(\n", + " program_name, start_date=\"20230701\",\n", + " ra_min_deg = 32., ra_max_deg=33., \n", + " dec_min_deg = -3., dec_max_deg = -2.,\n", + " image_type=\"stack\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "f0a2561b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Request passed validation! Found 2 images.'" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res.json()[\"msg\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "a634f907", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
prognamenightdatetargnameradecfidutctimefieldidimage_typesavepath
02023A0022023-07-29None32.402744-2.03849512023-07-29T17:26:45.891000+00:0021155stack/data/loki/data/winter/20230728/final/WINTERca...
12023A0022023-07-29None32.414180-2.37469812023-07-29T17:24:38.502000+00:0021155stack/data/loki/data/winter/20230728/final/WINTERca...
\n", + "
" + ], + "text/plain": [ + " progname nightdate targname ra dec fid \\\n", + "0 2023A002 2023-07-29 None 32.402744 -2.038495 1 \n", + "1 2023A002 2023-07-29 None 32.414180 -2.374698 1 \n", + "\n", + " utctime fieldid image_type \\\n", + "0 2023-07-29T17:26:45.891000+00:00 21155 stack \n", + "1 2023-07-29T17:24:38.502000+00:00 21155 stack \n", + "\n", + " savepath \n", + "0 /data/loki/data/winter/20230728/final/WINTERca... \n", + "1 /data/loki/data/winter/20230728/final/WINTERca... " + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "images" + ] + }, + { + "cell_type": "markdown", + "id": "d7198881", + "metadata": {}, + "source": [ + "# Query different image types" + ] + }, + { + "cell_type": "markdown", + "id": "7dca7581", + "metadata": {}, + "source": [ + "There are multiple types of image that can be queried by the API:" + ] + }, + { + "cell_type": "markdown", + "id": "0159143f", + "metadata": {}, + "source": [ + "## Exposure" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "738dc40b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Querying images for 2023A002 between 20230721 and 20230730 of type 'exposure', with name SN2023ixf\n", + "program_name='2023A002' start_date=20230721 end_date=20230730 kind='exposure' target_name='SN2023ixf'\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
prognamenightdatetargnameradecfidutctimefieldidimage_typesavepath
02023A0022023-07-27SN2023ixf211.56401454.00001822023-07-27T10:53:57.778000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
12023A0022023-07-27SN2023ixf211.60393253.98076322023-07-27T10:56:04.654000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
22023A0022023-07-27SN2023ixf211.60096153.95311922023-07-27T10:58:11.748000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
32023A0022023-07-27SN2023ixf211.48456353.95908522023-07-27T11:00:18.851000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
42023A0022023-07-27SN2023ixf211.46375053.98928322023-07-27T11:02:25.732000+00:003944exposure/Users/robertstein/Data/winter/20230726/raw/WI...
\n", + "
" + ], + "text/plain": [ + " progname nightdate targname ra dec fid \\\n", + "0 2023A002 2023-07-27 SN2023ixf 211.564014 54.000018 2 \n", + "1 2023A002 2023-07-27 SN2023ixf 211.603932 53.980763 2 \n", + "2 2023A002 2023-07-27 SN2023ixf 211.600961 53.953119 2 \n", + "3 2023A002 2023-07-27 SN2023ixf 211.484563 53.959085 2 \n", + "4 2023A002 2023-07-27 SN2023ixf 211.463750 53.989283 2 \n", + "\n", + " utctime fieldid image_type \\\n", + "0 2023-07-27T10:53:57.778000+00:00 3944 exposure \n", + "1 2023-07-27T10:56:04.654000+00:00 3944 exposure \n", + "2 2023-07-27T10:58:11.748000+00:00 3944 exposure \n", + "3 2023-07-27T11:00:18.851000+00:00 3944 exposure \n", + "4 2023-07-27T11:02:25.732000+00:00 3944 exposure \n", + "\n", + " savepath \n", + "0 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "1 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "2 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "3 /Users/robertstein/Data/winter/20230726/raw/WI... \n", + "4 /Users/robertstein/Data/winter/20230726/raw/WI... " + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res, images = winter.query_images_by_target_name(program_name, image_type=\"exposure\", target_name=target_name, start_date=start_date, end_date=end_date)\n", + "images" + ] + }, + { + "cell_type": "markdown", + "id": "9384408c", + "metadata": {}, + "source": [ + "## Raw" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "c73fa604", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Querying images for 2023A002 between 20230721 and 20230730 of type 'raw', with name SN2023ixf\n", + "program_name='2023A002' start_date=20230721 end_date=20230730 kind='raw' target_name='SN2023ixf'\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
prognamenightdatetargnameradecfidutctimefieldidimage_typesavepath
02023A0022023-07-27SN2023ixf211.56401454.00001822023-07-27T10:53:57.778000+00:003944raw/data/loki/data/winter/20230726/raw_unpacked/W...
12023A0022023-07-27SN2023ixf211.60393253.98076322023-07-27T10:56:04.654000+00:003944raw/data/loki/data/winter/20230726/raw_unpacked/W...
22023A0022023-07-27SN2023ixf211.60096153.95311922023-07-27T10:58:11.748000+00:003944raw/data/loki/data/winter/20230726/raw_unpacked/W...
32023A0022023-07-27SN2023ixf211.46375053.98928322023-07-27T11:02:25.732000+00:003944raw/data/loki/data/winter/20230726/raw_unpacked/W...
42023A0022023-07-27SN2023ixf211.48456353.95908522023-07-27T11:00:18.851000+00:003944raw/data/loki/data/winter/20230726/raw_unpacked/W...
\n", + "
" + ], + "text/plain": [ + " progname nightdate targname ra dec fid \\\n", + "0 2023A002 2023-07-27 SN2023ixf 211.564014 54.000018 2 \n", + "1 2023A002 2023-07-27 SN2023ixf 211.603932 53.980763 2 \n", + "2 2023A002 2023-07-27 SN2023ixf 211.600961 53.953119 2 \n", + "3 2023A002 2023-07-27 SN2023ixf 211.463750 53.989283 2 \n", + "4 2023A002 2023-07-27 SN2023ixf 211.484563 53.959085 2 \n", + "\n", + " utctime fieldid image_type \\\n", + "0 2023-07-27T10:53:57.778000+00:00 3944 raw \n", + "1 2023-07-27T10:56:04.654000+00:00 3944 raw \n", + "2 2023-07-27T10:58:11.748000+00:00 3944 raw \n", + "3 2023-07-27T11:02:25.732000+00:00 3944 raw \n", + "4 2023-07-27T11:00:18.851000+00:00 3944 raw \n", + "\n", + " savepath \n", + "0 /data/loki/data/winter/20230726/raw_unpacked/W... \n", + "1 /data/loki/data/winter/20230726/raw_unpacked/W... \n", + "2 /data/loki/data/winter/20230726/raw_unpacked/W... \n", + "3 /data/loki/data/winter/20230726/raw_unpacked/W... \n", + "4 /data/loki/data/winter/20230726/raw_unpacked/W... " + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res, images = winter.query_images_by_target_name(program_name, image_type=\"raw\", target_name=target_name, start_date=start_date, end_date=end_date)\n", + "images" + ] + }, + { + "cell_type": "markdown", + "id": "1665b1b4", + "metadata": {}, + "source": [ + "## Science" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "a6c2499a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Querying images for 2023A002 between 20230721 and 20230730 of type 'science', with name SN2023ixf\n", + "program_name='2023A002' start_date=20230721 end_date=20230730 kind='science' target_name='SN2023ixf'\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
prognamenightdatetargnameradecfidutctimefieldidimage_typesavepath
02023A0022023-07-27SN2023ixf211.60393253.98076322023-07-27T10:56:04.654000+00:003944science/data/loki/data/winter/20230726/post_scamp/WIN...
12023A0022023-07-27SN2023ixf211.60096153.95311922023-07-27T10:58:11.748000+00:003944science/data/loki/data/winter/20230726/post_scamp/WIN...
22023A0022023-07-27SN2023ixf211.56401454.00001822023-07-27T10:53:57.778000+00:003944science/data/loki/data/winter/20230726/post_scamp/WIN...
32023A0022023-07-27SN2023ixf211.48456353.95908522023-07-27T11:00:18.851000+00:003944science/data/loki/data/winter/20230726/post_scamp/WIN...
42023A0022023-07-27SN2023ixf211.46375053.98928322023-07-27T11:02:25.732000+00:003944science/data/loki/data/winter/20230726/post_scamp/WIN...
\n", + "
" + ], + "text/plain": [ + " progname nightdate targname ra dec fid \\\n", + "0 2023A002 2023-07-27 SN2023ixf 211.603932 53.980763 2 \n", + "1 2023A002 2023-07-27 SN2023ixf 211.600961 53.953119 2 \n", + "2 2023A002 2023-07-27 SN2023ixf 211.564014 54.000018 2 \n", + "3 2023A002 2023-07-27 SN2023ixf 211.484563 53.959085 2 \n", + "4 2023A002 2023-07-27 SN2023ixf 211.463750 53.989283 2 \n", + "\n", + " utctime fieldid image_type \\\n", + "0 2023-07-27T10:56:04.654000+00:00 3944 science \n", + "1 2023-07-27T10:58:11.748000+00:00 3944 science \n", + "2 2023-07-27T10:53:57.778000+00:00 3944 science \n", + "3 2023-07-27T11:00:18.851000+00:00 3944 science \n", + "4 2023-07-27T11:02:25.732000+00:00 3944 science \n", + "\n", + " savepath \n", + "0 /data/loki/data/winter/20230726/post_scamp/WIN... \n", + "1 /data/loki/data/winter/20230726/post_scamp/WIN... \n", + "2 /data/loki/data/winter/20230726/post_scamp/WIN... \n", + "3 /data/loki/data/winter/20230726/post_scamp/WIN... \n", + "4 /data/loki/data/winter/20230726/post_scamp/WIN... " + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res, images = winter.query_images_by_target_name(program_name, image_type=\"science\", target_name=target_name, start_date=start_date, end_date=end_date)\n", + "images" + ] + }, + { + "cell_type": "markdown", + "id": "d646c777", + "metadata": {}, + "source": [ + "## Stack" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "c512205e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Querying images for 2023A002 between 20230721 and 20230730 of type 'stack', with name SN2023ixf\n", + "program_name='2023A002' start_date=20230721 end_date=20230730 kind='stack' target_name='SN2023ixf'\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
prognamenightdatetargnameradecfidutctimefieldidimage_typesavepath
02023A0022023-07-27SN2023ixf210.941954.26235222023-07-27T10:53:57.778000+00:003944stack/data/loki/data/winter/20230726/final/WINTERca...
\n", + "
" + ], + "text/plain": [ + " progname nightdate targname ra dec fid \\\n", + "0 2023A002 2023-07-27 SN2023ixf 210.9419 54.262352 2 \n", + "\n", + " utctime fieldid image_type \\\n", + "0 2023-07-27T10:53:57.778000+00:00 3944 stack \n", + "\n", + " savepath \n", + "0 /data/loki/data/winter/20230726/final/WINTERca... " + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res, images = winter.query_images_by_target_name(program_name, image_type=\"stack\", target_name=target_name, start_date=start_date, end_date=end_date)\n", + "images" + ] + }, + { + "cell_type": "markdown", + "id": "8e484c3a", + "metadata": {}, + "source": [ + "## Diff" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "29c53ab5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Querying images for 2023A002 between 20230721 and 20230730 of type 'diff', with name SN2023ixf\n", + "program_name='2023A002' start_date=20230721 end_date=20230730 kind='diff' target_name='SN2023ixf'\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
prognamenightdatetargnameradecfidutctimefieldidimage_typesavepath
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: [progname, nightdate, targname, ra, dec, fid, utctime, fieldid, image_type, savepath]\n", + "Index: []" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res, images = winter.query_images_by_target_name(program_name, image_type=\"diff\", target_name=target_name, start_date=start_date, end_date=end_date)\n", + "images" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "88983a1c", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "winterapi", + "language": "python", + "name": "winterapi" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/Example_5_Downloading_Observatory_Data.ipynb b/notebooks/Example_5_Downloading_Observatory_Data.ipynb new file mode 100644 index 0000000..c451b95 --- /dev/null +++ b/notebooks/Example_5_Downloading_Observatory_Data.ipynb @@ -0,0 +1,325 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "f91a536f", + "metadata": {}, + "outputs": [], + "source": [ + "import logging\n", + "from winterapi import WinterAPI\n", + "from wintertoo.models import ProgramImageQuery" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "5e92d4e2", + "metadata": {}, + "outputs": [], + "source": [ + "logging.getLogger(\"winterapi\").setLevel(\"DEBUG\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "ba9823d9", + "metadata": {}, + "outputs": [], + "source": [ + "winter = WinterAPI()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "13e087c7", + "metadata": {}, + "outputs": [], + "source": [ + "program_name = \"2023A002\"" + ] + }, + { + "cell_type": "markdown", + "id": "179e69d0", + "metadata": {}, + "source": [ + "# Query by program and target" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "65f70acd", + "metadata": {}, + "outputs": [], + "source": [ + "image_type = \"stack\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b8c16182", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Querying images for 2023A002 between 20240129 and 20240206 of type 'stack'\n", + "program_name='2023A002' start_date=20240129 end_date=20240206 kind='stack'\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
prognamenightdatetargnameradecfidutctimefieldidimage_typesavepath
02023A0022024-01-30None184.7212749.99412022024-01-30T14:14:25.616000+00:00999999999stack/data/loki/data/winter/20240129/final/WINTERca...
12023A0022024-01-30None184.7909049.37140722024-01-30T14:14:25.616000+00:00999999999stack/data/loki/data/winter/20240129/final/WINTERca...
22023A0022024-01-30None185.2218249.97834822024-01-30T14:14:25.616000+00:00999999999stack/data/loki/data/winter/20240129/final/WINTERca...
32023A0022024-01-30None185.7820149.37677022024-01-30T14:14:25.616000+00:00999999999stack/data/loki/data/winter/20240129/final/WINTERca...
42023A0022024-01-30None185.8211449.97093622024-01-30T14:14:25.616000+00:00999999999stack/data/loki/data/winter/20240129/final/WINTERca...
\n", + "
" + ], + "text/plain": [ + " progname nightdate targname ra dec fid \\\n", + "0 2023A002 2024-01-30 None 184.72127 49.994120 2 \n", + "1 2023A002 2024-01-30 None 184.79090 49.371407 2 \n", + "2 2023A002 2024-01-30 None 185.22182 49.978348 2 \n", + "3 2023A002 2024-01-30 None 185.78201 49.376770 2 \n", + "4 2023A002 2024-01-30 None 185.82114 49.970936 2 \n", + "\n", + " utctime fieldid image_type \\\n", + "0 2024-01-30T14:14:25.616000+00:00 999999999 stack \n", + "1 2024-01-30T14:14:25.616000+00:00 999999999 stack \n", + "2 2024-01-30T14:14:25.616000+00:00 999999999 stack \n", + "3 2024-01-30T14:14:25.616000+00:00 999999999 stack \n", + "4 2024-01-30T14:14:25.616000+00:00 999999999 stack \n", + "\n", + " savepath \n", + "0 /data/loki/data/winter/20240129/final/WINTERca... \n", + "1 /data/loki/data/winter/20240129/final/WINTERca... \n", + "2 /data/loki/data/winter/20240129/final/WINTERca... \n", + "3 /data/loki/data/winter/20240129/final/WINTERca... \n", + "4 /data/loki/data/winter/20240129/final/WINTERca... " + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Let's start by doing an image query\n", + "res, images = winter.query_images_by_program(program_name, image_type=image_type, start_date=\"20240129\")\n", + "images" + ] + }, + { + "cell_type": "markdown", + "id": "b9642e4f", + "metadata": {}, + "source": [ + "# Downloading a list of images" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "88983a1c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/loki/data/winter/20240129/final/WINTERcamera_20240130-061425-616_mef_1_0_0_stack.fits',\n", + " '/data/loki/data/winter/20240129/final/WINTERcamera_20240130-061425-616_mef_5_0_0_stack.fits',\n", + " '/data/loki/data/winter/20240129/final/WINTERcamera_20240130-061425-616_mef_3_0_0_stack.fits',\n", + " '/data/loki/data/winter/20240129/final/WINTERcamera_20240130-061425-616_mef_2_0_0_stack.fits',\n", + " '/data/loki/data/winter/20240129/final/WINTERcamera_20240130-061425-616_mef_4_0_0_stack.fits']" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Let's say we want to download an image. What's the savepath?\n", + "savepaths = images[\"savepath\"].tolist()\n", + "savepaths" + ] + }, + { + "cell_type": "markdown", + "id": "ca13baee", + "metadata": {}, + "source": [ + "We can use the API to download this file, or files, as a compressed zip. \n", + "You must specify the type of image!" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "96ec4b1f", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "No output directory specified, using /Users/robertstein\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Saved images to /Users/robertstein/WINTER_images_stack_2024_02_05_22_58_41.zip\n", + "{'Server': 'nginx/1.14.1', 'Date': 'Tue, 06 Feb 2024 06:58:45 GMT', 'Content-Type': 'application/x-zip-compressed', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'content-disposition': 'attachment; filename=WINTER_images_stack_2024_02_05_22_58_41.zip', 'missing-images': '0', 'total-images': '5', 'image-type': 'stack'}\n" + ] + } + ], + "source": [ + "res, output_path = winter.download_image_list(program_name, image_type=image_type, paths=savepaths)\n", + "print(f\"Saved images to {output_path}\")\n", + "print(res.headers)\n", + "res.raise_for_status()" + ] + }, + { + "cell_type": "markdown", + "id": "f741610d", + "metadata": {}, + "source": [ + "Success! We downloaded a zip file with all our images." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2390cfd3", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "winterapi", + "language": "python", + "name": "winterapi" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/0_intro_accounts.ipynb b/notebooks/Info_1_Introduction_To_Credentials.ipynb similarity index 59% rename from notebooks/0_intro_accounts.ipynb rename to notebooks/Info_1_Introduction_To_Credentials.ipynb index 8cd9f9a..2028bb5 100644 --- a/notebooks/0_intro_accounts.ipynb +++ b/notebooks/Info_1_Introduction_To_Credentials.ipynb @@ -23,17 +23,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "84ae8151", "metadata": {}, "outputs": [], "source": [ - "WinterAPI.clear_cache()" + "# Uncomment to clear cache\n", + "# WinterAPI.clear_cache()" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "id": "3fbd0cbf", "metadata": {}, "outputs": [], @@ -51,7 +52,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "id": "90ac54c2", "metadata": { "scrolled": false @@ -87,7 +88,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "id": "5739be95", "metadata": {}, "outputs": [], @@ -107,7 +108,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "id": "e4dc8ccb", "metadata": {}, "outputs": [ @@ -125,7 +126,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[6], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mwinter\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43madd_user_details\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mrdstein@caltech.edu\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43man-incorrect-password\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43moverwrite\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[7], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mwinter\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43madd_user_details\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mrdstein@caltech.edu\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43man-incorrect-password\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43moverwrite\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m)\u001b[49m\n", "File \u001b[0;32m~/Code/winterapi/winterapi/messenger.py:160\u001b[0m, in \u001b[0;36mWinterAPI.add_user_details\u001b[0;34m(self, user, password, overwrite)\u001b[0m\n\u001b[1;32m 157\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m password \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 158\u001b[0m password \u001b[38;5;241m=\u001b[39m getpass\u001b[38;5;241m.\u001b[39mgetpass(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mEnter password for user \u001b[39m\u001b[38;5;132;01m{\u001b[39;00muser\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m: \u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m--> 160\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcheck_user_details\u001b[49m\u001b[43m(\u001b[49m\u001b[43muser\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43muser\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpassword\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpassword\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 162\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfidelius\u001b[38;5;241m.\u001b[39mset_user(user\u001b[38;5;241m=\u001b[39muser, password\u001b[38;5;241m=\u001b[39mpassword, overwrite\u001b[38;5;241m=\u001b[39moverwrite)\n", "File \u001b[0;32m~/Code/winterapi/winterapi/messenger.py:172\u001b[0m, in \u001b[0;36mWinterAPI.check_user_details\u001b[0;34m(self, user, password)\u001b[0m\n\u001b[1;32m 164\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcheck_user_details\u001b[39m(\u001b[38;5;28mself\u001b[39m, user: \u001b[38;5;28mstr\u001b[39m, password: \u001b[38;5;28mstr\u001b[39m) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m requests\u001b[38;5;241m.\u001b[39mResponse:\n\u001b[1;32m 165\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 166\u001b[0m \u001b[38;5;124;03m Check the user details are correct.\u001b[39;00m\n\u001b[1;32m 167\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 170\u001b[0m \u001b[38;5;124;03m :return: API response\u001b[39;00m\n\u001b[1;32m 171\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 172\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[43murl\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mUSER_URL\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mauth\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43muser\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpassword\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n", "File \u001b[0;32m~/anaconda3/envs/winterapi/lib/python3.11/site-packages/backoff/_sync.py:105\u001b[0m, in \u001b[0;36mretry_exception..retry\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 96\u001b[0m details \u001b[38;5;241m=\u001b[39m {\n\u001b[1;32m 97\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtarget\u001b[39m\u001b[38;5;124m\"\u001b[39m: target,\n\u001b[1;32m 98\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124margs\u001b[39m\u001b[38;5;124m\"\u001b[39m: args,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 101\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124melapsed\u001b[39m\u001b[38;5;124m\"\u001b[39m: elapsed,\n\u001b[1;32m 102\u001b[0m }\n\u001b[1;32m 104\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 105\u001b[0m ret \u001b[38;5;241m=\u001b[39m \u001b[43mtarget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 106\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m exception \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 107\u001b[0m max_tries_exceeded \u001b[38;5;241m=\u001b[39m (tries \u001b[38;5;241m==\u001b[39m max_tries_value)\n", @@ -181,46 +182,23 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "b5d1e0d3", "metadata": {}, "outputs": [], "source": [ - "winter.add_program(\"2020A000\", \"a-secure-api-key\")\n", - "winter.add_program(\"2020B000\", \"other-secure-api-key\")" + "winter.add_program(\"202XA000\", \"an-api-key\")\n", + "# winter.add_program(\"2020B000\", \"other-secure-api-key\")" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "a12a83db", "metadata": { "scrolled": true }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "API call failed with ': {\"success\":false,\"msg\":\"Failed to validate credentials provided.\"}'\n" - ] - }, - { - "ename": "ValueError", - "evalue": "API call failed with ': {\"success\":false,\"msg\":\"Failed to validate credentials provided.\"}'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[8], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mwinter\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43madd_program\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m2022A000\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43ma-fake-api-key\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/Code/winterapi/winterapi/messenger.py:203\u001b[0m, in \u001b[0;36mWinterAPI.add_program\u001b[0;34m(self, program_name, program_api_key, overwrite)\u001b[0m\n\u001b[1;32m 198\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m program_api_key \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 199\u001b[0m program_api_key \u001b[38;5;241m=\u001b[39m getpass\u001b[38;5;241m.\u001b[39mgetpass(\n\u001b[1;32m 200\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mEnter password for user \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mprogram_name\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m: \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 201\u001b[0m )\n\u001b[0;32m--> 203\u001b[0m res \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcheck_program_details\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 204\u001b[0m \u001b[43m \u001b[49m\u001b[43mprogram_name\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mprogram_name\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 205\u001b[0m \u001b[43m \u001b[49m\u001b[43mprogram_api_key\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mprogram_api_key\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 206\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 208\u001b[0m program_dict \u001b[38;5;241m=\u001b[39m res\u001b[38;5;241m.\u001b[39mjson()[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mbody\u001b[39m\u001b[38;5;124m\"\u001b[39m]\n\u001b[1;32m 209\u001b[0m program_dict[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mprog_key\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m program_api_key\n", - "File \u001b[0;32m~/Code/winterapi/winterapi/messenger.py:223\u001b[0m, in \u001b[0;36mWinterAPI.check_program_details\u001b[0;34m(self, program_name, program_api_key)\u001b[0m\n\u001b[1;32m 213\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcheck_program_details\u001b[39m(\n\u001b[1;32m 214\u001b[0m \u001b[38;5;28mself\u001b[39m, program_name: \u001b[38;5;28mstr\u001b[39m, program_api_key: \u001b[38;5;28mstr\u001b[39m\n\u001b[1;32m 215\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m requests\u001b[38;5;241m.\u001b[39mResponse:\n\u001b[1;32m 216\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 217\u001b[0m \u001b[38;5;124;03m Check program details are correct.\u001b[39;00m\n\u001b[1;32m 218\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 221\u001b[0m \u001b[38;5;124;03m :return: API response\u001b[39;00m\n\u001b[1;32m 222\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 223\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 224\u001b[0m \u001b[43m \u001b[49m\u001b[43mPROGRAM_URL\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mprogram_name\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mprogram_name\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mprogram_api_key\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mprogram_api_key\u001b[49m\n\u001b[1;32m 225\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/anaconda3/envs/winterapi/lib/python3.11/site-packages/backoff/_sync.py:105\u001b[0m, in \u001b[0;36mretry_exception..retry\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 96\u001b[0m details \u001b[38;5;241m=\u001b[39m {\n\u001b[1;32m 97\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtarget\u001b[39m\u001b[38;5;124m\"\u001b[39m: target,\n\u001b[1;32m 98\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124margs\u001b[39m\u001b[38;5;124m\"\u001b[39m: args,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 101\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124melapsed\u001b[39m\u001b[38;5;124m\"\u001b[39m: elapsed,\n\u001b[1;32m 102\u001b[0m }\n\u001b[1;32m 104\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 105\u001b[0m ret \u001b[38;5;241m=\u001b[39m \u001b[43mtarget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 106\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m exception \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 107\u001b[0m max_tries_exceeded \u001b[38;5;241m=\u001b[39m (tries \u001b[38;5;241m==\u001b[39m max_tries_value)\n", - "File \u001b[0;32m~/Code/winterapi/winterapi/messenger.py:87\u001b[0m, in \u001b[0;36mWinterAPI.get\u001b[0;34m(self, url, auth, **kwargs)\u001b[0m\n\u001b[1;32m 85\u001b[0m err \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mAPI call failed with \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mres\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mres\u001b[38;5;241m.\u001b[39mtext\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 86\u001b[0m logger\u001b[38;5;241m.\u001b[39merror(err)\n\u001b[0;32m---> 87\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(err)\n\u001b[1;32m 88\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m res\n", - "\u001b[0;31mValueError\u001b[0m: API call failed with ': {\"success\":false,\"msg\":\"Failed to validate credentials provided.\"}'" - ] - } - ], + "outputs": [], "source": [ "winter.add_program(\"2022A000\", \"a-fake-api-key\")" ] diff --git a/notebooks/Info_2a_ToO_Parameters.ipynb b/notebooks/Info_2a_ToO_Parameters.ipynb new file mode 100644 index 0000000..106571f --- /dev/null +++ b/notebooks/Info_2a_ToO_Parameters.ipynb @@ -0,0 +1,600 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "d331f783", + "metadata": {}, + "outputs": [], + "source": [ + "import logging\n", + "from IPython.display import JSON\n", + "import json\n", + "from winterapi import WinterAPI\n", + "from wintertoo.models import SummerFieldToO, SummerRaDecToO, WinterFieldToO, WinterRaDecToO" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "68657fec", + "metadata": {}, + "outputs": [], + "source": [ + "logging.getLogger(\"winterapi\").setLevel(\"DEBUG\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "75c8a14e", + "metadata": {}, + "outputs": [], + "source": [ + "winter = WinterAPI()" + ] + }, + { + "cell_type": "markdown", + "id": "bd4d474c", + "metadata": {}, + "source": [ + "Our credentials from the previous notebook will still be active!" + ] + }, + { + "cell_type": "markdown", + "id": "eb5f0374", + "metadata": {}, + "source": [ + "# Setting up a ToO" + ] + }, + { + "cell_type": "markdown", + "id": "e6a15b09", + "metadata": {}, + "source": [ + "Now we are going to set up a ToO. There are two ways to trigger a ToO:\n", + "* Using a ra/dec value\n", + "* Using a field value\n", + "\n", + "There are also two types of instruments to trigger: WINTER or SUMMER. \n", + "\n", + "That makes **4 different ToO objects to choose from*.\n", + "\n", + "Let's go through the procedure for a Summer ToO using a field." + ] + }, + { + "cell_type": "markdown", + "id": "504be7f4", + "metadata": {}, + "source": [ + "## Step 1: What can go into a ToO request?" + ] + }, + { + "cell_type": "markdown", + "id": "3a212a51", + "metadata": {}, + "source": [ + "For `winterapi`, we handle data using `pydantic`. We specify what types of data can be provided, what the conditions are, and what the default values are.\n", + "\n", + "You can see all options in a human-readable way by converting the data model to a json schema:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "15e67d82", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"additionalProperties\": false,\n", + " \"description\": \"Winter ToO Request with field\",\n", + " \"properties\": {\n", + " \"field_id\": {\n", + " \"minimum\": 1,\n", + " \"title\": \"Field ID\",\n", + " \"type\": \"integer\"\n", + " },\n", + " \"filters\": {\n", + " \"default\": [\n", + " \"Y\",\n", + " \"J\",\n", + " \"Hs\"\n", + " ],\n", + " \"items\": {\n", + " \"enum\": [\n", + " \"dark\",\n", + " \"Y\",\n", + " \"J\",\n", + " \"Hs\"\n", + " ],\n", + " \"type\": \"string\"\n", + " },\n", + " \"title\": \"Filters\",\n", + " \"type\": \"array\"\n", + " },\n", + " \"target_priority\": {\n", + " \"default\": 50.0,\n", + " \"minimum\": 0.0,\n", + " \"title\": \"Priority for target\",\n", + " \"type\": \"number\"\n", + " },\n", + " \"t_exp\": {\n", + " \"default\": 30.0,\n", + " \"minimum\": 1.0,\n", + " \"title\": \"Combined Exposure Time across dithers (s)\",\n", + " \"type\": \"number\"\n", + " },\n", + " \"n_exp\": {\n", + " \"default\": 1,\n", + " \"minimum\": 1,\n", + " \"title\": \"Number of dither sets\",\n", + " \"type\": \"integer\"\n", + " },\n", + " \"n_dither\": {\n", + " \"default\": 1,\n", + " \"minimum\": 1,\n", + " \"title\": \"Number of dithers\",\n", + " \"type\": \"integer\"\n", + " },\n", + " \"dither_distance\": {\n", + " \"default\": 30.0,\n", + " \"minimum\": 0.0,\n", + " \"title\": \"dither distance (arcsec)\",\n", + " \"type\": \"number\"\n", + " },\n", + " \"start_time_mjd\": {\n", + " \"anyOf\": [\n", + " {\n", + " \"type\": \"number\"\n", + " },\n", + " {\n", + " \"type\": \"null\"\n", + " }\n", + " ],\n", + " \"default\": 60341.0835267944,\n", + " \"title\": \"ToO validity start (MJD)\"\n", + " },\n", + " \"end_time_mjd\": {\n", + " \"anyOf\": [\n", + " {\n", + " \"minimum\": 60341.07352680047,\n", + " \"type\": \"number\"\n", + " },\n", + " {\n", + " \"type\": \"null\"\n", + " }\n", + " ],\n", + " \"default\": 60342.07352679921,\n", + " \"title\": \"ToO validity end (MJD)\"\n", + " },\n", + " \"max_airmass\": {\n", + " \"anyOf\": [\n", + " {\n", + " \"maximum\": 5,\n", + " \"minimum\": 1,\n", + " \"type\": \"number\"\n", + " },\n", + " {\n", + " \"type\": \"null\"\n", + " }\n", + " ],\n", + " \"default\": 2.0,\n", + " \"title\": \"Allowed airmass range\"\n", + " }\n", + " },\n", + " \"required\": [\n", + " \"field_id\"\n", + " ],\n", + " \"title\": \"WinterFieldToO\",\n", + " \"type\": \"object\"\n", + "}\n" + ] + } + ], + "source": [ + "print(json.dumps(WinterFieldToO.model_json_schema(), indent=2))" + ] + }, + { + "cell_type": "markdown", + "id": "56fceaf1", + "metadata": {}, + "source": [ + "There is a lot of information, but the most important line is right at the bottom. The only thing that is required is `field_id`. Everything else is optional.\n", + "\n", + "We can initialise the ToO using a random field number:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "3dcfcdae", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "WinterFieldToO(field_id=55, filters=['Y', 'J', 'Hs'], target_priority=50.0, t_exp=30.0, n_exp=1, n_dither=1, dither_distance=30.0, start_time_mjd=60341.0835267944, end_time_mjd=60342.07352679921, max_airmass=2.0)" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "too = WinterFieldToO(field_id=55)\n", + "too" + ] + }, + { + "cell_type": "markdown", + "id": "43a49362", + "metadata": {}, + "source": [ + "As you can see, we got a full ToO request with sensible defaults. If we only wanted one filter rather than 3, we could specify this:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "6605d149", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "WinterFieldToO(field_id=55, filters=['J'], target_priority=50.0, t_exp=30.0, n_exp=1, n_dither=1, dither_distance=30.0, start_time_mjd=60341.0835267944, end_time_mjd=60342.07352679921, max_airmass=2.0)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "too = WinterFieldToO(field_id=55, filters=[\"J\"])\n", + "too" + ] + }, + { + "cell_type": "markdown", + "id": "54ef13e4", + "metadata": {}, + "source": [ + "Pydantic also validates the data we enter, to make sure things are sensible. Here are some examples of things you can't do:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "217812b9", + "metadata": {}, + "outputs": [ + { + "ename": "ValidationError", + "evalue": "1 validation error for WinterFieldToO\nfilters.0\n Input should be 'dark', 'Y', 'J' or 'Hs' [type=literal_error, input_value='V', input_type=str]\n For further information visit https://errors.pydantic.dev/2.3/v/literal_error", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValidationError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[7], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# Names a filter that isn't available\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m too \u001b[38;5;241m=\u001b[39m \u001b[43mWinterFieldToO\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfield_id\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m55\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfilters\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mV\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3\u001b[0m too\n", + "File \u001b[0;32m~/anaconda3/envs/winterapi/lib/python3.11/site-packages/pydantic/main.py:165\u001b[0m, in \u001b[0;36mBaseModel.__init__\u001b[0;34m(__pydantic_self__, **data)\u001b[0m\n\u001b[1;32m 163\u001b[0m \u001b[38;5;66;03m# `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks\u001b[39;00m\n\u001b[1;32m 164\u001b[0m __tracebackhide__ \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[0;32m--> 165\u001b[0m \u001b[43m__pydantic_self__\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m__pydantic_validator__\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate_python\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mself_instance\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m__pydantic_self__\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[0;31mValidationError\u001b[0m: 1 validation error for WinterFieldToO\nfilters.0\n Input should be 'dark', 'Y', 'J' or 'Hs' [type=literal_error, input_value='V', input_type=str]\n For further information visit https://errors.pydantic.dev/2.3/v/literal_error" + ] + } + ], + "source": [ + "# Names a filter that isn't available\n", + "too = WinterFieldToO(field_id=55, filters=[\"V\"])\n", + "too" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "00b582dd", + "metadata": {}, + "outputs": [ + { + "ename": "ValidationError", + "evalue": "1 validation error for WinterFieldToO\nend_time_mjd\n Input should be greater than or equal to 60341.07352680047 [type=greater_than_equal, input_value=58000.0, input_type=float]\n For further information visit https://errors.pydantic.dev/2.3/v/greater_than_equal", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValidationError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[8], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# end time in the past\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m too \u001b[38;5;241m=\u001b[39m \u001b[43mWinterFieldToO\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfield_id\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m55\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mend_time_mjd\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m58000.\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3\u001b[0m too\n", + "File \u001b[0;32m~/anaconda3/envs/winterapi/lib/python3.11/site-packages/pydantic/main.py:165\u001b[0m, in \u001b[0;36mBaseModel.__init__\u001b[0;34m(__pydantic_self__, **data)\u001b[0m\n\u001b[1;32m 163\u001b[0m \u001b[38;5;66;03m# `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks\u001b[39;00m\n\u001b[1;32m 164\u001b[0m __tracebackhide__ \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[0;32m--> 165\u001b[0m \u001b[43m__pydantic_self__\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m__pydantic_validator__\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate_python\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mself_instance\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m__pydantic_self__\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[0;31mValidationError\u001b[0m: 1 validation error for WinterFieldToO\nend_time_mjd\n Input should be greater than or equal to 60341.07352680047 [type=greater_than_equal, input_value=58000.0, input_type=float]\n For further information visit https://errors.pydantic.dev/2.3/v/greater_than_equal" + ] + } + ], + "source": [ + "# end time in the past\n", + "too = WinterFieldToO(field_id=55, end_time_mjd=58000.)\n", + "too" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "7133e738", + "metadata": {}, + "outputs": [ + { + "ename": "ValidationError", + "evalue": "1 validation error for WinterFieldToO\nn_dither\n Input should be greater than or equal to 1 [type=greater_than_equal, input_value=0, input_type=int]\n For further information visit https://errors.pydantic.dev/2.3/v/greater_than_equal", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValidationError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[9], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# Chooses a nonsensical n_dither number\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m too \u001b[38;5;241m=\u001b[39m \u001b[43mWinterFieldToO\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfield_id\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m55\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn_dither\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3\u001b[0m too\n", + "File \u001b[0;32m~/anaconda3/envs/winterapi/lib/python3.11/site-packages/pydantic/main.py:165\u001b[0m, in \u001b[0;36mBaseModel.__init__\u001b[0;34m(__pydantic_self__, **data)\u001b[0m\n\u001b[1;32m 163\u001b[0m \u001b[38;5;66;03m# `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks\u001b[39;00m\n\u001b[1;32m 164\u001b[0m __tracebackhide__ \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[0;32m--> 165\u001b[0m \u001b[43m__pydantic_self__\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m__pydantic_validator__\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate_python\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mself_instance\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m__pydantic_self__\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[0;31mValidationError\u001b[0m: 1 validation error for WinterFieldToO\nn_dither\n Input should be greater than or equal to 1 [type=greater_than_equal, input_value=0, input_type=int]\n For further information visit https://errors.pydantic.dev/2.3/v/greater_than_equal" + ] + } + ], + "source": [ + "# Chooses a nonsensical n_dither number\n", + "too = WinterFieldToO(field_id=55, n_dither=0)\n", + "too" + ] + }, + { + "cell_type": "markdown", + "id": "1371556c", + "metadata": {}, + "source": [ + "## Step 2 Choosing a ToO using ra/dec" + ] + }, + { + "cell_type": "markdown", + "id": "7614d0d0", + "metadata": {}, + "source": [ + "If you know the field to observe, then you have all the information you need, because each grid has a unique ra/dec value for that field.\n", + "The API will look that value up, so you do not need to worry about it.\n", + "\n", + "However, more often, if you are performing a ToO, it's because you know the ra/dec of a specific object.\n", + "\n", + "In that case, we can use a Ra/Dec model. Here's one with Winter:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "8f56c276", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"additionalProperties\": false,\n", + " \"description\": \"Winter ToO Request with Ra/Dec\",\n", + " \"properties\": {\n", + " \"ra_deg\": {\n", + " \"example\": 180.0,\n", + " \"maximum\": 360.0,\n", + " \"minimum\": 0.0,\n", + " \"title\": \"Right ascension in decimal degrees\",\n", + " \"type\": \"number\"\n", + " },\n", + " \"dec_deg\": {\n", + " \"example\": 0.0,\n", + " \"maximum\": 90.0,\n", + " \"minimum\": -90.0,\n", + " \"title\": \"Declination in decimal degrees\",\n", + " \"type\": \"number\"\n", + " },\n", + " \"use_field_grid\": {\n", + " \"default\": true,\n", + " \"title\": \"boolean whether to select nearest field in grid for central ra/dec\",\n", + " \"type\": \"boolean\"\n", + " },\n", + " \"filters\": {\n", + " \"default\": [\n", + " \"Y\",\n", + " \"J\",\n", + " \"Hs\"\n", + " ],\n", + " \"items\": {\n", + " \"enum\": [\n", + " \"dark\",\n", + " \"Y\",\n", + " \"J\",\n", + " \"Hs\"\n", + " ],\n", + " \"type\": \"string\"\n", + " },\n", + " \"title\": \"Filters\",\n", + " \"type\": \"array\"\n", + " },\n", + " \"target_priority\": {\n", + " \"default\": 50.0,\n", + " \"minimum\": 0.0,\n", + " \"title\": \"Priority for target\",\n", + " \"type\": \"number\"\n", + " },\n", + " \"t_exp\": {\n", + " \"default\": 30.0,\n", + " \"minimum\": 1.0,\n", + " \"title\": \"Combined Exposure Time across dithers (s)\",\n", + " \"type\": \"number\"\n", + " },\n", + " \"n_exp\": {\n", + " \"default\": 1,\n", + " \"minimum\": 1,\n", + " \"title\": \"Number of dither sets\",\n", + " \"type\": \"integer\"\n", + " },\n", + " \"n_dither\": {\n", + " \"default\": 1,\n", + " \"minimum\": 1,\n", + " \"title\": \"Number of dithers\",\n", + " \"type\": \"integer\"\n", + " },\n", + " \"dither_distance\": {\n", + " \"default\": 30.0,\n", + " \"minimum\": 0.0,\n", + " \"title\": \"dither distance (arcsec)\",\n", + " \"type\": \"number\"\n", + " },\n", + " \"start_time_mjd\": {\n", + " \"anyOf\": [\n", + " {\n", + " \"type\": \"number\"\n", + " },\n", + " {\n", + " \"type\": \"null\"\n", + " }\n", + " ],\n", + " \"default\": 60341.0835267944,\n", + " \"title\": \"ToO validity start (MJD)\"\n", + " },\n", + " \"end_time_mjd\": {\n", + " \"anyOf\": [\n", + " {\n", + " \"minimum\": 60341.07352680047,\n", + " \"type\": \"number\"\n", + " },\n", + " {\n", + " \"type\": \"null\"\n", + " }\n", + " ],\n", + " \"default\": 60342.07352679921,\n", + " \"title\": \"ToO validity end (MJD)\"\n", + " },\n", + " \"max_airmass\": {\n", + " \"anyOf\": [\n", + " {\n", + " \"maximum\": 5,\n", + " \"minimum\": 1,\n", + " \"type\": \"number\"\n", + " },\n", + " {\n", + " \"type\": \"null\"\n", + " }\n", + " ],\n", + " \"default\": 2.0,\n", + " \"title\": \"Allowed airmass range\"\n", + " }\n", + " },\n", + " \"required\": [\n", + " \"ra_deg\",\n", + " \"dec_deg\"\n", + " ],\n", + " \"title\": \"WinterRaDecToO\",\n", + " \"type\": \"object\"\n", + "}\n" + ] + } + ], + "source": [ + "print(json.dumps(WinterRaDecToO.model_json_schema(), indent=2))" + ] + }, + { + "cell_type": "markdown", + "id": "cc4aba3a", + "metadata": {}, + "source": [ + "In this case, we only need to provide the ra and dec:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "266c0940", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "WinterRaDecToO(ra_deg=300.0, dec_deg=51.0, use_field_grid=True, filters=['Y', 'J', 'Hs'], target_priority=50.0, t_exp=30.0, n_exp=1, n_dither=1, dither_distance=30.0, start_time_mjd=60341.0835267944, end_time_mjd=60342.07352679921, max_airmass=2.0)" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "too = WinterRaDecToO(ra_deg=300., dec_deg=51.)\n", + "too" + ] + }, + { + "cell_type": "markdown", + "id": "5bdf48c2", + "metadata": {}, + "source": [ + "**One important note: by default, a schedule with an ra/dec will be matched to the nearest field, and the exposure will be centered on that field RA/dec**\n", + " \n", + "You can disable this behaviour by setting `use_field_grid` to false! " + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "42a4c48c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "WinterRaDecToO(ra_deg=300.0, dec_deg=51.0, use_field_grid=False, filters=['Y', 'J', 'Hs'], target_priority=50.0, t_exp=30.0, n_exp=1, n_dither=1, dither_distance=30.0, start_time_mjd=60341.0835267944, end_time_mjd=60342.07352679921, max_airmass=2.0)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "too = WinterRaDecToO(ra_deg=300., dec_deg=51., use_field_grid=False)\n", + "too" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4c0bada9", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "winterapi", + "language": "python", + "name": "winterapi" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/2_submitting_toos.ipynb b/notebooks/Info_2b_Submitting_ToOs.ipynb similarity index 57% rename from notebooks/2_submitting_toos.ipynb rename to notebooks/Info_2b_Submitting_ToOs.ipynb index 59b2bc9..4bccffd 100644 --- a/notebooks/2_submitting_toos.ipynb +++ b/notebooks/Info_2b_Submitting_ToOs.ipynb @@ -10,7 +10,7 @@ "import logging\n", "import pandas as pd\n", "from winterapi import WinterAPI\n", - "from wintertoo.models import SummerFieldToO, SummerRaDecToO, WinterFieldToO, WinterRaDecToO" + "from wintertoo.models import WinterFieldToO, WinterRaDecToO" ] }, { @@ -58,8 +58,8 @@ { "data": { "text/plain": [ - "[SummerFieldToO(field_id=55, filters=['u', 'g', 'r', 'i'], target_priority=50.0, t_exp=30.0, n_exp=1, n_dither=1, dither_distance=15.0, start_time_mjd=60103.72832172684, end_time_mjd=60104.71832173089, max_airmass=2.0),\n", - " SummerFieldToO(field_id=56, filters=['u', 'g', 'r', 'i'], target_priority=50.0, t_exp=30.0, n_exp=1, n_dither=1, dither_distance=15.0, start_time_mjd=60103.72832172684, end_time_mjd=60104.71832173089, max_airmass=2.0)]" + "[WinterFieldToO(field_id=55, filters=['Y', 'J', 'Hs'], target_priority=50.0, t_exp=30.0, n_exp=1, n_dither=1, dither_distance=30.0, start_time_mjd=60341.08759327358, end_time_mjd=60342.077593278416, max_airmass=2.0),\n", + " WinterFieldToO(field_id=56, filters=['Y', 'J', 'Hs'], target_priority=50.0, t_exp=30.0, n_exp=1, n_dither=1, dither_distance=30.0, start_time_mjd=60341.08759327358, end_time_mjd=60342.077593278416, max_airmass=2.0)]" ] }, "execution_count": 4, @@ -68,8 +68,8 @@ } ], "source": [ - "too_1 = SummerFieldToO(field_id=55)\n", - "too_2 = SummerFieldToO(field_id=56)\n", + "too_1 = WinterFieldToO(field_id=55)\n", + "too_2 = WinterFieldToO(field_id=56)\n", "\n", "too_list = [too_1, too_2]\n", "too_list" @@ -103,7 +103,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Available programs: ['2020A000', '2020B000']\n" + "Available programs: ['202XA000']\n" ] } ], @@ -121,23 +121,12 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "f534541c", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Program(progname='2020A000', prog_key='a-secure-api-key', puid=None, progid=1, pi_name='Stein', pi_email='rdstein@caltech.edu', startdate=datetime.date(2023, 5, 1), enddate=datetime.date(2024, 12, 31), hours_allocated=10.0, hours_remaining=10.0, maxpriority=150.0, progtitle='\"Dummy program 1\"')" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "winter.get_program_details(\"2020A000\")" + "winter.get_program_details(\"202XA000\")" ] }, { @@ -197,189 +186,145 @@ " \n", " 0\n", " 259.2\n", - " 88.9593\n", + " 86.0\n", " 55\n", - " u\n", + " Y\n", " 30.0\n", " 50.0\n", " Stein\n", - " 2020A000\n", + " 202XA000\n", " 1\n", - " 60103.728322\n", - " 60104.718322\n", + " 60341.087593\n", + " 60342.077593\n", " False\n", " 2.0\n", " 1\n", - " 15.0\n", + " 30.0\n", " 0\n", " \n", " \n", " 1\n", " 259.2\n", - " 88.9593\n", + " 86.0\n", " 55\n", - " g\n", + " J\n", " 30.0\n", " 50.0\n", " Stein\n", - " 2020A000\n", + " 202XA000\n", " 1\n", - " 60103.728322\n", - " 60104.718322\n", + " 60341.087593\n", + " 60342.077593\n", " False\n", " 2.0\n", " 1\n", - " 15.0\n", + " 30.0\n", " 1\n", " \n", " \n", " 2\n", " 259.2\n", - " 88.9593\n", + " 86.0\n", " 55\n", - " r\n", + " Hs\n", " 30.0\n", " 50.0\n", " Stein\n", - " 2020A000\n", + " 202XA000\n", " 1\n", - " 60103.728322\n", - " 60104.718322\n", + " 60341.087593\n", + " 60342.077593\n", " False\n", " 2.0\n", " 1\n", - " 15.0\n", + " 30.0\n", " 2\n", " \n", " \n", " 3\n", - " 259.2\n", - " 88.9593\n", - " 55\n", - " i\n", + " 273.6\n", + " 86.0\n", + " 56\n", + " Y\n", " 30.0\n", " 50.0\n", " Stein\n", - " 2020A000\n", + " 202XA000\n", " 1\n", - " 60103.728322\n", - " 60104.718322\n", + " 60341.087593\n", + " 60342.077593\n", " False\n", " 2.0\n", " 1\n", - " 15.0\n", + " 30.0\n", " 3\n", " \n", " \n", " 4\n", " 273.6\n", - " 88.9593\n", + " 86.0\n", " 56\n", - " u\n", + " J\n", " 30.0\n", " 50.0\n", " Stein\n", - " 2020A000\n", + " 202XA000\n", " 1\n", - " 60103.728322\n", - " 60104.718322\n", + " 60341.087593\n", + " 60342.077593\n", " False\n", " 2.0\n", " 1\n", - " 15.0\n", + " 30.0\n", " 4\n", " \n", " \n", " 5\n", " 273.6\n", - " 88.9593\n", + " 86.0\n", " 56\n", - " g\n", + " Hs\n", " 30.0\n", " 50.0\n", " Stein\n", - " 2020A000\n", + " 202XA000\n", " 1\n", - " 60103.728322\n", - " 60104.718322\n", + " 60341.087593\n", + " 60342.077593\n", " False\n", " 2.0\n", " 1\n", - " 15.0\n", - " 5\n", - " \n", - " \n", - " 6\n", - " 273.6\n", - " 88.9593\n", - " 56\n", - " r\n", " 30.0\n", - " 50.0\n", - " Stein\n", - " 2020A000\n", - " 1\n", - " 60103.728322\n", - " 60104.718322\n", - " False\n", - " 2.0\n", - " 1\n", - " 15.0\n", - " 6\n", - " \n", - " \n", - " 7\n", - " 273.6\n", - " 88.9593\n", - " 56\n", - " i\n", - " 30.0\n", - " 50.0\n", - " Stein\n", - " 2020A000\n", - " 1\n", - " 60103.728322\n", - " 60104.718322\n", - " False\n", - " 2.0\n", - " 1\n", - " 15.0\n", - " 7\n", + " 5\n", " \n", " \n", "\n", "" ], "text/plain": [ - " raDeg decDeg fieldID filter visitExpTime priority progPI progName \\\n", - "0 259.2 88.9593 55 u 30.0 50.0 Stein 2020A000 \n", - "1 259.2 88.9593 55 g 30.0 50.0 Stein 2020A000 \n", - "2 259.2 88.9593 55 r 30.0 50.0 Stein 2020A000 \n", - "3 259.2 88.9593 55 i 30.0 50.0 Stein 2020A000 \n", - "4 273.6 88.9593 56 u 30.0 50.0 Stein 2020A000 \n", - "5 273.6 88.9593 56 g 30.0 50.0 Stein 2020A000 \n", - "6 273.6 88.9593 56 r 30.0 50.0 Stein 2020A000 \n", - "7 273.6 88.9593 56 i 30.0 50.0 Stein 2020A000 \n", + " raDeg decDeg fieldID filter visitExpTime priority progPI progName \\\n", + "0 259.2 86.0 55 Y 30.0 50.0 Stein 202XA000 \n", + "1 259.2 86.0 55 J 30.0 50.0 Stein 202XA000 \n", + "2 259.2 86.0 55 Hs 30.0 50.0 Stein 202XA000 \n", + "3 273.6 86.0 56 Y 30.0 50.0 Stein 202XA000 \n", + "4 273.6 86.0 56 J 30.0 50.0 Stein 202XA000 \n", + "5 273.6 86.0 56 Hs 30.0 50.0 Stein 202XA000 \n", "\n", " progID validStart validStop observed maxAirmass ditherNumber \\\n", - "0 1 60103.728322 60104.718322 False 2.0 1 \n", - "1 1 60103.728322 60104.718322 False 2.0 1 \n", - "2 1 60103.728322 60104.718322 False 2.0 1 \n", - "3 1 60103.728322 60104.718322 False 2.0 1 \n", - "4 1 60103.728322 60104.718322 False 2.0 1 \n", - "5 1 60103.728322 60104.718322 False 2.0 1 \n", - "6 1 60103.728322 60104.718322 False 2.0 1 \n", - "7 1 60103.728322 60104.718322 False 2.0 1 \n", + "0 1 60341.087593 60342.077593 False 2.0 1 \n", + "1 1 60341.087593 60342.077593 False 2.0 1 \n", + "2 1 60341.087593 60342.077593 False 2.0 1 \n", + "3 1 60341.087593 60342.077593 False 2.0 1 \n", + "4 1 60341.087593 60342.077593 False 2.0 1 \n", + "5 1 60341.087593 60342.077593 False 2.0 1 \n", "\n", " ditherStepSize obsHistID \n", - "0 15.0 0 \n", - "1 15.0 1 \n", - "2 15.0 2 \n", - "3 15.0 3 \n", - "4 15.0 4 \n", - "5 15.0 5 \n", - "6 15.0 6 \n", - "7 15.0 7 " + "0 30.0 0 \n", + "1 30.0 1 \n", + "2 30.0 2 \n", + "3 30.0 3 \n", + "4 30.0 4 \n", + "5 30.0 5 " ] }, "execution_count": 7, @@ -389,7 +334,7 @@ ], "source": [ "local_schedule = winter.build_schedule_locally(\n", - " program_name=\"2020A000\",\n", + " program_name=\"202XA000\",\n", " data=too_list\n", ")\n", "local_schedule" @@ -416,8 +361,8 @@ "metadata": {}, "outputs": [], "source": [ - "api_res, api_schedule = winter.submit_too_summer(\n", - " program_name=\"2020A000\",\n", + "api_res, api_schedule = winter.submit_too(\n", + " program_name=\"202XA000\",\n", " data=too_list,\n", " submit_trigger=False\n", ")" @@ -480,189 +425,145 @@ " \n", " 0\n", " 259.2\n", - " 88.9593\n", + " 86.0\n", " 55\n", - " u\n", + " Y\n", " 30.0\n", " 50.0\n", " Stein\n", - " 2020A000\n", + " 202XA000\n", " 1\n", - " 60103.728322\n", - " 60104.718322\n", + " 60341.087593\n", + " 60342.077593\n", " False\n", " 2.0\n", " 1\n", - " 15.0\n", + " 30.0\n", " 0\n", " \n", " \n", " 1\n", " 259.2\n", - " 88.9593\n", + " 86.0\n", " 55\n", - " g\n", + " J\n", " 30.0\n", " 50.0\n", " Stein\n", - " 2020A000\n", + " 202XA000\n", " 1\n", - " 60103.728322\n", - " 60104.718322\n", + " 60341.087593\n", + " 60342.077593\n", " False\n", " 2.0\n", " 1\n", - " 15.0\n", + " 30.0\n", " 1\n", " \n", " \n", " 2\n", " 259.2\n", - " 88.9593\n", + " 86.0\n", " 55\n", - " r\n", + " Hs\n", " 30.0\n", " 50.0\n", " Stein\n", - " 2020A000\n", + " 202XA000\n", " 1\n", - " 60103.728322\n", - " 60104.718322\n", + " 60341.087593\n", + " 60342.077593\n", " False\n", " 2.0\n", " 1\n", - " 15.0\n", + " 30.0\n", " 2\n", " \n", " \n", " 3\n", - " 259.2\n", - " 88.9593\n", - " 55\n", - " i\n", + " 273.6\n", + " 86.0\n", + " 56\n", + " Y\n", " 30.0\n", " 50.0\n", " Stein\n", - " 2020A000\n", + " 202XA000\n", " 1\n", - " 60103.728322\n", - " 60104.718322\n", + " 60341.087593\n", + " 60342.077593\n", " False\n", " 2.0\n", " 1\n", - " 15.0\n", + " 30.0\n", " 3\n", " \n", " \n", " 4\n", " 273.6\n", - " 88.9593\n", + " 86.0\n", " 56\n", - " u\n", + " J\n", " 30.0\n", " 50.0\n", " Stein\n", - " 2020A000\n", + " 202XA000\n", " 1\n", - " 60103.728322\n", - " 60104.718322\n", + " 60341.087593\n", + " 60342.077593\n", " False\n", " 2.0\n", " 1\n", - " 15.0\n", + " 30.0\n", " 4\n", " \n", " \n", " 5\n", " 273.6\n", - " 88.9593\n", + " 86.0\n", " 56\n", - " g\n", + " Hs\n", " 30.0\n", " 50.0\n", " Stein\n", - " 2020A000\n", + " 202XA000\n", " 1\n", - " 60103.728322\n", - " 60104.718322\n", + " 60341.087593\n", + " 60342.077593\n", " False\n", " 2.0\n", " 1\n", - " 15.0\n", - " 5\n", - " \n", - " \n", - " 6\n", - " 273.6\n", - " 88.9593\n", - " 56\n", - " r\n", " 30.0\n", - " 50.0\n", - " Stein\n", - " 2020A000\n", - " 1\n", - " 60103.728322\n", - " 60104.718322\n", - " False\n", - " 2.0\n", - " 1\n", - " 15.0\n", - " 6\n", - " \n", - " \n", - " 7\n", - " 273.6\n", - " 88.9593\n", - " 56\n", - " i\n", - " 30.0\n", - " 50.0\n", - " Stein\n", - " 2020A000\n", - " 1\n", - " 60103.728322\n", - " 60104.718322\n", - " False\n", - " 2.0\n", - " 1\n", - " 15.0\n", - " 7\n", + " 5\n", " \n", " \n", "\n", "" ], "text/plain": [ - " raDeg decDeg fieldID filter visitExpTime priority progPI progName \\\n", - "0 259.2 88.9593 55 u 30.0 50.0 Stein 2020A000 \n", - "1 259.2 88.9593 55 g 30.0 50.0 Stein 2020A000 \n", - "2 259.2 88.9593 55 r 30.0 50.0 Stein 2020A000 \n", - "3 259.2 88.9593 55 i 30.0 50.0 Stein 2020A000 \n", - "4 273.6 88.9593 56 u 30.0 50.0 Stein 2020A000 \n", - "5 273.6 88.9593 56 g 30.0 50.0 Stein 2020A000 \n", - "6 273.6 88.9593 56 r 30.0 50.0 Stein 2020A000 \n", - "7 273.6 88.9593 56 i 30.0 50.0 Stein 2020A000 \n", + " raDeg decDeg fieldID filter visitExpTime priority progPI progName \\\n", + "0 259.2 86.0 55 Y 30.0 50.0 Stein 202XA000 \n", + "1 259.2 86.0 55 J 30.0 50.0 Stein 202XA000 \n", + "2 259.2 86.0 55 Hs 30.0 50.0 Stein 202XA000 \n", + "3 273.6 86.0 56 Y 30.0 50.0 Stein 202XA000 \n", + "4 273.6 86.0 56 J 30.0 50.0 Stein 202XA000 \n", + "5 273.6 86.0 56 Hs 30.0 50.0 Stein 202XA000 \n", "\n", " progID validStart validStop observed maxAirmass ditherNumber \\\n", - "0 1 60103.728322 60104.718322 False 2.0 1 \n", - "1 1 60103.728322 60104.718322 False 2.0 1 \n", - "2 1 60103.728322 60104.718322 False 2.0 1 \n", - "3 1 60103.728322 60104.718322 False 2.0 1 \n", - "4 1 60103.728322 60104.718322 False 2.0 1 \n", - "5 1 60103.728322 60104.718322 False 2.0 1 \n", - "6 1 60103.728322 60104.718322 False 2.0 1 \n", - "7 1 60103.728322 60104.718322 False 2.0 1 \n", + "0 1 60341.087593 60342.077593 False 2.0 1 \n", + "1 1 60341.087593 60342.077593 False 2.0 1 \n", + "2 1 60341.087593 60342.077593 False 2.0 1 \n", + "3 1 60341.087593 60342.077593 False 2.0 1 \n", + "4 1 60341.087593 60342.077593 False 2.0 1 \n", + "5 1 60341.087593 60342.077593 False 2.0 1 \n", "\n", " ditherStepSize obsHistID \n", - "0 15.0 0 \n", - "1 15.0 1 \n", - "2 15.0 2 \n", - "3 15.0 3 \n", - "4 15.0 4 \n", - "5 15.0 5 \n", - "6 15.0 6 \n", - "7 15.0 7 " + "0 30.0 0 \n", + "1 30.0 1 \n", + "2 30.0 2 \n", + "3 30.0 3 \n", + "4 30.0 4 \n", + "5 30.0 5 " ] }, "execution_count": 9, @@ -697,8 +598,8 @@ "source": [ "# An example ToO that would submit for real!\n", "#\n", - "# api_res, api_schedule = winter.submit_too_summer(\n", - "# program_name=\"2020A000\",\n", + "# api_res, api_schedule = winter.submit_too(\n", + "# program_name=\"202XA000\",\n", "# data=too_list,\n", "# submit_trigger=True\n", "# )\n", diff --git a/pyproject.toml b/pyproject.toml index ff1496a..240caa6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "winterapi" -version = "0.3.0" +version = "1.0.0" description = "" authors = [ {name = "Robert Stein", email = "rdstein@caltech.edu"} @@ -29,17 +29,18 @@ classifiers = [ ] dependencies = [ "pandas", + "packaging", "keyring", "cryptography", "pre-commit", "jupyter", "backoff", "pydantic", - "wintertoo>=0.4.2" + "wintertoo>=1.2.0" ] [project.optional-dependencies] dev = [ - "black == 23.11.0", + "black == 24.1.1", "isort == 5.13.0", "pylint == 3.0.2", "coveralls", diff --git a/tests/test_login.py b/tests/test_login.py index 502632b..fd40c4d 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -1,6 +1,7 @@ """ Module for testing the API """ + import logging import time import unittest diff --git a/tests/test_schedule.py b/tests/test_schedule.py index 6a70493..80115c6 100644 --- a/tests/test_schedule.py +++ b/tests/test_schedule.py @@ -1,6 +1,7 @@ """ Test for schedule """ + import logging import os import unittest @@ -42,6 +43,7 @@ def test_ra_dec(self): n_dither=9, start_time_mjd=62721.1894969287, end_time_mjd=62722.1894969452, + target_name="test_field", ) too_radec = WinterRaDecToO( @@ -50,6 +52,7 @@ def test_ra_dec(self): start_time_mjd=62721.1894969287, end_time_mjd=62722.1894969452, use_field_grid=False, + target_name="test_radec", ) too_list = [too_field, too_radec] diff --git a/tests/testdata/test_schedule.csv b/tests/testdata/test_schedule.csv index efedbdd..428fef9 100644 --- a/tests/testdata/test_schedule.csv +++ b/tests/testdata/test_schedule.csv @@ -1,7 +1,7 @@ -raDeg,decDeg,fieldID,filter,visitExpTime,priority,progPI,progName,progID,validStart,validStop,observed,maxAirmass,ditherNumber,ditherStepSize,obsHistID -211.56398,54.0,3944,Y,30.0,50.0,Stein,2023A999,0,62721.1894969287,62722.1894969452,False,2.0,9,600.0,0 -211.56398,54.0,3944,J,30.0,50.0,Stein,2023A999,0,62721.1894969287,62722.1894969452,False,2.0,9,600.0,1 -211.56398,54.0,3944,Hs,30.0,50.0,Stein,2023A999,0,62721.1894969287,62722.1894969452,False,2.0,9,600.0,2 -210.910674637,54.3116510708,999999999,Y,30.0,50.0,Stein,2023A999,0,62721.1894969287,62722.1894969452,False,2.0,1,600.0,3 -210.910674637,54.3116510708,999999999,J,30.0,50.0,Stein,2023A999,0,62721.1894969287,62722.1894969452,False,2.0,1,600.0,4 -210.910674637,54.3116510708,999999999,Hs,30.0,50.0,Stein,2023A999,0,62721.1894969287,62722.1894969452,False,2.0,1,600.0,5 +targName,raDeg,decDeg,fieldID,filter,visitExpTime,priority,progPI,progName,progID,validStart,validStop,observed,maxAirmass,ditherNumber,ditherStepSize,obsHistID +test_field,211.56398,54.0,3944,Y,30.0,50.0,Stein,2023A999,0,62721.1894969287,62722.1894969452,False,2.0,9,30.0,0 +test_field,211.56398,54.0,3944,J,30.0,50.0,Stein,2023A999,0,62721.1894969287,62722.1894969452,False,2.0,9,30.0,1 +test_field,211.56398,54.0,3944,Hs,30.0,50.0,Stein,2023A999,0,62721.1894969287,62722.1894969452,False,2.0,9,30.0,2 +test_radec,210.910674637,54.3116510708,999999999,Y,30.0,50.0,Stein,2023A999,0,62721.1894969287,62722.1894969452,False,2.0,1,30.0,3 +test_radec,210.910674637,54.3116510708,999999999,J,30.0,50.0,Stein,2023A999,0,62721.1894969287,62722.1894969452,False,2.0,1,30.0,4 +test_radec,210.910674637,54.3116510708,999999999,Hs,30.0,50.0,Stein,2023A999,0,62721.1894969287,62722.1894969452,False,2.0,1,30.0,5 diff --git a/winterapi/__init__.py b/winterapi/__init__.py index 13a099e..3529da5 100644 --- a/winterapi/__init__.py +++ b/winterapi/__init__.py @@ -1,4 +1,5 @@ """ WinterAPI is a Python toolkit for interacting with the Winter API. """ + from winterapi.messenger import WinterAPI diff --git a/winterapi/base_api.py b/winterapi/base_api.py new file mode 100644 index 0000000..4d8437b --- /dev/null +++ b/winterapi/base_api.py @@ -0,0 +1,192 @@ +""" +Module with the base class for generic API interactions +""" + +import json +import logging +import re +from pathlib import Path + +import backoff +import requests +from pydantic import BaseModel + +logger = logging.getLogger(__name__) + +MAX_TIMEOUT = 30.0 + + +class BaseAPI: + """ + Base class for interacting with the API + """ + + def get_auth(self): + """ + Get the authentication details. + + :return: Authentication details. + """ + raise NotImplementedError + + @staticmethod + def clean_data(data): + """ + Clean the data for the API. + + :param data: Data to clean. + :return: Cleaned data. + """ + + if isinstance(data, list): + convert = json.dumps([x.model_dump() for x in data]) + elif isinstance(data, BaseModel): + convert = json.dumps([data.model_dump()]) + else: + err = f"Unrecognised data type {type(data)}" + logger.error(err) + raise TypeError(err) + + return convert + + @backoff.on_exception( + backoff.expo, requests.exceptions.RequestException, max_time=MAX_TIMEOUT + ) + def get(self, url, auth=None, data=None, **kwargs) -> requests.Response: + """ + Run a get request. + + :param url: URL to get. + :param auth: Authentication details. + :param data: Data to get. + :param kwargs: additional arguments for API. + :return: API response. + """ + if auth is None: + auth = self.get_auth() + + if data is not None: + data = self.clean_data(data) + + res = requests.get( + url, data=data, auth=auth, params=kwargs, timeout=MAX_TIMEOUT + ) + + if res.status_code != 200: + err = f"API call failed with '{res}: {res.text}'" + logger.error(err) + raise ValueError(err) + return res + + @backoff.on_exception( + backoff.expo, requests.exceptions.RequestException, max_time=MAX_TIMEOUT + ) + def post( + self, url, data: BaseModel | list[BaseModel], auth=None, **kwargs + ) -> requests.Response: + """ + Run a post request. + + :param url: URL to post to. + :param data: Data to post. + :param auth: Authentication details. + :param kwargs: additional arguments for API. + :return: Response. + """ + if auth is None: + auth = self.get_auth() + + convert = self.clean_data(data) + + res = requests.post( + url, data=convert, auth=auth, params=kwargs, timeout=MAX_TIMEOUT + ) + + if res.status_code != 200: + err = f"API call failed with '{res}: {res.text}'" + logger.error(err) + raise ValueError(err) + return res + + @backoff.on_exception( + backoff.expo, requests.exceptions.RequestException, max_time=MAX_TIMEOUT + ) + def delete(self, url, auth=None, **kwargs) -> requests.Response: + """ + Run a delete request. + + :param url: URL to post to. + :param auth: Authentication details. + :param kwargs: additional arguments for API. + :return: Response. + """ + if auth is None: + auth = self.get_auth() + + res = requests.delete(url, auth=auth, params=kwargs, timeout=MAX_TIMEOUT) + + if res.status_code != 200: + err = f"API call failed with '{res}: {res.text}'" + logger.error(err) + raise ValueError(err) + return res + + @backoff.on_exception( + backoff.expo, requests.exceptions.RequestException, max_time=MAX_TIMEOUT + ) + def get_stream( + self, url, output_dir: str | Path | None = None, auth=None, data=None, **kwargs + ) -> tuple[requests.Response, Path]: + """ + Run a get request. + + :param url: URL to get. + :param output_dir: Directory to save the output. + :param auth: Authentication details. + :param kwargs: additional arguments for API. + :return: API response. + """ + if auth is None: + auth = self.get_auth() + + if data is not None: + data = self.clean_data(data) + + if output_dir is None: + output_dir = Path.home() + logger.warning(f"No output directory specified, using {output_dir}") + + if not isinstance(output_dir, Path): + output_dir = Path(output_dir) + + if not output_dir.parent.exists(): + output_dir.parent.mkdir(parents=True) + + fname = "winterapi_output.zip" + + with requests.Session() as session: + + with session.get( + url, + data=data, + auth=auth, + params=kwargs, + timeout=MAX_TIMEOUT, + stream=True, + ) as resp: + header = resp.headers + + if "Content-Disposition" in header.keys(): + fname = re.findall("filename=(.+)", header["Content-Disposition"])[ + 0 + ] + + output_path = output_dir.joinpath(fname) + + with open(output_path, "wb") as output_f: + for chunk in resp.iter_content(chunk_size=8192): + output_f.write(chunk) + + logger.info(f"Downloaded file to {output_path}") + + return resp, output_path diff --git a/winterapi/configure_tests.py b/winterapi/configure_tests.py index b749ecb..b0f885c 100644 --- a/winterapi/configure_tests.py +++ b/winterapi/configure_tests.py @@ -1,6 +1,7 @@ """ This script is used to configure the environment variables for the tests. """ + import os from winterapi import WinterAPI diff --git a/winterapi/credentials.py b/winterapi/credentials.py index 7b78e72..21853bb 100644 --- a/winterapi/credentials.py +++ b/winterapi/credentials.py @@ -1,6 +1,7 @@ """ Module for credentials """ + import base64 import json import logging diff --git a/winterapi/endpoints.py b/winterapi/endpoints.py index 7d9486c..3472459 100644 --- a/winterapi/endpoints.py +++ b/winterapi/endpoints.py @@ -1,10 +1,19 @@ """ Module for storing the endpoints for the API. """ -BASE_URL = "http://127.0.0.1:7000" -BASE_URL = "http://winter.caltech.edu:82" + +import os + +run_local = os.getenv("WINTER_API_LOCAL", "0") in ["True", "true", "1"] +BASE_URL = "http://127.0.0.1:7000" if run_local else "http://winter.caltech.edu:82" PING_URL = BASE_URL + "/ping" +VERSION_URL = BASE_URL + "/validation/version" USER_URL = BASE_URL + "/validation/user" PROGRAM_URL = BASE_URL + "/validation/program" WINTER_TOO_URL = BASE_URL + "/too/winter" SUMMER_TOO_URL = BASE_URL + "/too/summer" +SCHEDULE_SUMMARY_URL = BASE_URL + "/too/summary" +SCHEDULE_DETAILS_URL = BASE_URL + "/too/details" +SCHEDULE_DELETE_URL = BASE_URL + "/too/delete" +IMAGE_QUERY_URL = BASE_URL + "/images/query" +DOWNLOAD_LIST_URL = BASE_URL + "/images/download_list" diff --git a/winterapi/fidelius.py b/winterapi/fidelius.py index 77047f7..a814c72 100644 --- a/winterapi/fidelius.py +++ b/winterapi/fidelius.py @@ -1,6 +1,7 @@ """ Fidelius is the keeper of secrets. It is the class that handles the secrets. """ + import logging import numpy as np diff --git a/winterapi/messenger.py b/winterapi/messenger.py index 0753c16..406b14a 100644 --- a/winterapi/messenger.py +++ b/winterapi/messenger.py @@ -1,16 +1,27 @@ """ This module contains the messenger, which is used to communicate with the API """ + import getpass -import json import logging +from importlib import metadata +from pathlib import Path from typing import Optional -import backoff import pandas as pd import requests -from pydantic import BaseModel -from wintertoo.models import Program +from astropy import units as u +from astropy.time import Time +from packaging import version +from wintertoo.data import DEFAULT_IMAGE_TYPE, WinterImageTypes +from wintertoo.models import ( + ConeImageQuery, + ImagePath, + Program, + ProgramImageQuery, + RectangleImageQuery, + TargetImageQuery, +) from wintertoo.models.too import ( AllTooClasses, Summer, @@ -21,30 +32,76 @@ WinterRaDecToO, ) from wintertoo.schedule import concat_toos +from wintertoo.utils import get_date +from winterapi.base_api import MAX_TIMEOUT, BaseAPI from winterapi.endpoints import ( + DOWNLOAD_LIST_URL, + IMAGE_QUERY_URL, PING_URL, PROGRAM_URL, + SCHEDULE_DELETE_URL, + SCHEDULE_DETAILS_URL, + SCHEDULE_SUMMARY_URL, SUMMER_TOO_URL, USER_URL, + VERSION_URL, WINTER_TOO_URL, ) from winterapi.fidelius import Fidelius logger = logging.getLogger(__name__) -MAX_TIMEOUT = 30 - -class WinterAPI: +class WinterAPI(BaseAPI): # pylint: disable=too-many-public-methods """ Class to communicate with the Winter API """ def __init__(self): self.fidelius = Fidelius() - logger.info(f"API ping success is {self.ping()}") + ping = self.ping() + if not ping: + logger.warning("Could not successfully ping server") + logger.info(f"API ping success is {ping}") self.auth = (None, None) + self.check_version() + + @staticmethod + def ping(): + """ + Ping the API. + + :return: boolean for success + """ + try: + res = requests.get(PING_URL, timeout=MAX_TIMEOUT) + res.raise_for_status() + return res.status_code == 200 + except requests.exceptions.ConnectionError: + return False + + @staticmethod + def check_version(): + """ + Check the version of the API. + + :return: API response + """ + res = requests.get(VERSION_URL, timeout=MAX_TIMEOUT) + if res.status_code == 200: + server_version = version.parse(res.json()["body"]) + local_version = version.parse(metadata.version("winterapi")) + logger.info(f"Server requires minimum winterapi version: {server_version}") + logger.info(f"Local winterapi version: {local_version}") + if server_version > local_version: + logger.warning( + f"Local winterapi version ({local_version}) is out of date! " + f"Server requires a minimum of {server_version}. " + f"Please update winterapi." + ) + else: + logger.warning("Could not check minimum version of winterapi for server") @staticmethod def clear_cache(): @@ -65,79 +122,6 @@ def get_auth(self): self.auth = (self.fidelius.get_user(), self.fidelius.get_password()) return self.auth - @backoff.on_exception( - backoff.expo, requests.exceptions.RequestException, max_time=MAX_TIMEOUT - ) - def get(self, url, auth=None, **kwargs) -> requests.Response: - """ - Run a get request. - - :param url: URL to get. - :param auth: Authentication details. - :param kwargs: additional arguments for API. - :return: API response. - """ - if auth is None: - auth = self.get_auth() - res = requests.get(url, auth=auth, params=kwargs, timeout=MAX_TIMEOUT) - - if res.status_code != 200: - err = f"API call failed with '{res}: {res.text}'" - logger.error(err) - raise ValueError(err) - return res - - @backoff.on_exception( - backoff.expo, requests.exceptions.RequestException, max_time=MAX_TIMEOUT - ) - def post( - self, url, data: AllTooClasses | list[AllTooClasses], auth=None, **kwargs - ) -> requests.Response: - """ - Run a post request. - - :param url: URL to post to. - :param data: Data to post. - :param auth: Authentication details. - :param kwargs: additional arguments for API. - :return: Response. - """ - if auth is None: - auth = self.get_auth() - - if isinstance(data, list): - convert = json.dumps([x.model_dump() for x in data]) - elif isinstance(data, BaseModel): - convert = json.dumps([data.model_dump()]) - else: - err = f"Unrecognised data type {type(data)}" - logger.error(err) - raise TypeError(err) - - res = requests.post( - url, data=convert, auth=auth, params=kwargs, timeout=MAX_TIMEOUT - ) - - if res.status_code != 200: - err = f"API call failed with '{res}: {res.text}'" - logger.error(err) - raise ValueError(err) - return res - - @staticmethod - def ping(): - """ - Ping the API. - - :return: boolean for success - """ - try: - res = requests.get(PING_URL, timeout=MAX_TIMEOUT) - res.raise_for_status() - return res.status_code == 200 - except ConnectionError: - return False - def add_user_details( self, user: Optional[str] = None, @@ -290,7 +274,7 @@ def submit_too( if not isinstance(data, list): data = [data] for entry in data: - assert isinstance(entry, Winter) + assert isinstance(entry, Winter), f"Entry {entry} is not a Winter ToO" return self._submit_too( program_name=program_name, url=WINTER_TOO_URL, @@ -335,3 +319,320 @@ def build_schedule_locally( """ program = self.get_program_details(program_name=program_name) return concat_toos(data, program=program) + + def get_observatory_queue( + self, + program_name: str, + ) -> tuple[requests.Response, pd.DataFrame]: + """ + Function to get the observatory queue + + :param program_name: Name of the program under which to check ToOs + :return: API response and TOO schedule + """ + + program = self.get_program_details(program_name=program_name) + + res = self.get( + SCHEDULE_SUMMARY_URL, + program_name=program_name, + program_api_key=program.prog_key, + ) + + observatory_queue = pd.DataFrame(res.json()["body"]) + return res, observatory_queue + + def get_too_details( + self, + program_name: str, + too_schedule_name: str, + ) -> tuple[requests.Response, pd.DataFrame]: + """ + Function to get the details of a single queued TOO schedule + + :param program_name: Name of the program under which TOO was submitted + :param too_schedule_name: Name of the TOO schedule + :return: API response and TOO schedule + """ + + program = self.get_program_details(program_name=program_name) + + res = self.get( + SCHEDULE_DETAILS_URL, + program_name=program_name, + program_api_key=program.prog_key, + schedule_name=too_schedule_name, + ) + + too_schedule = pd.DataFrame(res.json()["body"]) + return res, too_schedule + + def delete_too_request( + self, + program_name: str, + too_schedule_name: str, + ) -> requests.Response: + """ + Function to delete a queued TOO schedule + + :param program_name: Name of the program under which TOO was submitted + :param too_schedule_name: Name of the TOO schedule + :return: API response + """ + + program = self.get_program_details(program_name=program_name) + + res = self.delete( + SCHEDULE_DELETE_URL, + program_name=program_name, + program_api_key=program.prog_key, + schedule_name=too_schedule_name, + ) + + return res + + def query_images( + self, + query: ( + TargetImageQuery | RectangleImageQuery | ConeImageQuery | ProgramImageQuery + ), + ) -> tuple[requests.Response, pd.DataFrame]: + """ + Function to get the observatory queue + + :param query: Query Request + :return: API response and TOO schedule + """ + + program = self.get_program_details(program_name=query.program_name) + + res = self.get( + IMAGE_QUERY_URL, + program_name=query.program_name, + program_api_key=program.prog_key, + data=[query], + ) + + image_summary = pd.DataFrame(res.json()["body"]) + return res, image_summary + + @staticmethod + def check_query_dates( + start_date: str | None = None, + end_date: str | None = None, + ) -> tuple[str, str]: + """ + Function to check the dates + + :param start_date: Start date + :param end_date: End date + :return: Start and end date, in ISO format + """ + if start_date is None: + start_date = get_date(Time.now() - 30.0 * u.day) + + if end_date is None: + end_date = get_date(Time.now()) + + return start_date, end_date + + def query_images_by_program( + self, + program_name: str, + start_date: str | None = None, + end_date: str | None = None, + image_type: WinterImageTypes = DEFAULT_IMAGE_TYPE, + ) -> tuple[requests.Response, pd.DataFrame]: + """ + Function to get the observatory queue + + :param program_name: Name of the program under which to check ToOs + :param start_date: Start date for images + :param end_date: End date for images + :param image_type: Type of image to query + :return: API response and TOO schedule + """ + + start_date, end_date = self.check_query_dates( + start_date=start_date, end_date=end_date + ) + + print( + f"Querying images for {program_name} between " + f"{start_date} and {end_date} of type '{image_type}'" + ) + + query = ProgramImageQuery( + program_name=program_name, + start_date=start_date, + end_date=end_date, + kind=image_type, + ) + + return self.query_images(query=query) + + def query_images_by_target_name( # pylint: disable=too-many-arguments + self, + program_name: str, + target_name: str | None, + start_date: str | None = None, + end_date: str | None = None, + image_type: WinterImageTypes = DEFAULT_IMAGE_TYPE, + ) -> tuple[requests.Response, pd.DataFrame]: + """ + Function to get the observatory queue + + :param program_name: Name of the program under which to check ToOs + :param target_name: Name of the target + :param start_date: Start date for images + :param end_date: End date for images + :param image_type: Type of image to query + :return: API response and TOO schedule + """ + + start_date, end_date = self.check_query_dates( + start_date=start_date, end_date=end_date + ) + + print( + f"Querying images for {program_name} between " + f"{start_date} and {end_date} of type '{image_type}', " + f"with name {target_name}" + ) + + query = TargetImageQuery( + program_name=program_name, + target_name=target_name, + start_date=start_date, + end_date=end_date, + kind=image_type, + ) + + return self.query_images(query=query) + + def query_images_by_cone( # pylint: disable=too-many-arguments + self, + program_name: str, + ra_deg: float, + dec_deg: float, + radius_deg: float = 1.0, + start_date: str | None = None, + end_date: str | None = None, + image_type: WinterImageTypes = DEFAULT_IMAGE_TYPE, + ) -> tuple[requests.Response, pd.DataFrame]: + """ + Function to get the observatory queue + + :param program_name: Name of the program under which to check ToOs + :param ra_deg: Right Ascension in degrees + :param dec_deg: Declination in degrees + :param radius_deg: Radius in degrees + :param start_date: Start date for images + :param end_date: End date for images + :param image_type: Type of image to query + :return: API response and TOO schedule + """ + + start_date, end_date = self.check_query_dates( + start_date=start_date, end_date=end_date + ) + + print( + f"Querying images for {program_name} between " + f"{start_date} and {end_date} of type '{image_type}', " + f"with a radius of {radius_deg} degrees around {ra_deg}, {dec_deg}" + ) + + query = ConeImageQuery( + program_name=program_name, + ra=ra_deg, + dec=dec_deg, + radius_deg=radius_deg, + start_date=start_date, + end_date=end_date, + kind=image_type, + ) + + return self.query_images(query=query) + + def query_images_by_rectangle( # pylint: disable=too-many-arguments + self, + program_name: str, + ra_min_deg: float, + ra_max_deg: float, + dec_min_deg: float, + dec_max_deg: float, + start_date: str | None = None, + end_date: str | None = None, + image_type: WinterImageTypes = DEFAULT_IMAGE_TYPE, + ) -> tuple[requests.Response, pd.DataFrame]: + """ + Function to get the observatory queue + + :param program_name: Name of the program under which to check ToOs + :param ra_min_deg: Minimum Right Ascension in degrees + :param ra_max_deg: Maximum Right Ascension in degrees + :param dec_min_deg: Minimum Declination in degrees + :param dec_max_deg: Maximum Declination in degrees + :param start_date: Start date for images + :param end_date: End date for images + :param image_type: Type of image to query + :return: API response and TOO schedule + """ + + start_date, end_date = self.check_query_dates( + start_date=start_date, end_date=end_date + ) + + print( + f"Querying images for {program_name} between " + f"{start_date} and {end_date} of type '{image_type}', " + f"with RA between {ra_min_deg} and {ra_max_deg} and " + f"Dec between {dec_min_deg} and {dec_max_deg}" + ) + + query = RectangleImageQuery( + program_name=program_name, + ra_min=ra_min_deg, + ra_max=ra_max_deg, + dec_min=dec_min_deg, + dec_max=dec_max_deg, + start_date=start_date, + end_date=end_date, + kind=image_type, + ) + + return self.query_images(query=query) + + def download_image_list( + self, + program_name: str, + paths: list[str] | str, + image_type: WinterImageTypes, + output_dir: str | None | Path = None, + ) -> tuple[requests.Response, Path]: + """ + Download images as a zip file. + + :param program_name: Name of the program under which to check ToOs + :param image_type: Type of image to query + :param output_dir: Directory to save the zip to + :param paths: List of paths to download + :return: API response + """ + + if not isinstance(paths, list): + paths = [paths] + + program = self.get_program_details(program_name=program_name) + + res, output_path = self.get_stream( + DOWNLOAD_LIST_URL, + savepath=output_dir, + program_name=program_name, + program_api_key=program.prog_key, + data=[ImagePath(path=x) for x in paths], + kind=image_type, + ) + + return res, output_path