Skip to content

Commit

Permalink
Merge pull request spacetelescope#1624 from BradleySappington/page_co…
Browse files Browse the repository at this point in the history
…mments

Add Image and Exposure level Comments
  • Loading branch information
mfixstsci authored Aug 2, 2024
2 parents f19f30a + 01c4940 commit 2cb0a06
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 54 deletions.
1 change: 1 addition & 0 deletions jwql/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@

# Default Model Values
DEFAULT_MODEL_CHARFIELD = "empty"
DEFAULT_MODEL_COMMENT = ""

# Filename Component Lengths
FILE_AC_CAR_ID_LEN = 4
Expand Down
152 changes: 128 additions & 24 deletions jwql/website/apps/jwql/data_containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,44 +27,61 @@
"""

import copy
from collections import OrderedDict
import glob
import json
from operator import getitem
import logging
import os
import re
import tempfile
import logging
from collections import OrderedDict
from datetime import datetime
from operator import getitem, itemgetter

import numpy as np
import pandas as pd
import pyvo as vo
import requests
from astropy.io import fits
from astropy.time import Time
from astroquery.mast import Mast
from bs4 import BeautifulSoup
from django import setup
from django import forms, setup
from django.conf import settings
from django.contrib import messages
from django.core.exceptions import ObjectDoesNotExist
from django.db.models.query import QuerySet
import numpy as np
from operator import itemgetter
import pandas as pd
import pyvo as vo
import requests
from datetime import datetime

from jwql.database import database_interface as di
from jwql.database.database_interface import load_connection
from jwql.edb.engineering_database import get_mnemonic, get_mnemonic_info, mnemonic_inventory
from jwql.utils.utils import check_config_for_key, ensure_dir_exists, filesystem_path, filename_parser, get_config
from jwql.utils.constants import MAST_QUERY_LIMIT, MONITORS, THUMBNAIL_LISTFILE, THUMBNAIL_FILTER_LOOK
from jwql.utils.constants import EXPOSURE_PAGE_SUFFIX_ORDER, IGNORED_SUFFIXES, INSTRUMENT_SERVICE_MATCH
from jwql.utils.constants import JWST_INSTRUMENT_NAMES_MIXEDCASE, JWST_INSTRUMENT_NAMES
from jwql.utils.constants import REPORT_KEYS_PER_INSTRUMENT
from jwql.utils.constants import SUFFIXES_TO_ADD_ASSOCIATION, SUFFIXES_WITH_AVERAGED_INTS, QueryConfigKeys
from jwql.utils.constants import ON_GITHUB_ACTIONS, ON_READTHEDOCS
from jwql.utils.constants import (
DEFAULT_MODEL_COMMENT,
EXPOSURE_PAGE_SUFFIX_ORDER,
IGNORED_SUFFIXES,
INSTRUMENT_SERVICE_MATCH,
JWST_INSTRUMENT_NAMES,
JWST_INSTRUMENT_NAMES_MIXEDCASE,
MAST_QUERY_LIMIT,
MONITORS,
ON_GITHUB_ACTIONS,
ON_READTHEDOCS,
REPORT_KEYS_PER_INSTRUMENT,
SUFFIXES_TO_ADD_ASSOCIATION,
SUFFIXES_WITH_AVERAGED_INTS,
THUMBNAIL_FILTER_LOOK,
THUMBNAIL_LISTFILE,
QueryConfigKeys,
)
from jwql.utils.credentials import get_mast_token
from jwql.utils.permissions import set_permissions
from jwql.utils.utils import get_rootnames_for_instrument_proposal
from astroquery.mast import Mast
from jwql.utils.utils import (
check_config_for_key,
ensure_dir_exists,
filename_parser,
filesystem_path,
get_config,
get_rootnames_for_instrument_proposal,
)

# Increase the limit on the number of entries that can be returned by
# a MAST query.
Expand All @@ -79,8 +96,16 @@
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "jwql.website.jwql_proj.settings")
setup()

from .forms import MnemonicSearchForm, MnemonicQueryForm, MnemonicExplorationForm, InstrumentAnomalySubmitForm
from jwql.website.apps.jwql.models import Observation, Proposal, RootFileInfo, Anomalies
from jwql.website.apps.jwql.models import Anomalies, Observation, Proposal, RootFileInfo

from .forms import (
InstrumentAnomalySubmitForm,
MnemonicExplorationForm,
MnemonicQueryForm,
MnemonicSearchForm,
RootFileInfoCommentSubmitForm,
RootFileInfoExposureCommentSubmitForm,
)
check_config_for_key('auth_mast')
configs = get_config()
auth_mast = configs['auth_mast']
Expand Down Expand Up @@ -396,11 +421,16 @@ def get_additional_exposure_info(root_file_infos, image_info):
filter_value = '/'.join(set([e.filter for e in root_file_infos]))
pupil_value = '/'.join(set([e.pupil for e in root_file_infos]))
grating_value = '/'.join(set([e.grating for e in root_file_infos]))
exp_comment = root_file_infos.first().exp_comment
elif isinstance(root_file_infos, RootFileInfo):
root_file_info = root_file_infos
filter_value = root_file_info.filter
pupil_value = root_file_info.pupil
grating_value = root_file_info.grating
exp_comment = root_file_info.exp_comment

# Print N/A if no exposure comment is used
exp_comment = exp_comment if exp_comment != DEFAULT_MODEL_COMMENT else "N/A"

# Initialize dictionary of file info to show at the top of the page, along
# with another for info that will be in the collapsible text box.
Expand All @@ -427,7 +457,8 @@ def get_additional_exposure_info(root_file_infos, image_info):
'TARG_DEC': 'N/A',
'CRDS context': 'N/A',
'PA_V3': 'N/A',
'EXPSTART': root_file_info.expstart
'EXPSTART': root_file_info.expstart,
'EXP_COMMENT': exp_comment
}
elif isinstance(root_file_infos, RootFileInfo):
additional_info = {'READPATT': root_file_info.read_patt,
Expand All @@ -442,7 +473,8 @@ def get_additional_exposure_info(root_file_infos, image_info):
'DEC_REF': 'N/A',
'CRDS context': 'N/A',
'ROLL_REF': 'N/A',
'EXPSTART': root_file_info.expstart
'EXPSTART': root_file_info.expstart,
'EXP_COMMENT': exp_comment
}

# Deal with instrument-specific parameters
Expand Down Expand Up @@ -666,6 +698,74 @@ def get_anomaly_form(request, inst, file_root):
return form


def get_comment_form(request, file_root):
"""Generate form data for comment form
Parameters
----------
request : HttpRequest object
Incoming request
file_root : str
FITS filename of selected image in filesystem. May be a
file or group root name.
Returns
-------
RootFileInfoCommentSubmitForm object
form object to be sent with context to template
"""

root_file_info = RootFileInfo.objects.get(root_name=file_root)

if request.method == 'POST':
comment_form = RootFileInfoCommentSubmitForm(request.POST, instance=root_file_info)
if comment_form.is_valid():
comment_form.save()
else:
messages.error(request, "Failed to update comment form")
else:
comment_form = RootFileInfoCommentSubmitForm(instance=root_file_info)

return comment_form


def get_exp_comment_form(request, file_root):
"""Generate form data for exposure comment
This form updates all exposure level comments in each related rootfileimage model.
Each model related to this exposure will have the same exposure_comment associated with it.
When getting the default comment for this form, just use the first of the set. When updating
the comment, update for every rootfileinfo in the query set.
Parameters
----------
request : HttpRequest object
Incoming request
file_root : str
Partial FITS filename substring of exposure root name.
Returns
-------
RootFileInfoExposureCommentSubmitForm object
form object to be sent with context to template
"""

rootfileinfo_set = RootFileInfo.objects.filter(root_name__startswith=file_root)

if request.method == 'POST':
exp_comment_form = RootFileInfoExposureCommentSubmitForm(request.POST, instance=rootfileinfo_set.first())
if exp_comment_form.is_valid():
# Update the for all images in exposure
for rootfileinfo in rootfileinfo_set:
rootfileinfo.exp_comment = exp_comment_form.cleaned_data['exp_comment']
rootfileinfo.save()
else:
messages.error(request, "Failed to update exposure comment form")
else:
exp_comment_form = RootFileInfoExposureCommentSubmitForm(instance=rootfileinfo_set.first())

return exp_comment_form


def get_group_anomalies(file_root):
"""Generate form data for context
Expand All @@ -678,7 +778,7 @@ def get_group_anomalies(file_root):
Returns
-------
group_anomaly_dict dict
root file name key with string of anomalies
root file name key with string of anomalies followed by anomaly comment
"""
# Check for group root name
rootfileinfo_set = RootFileInfo.objects.filter(root_name__startswith=file_root).order_by("root_name")
Expand All @@ -687,6 +787,10 @@ def get_group_anomalies(file_root):
anomalies_list = get_current_flagged_anomalies([rootfileinfo])
anomalies_string = ', '.join(anomalies_list)
group_anomaly_dict[rootfileinfo.root_name] = anomalies_string
if rootfileinfo.comment != DEFAULT_MODEL_COMMENT:
anomalies_string += f" -- Comments: {rootfileinfo.comment}"
group_anomaly_dict[rootfileinfo.root_name] = anomalies_string

return group_anomaly_dict


Expand Down
59 changes: 46 additions & 13 deletions jwql/website/apps/jwql/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,29 +44,42 @@ def view_function(request):
placed in the ``jwql`` directory.
"""

from collections import defaultdict
import datetime
import glob
import os
import logging
import os
from collections import defaultdict

from astropy.time import Time, TimeDelta
from django import forms
from django.shortcuts import redirect
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from jwql.edb.engineering_database import is_valid_mnemonic
from jwql.website.apps.jwql.models import Anomalies
from wtforms import StringField, SubmitField


from jwql.utils.constants import (ANOMALY_CHOICES_PER_INSTRUMENT, ANOMALIES_PER_INSTRUMENT, APERTURES_PER_INSTRUMENT, DETECTOR_PER_INSTRUMENT,
EXP_TYPE_PER_INSTRUMENT, FILTERS_PER_INSTRUMENT, GENERIC_SUFFIX_TYPES, GRATING_PER_INSTRUMENT,
GUIDER_FILENAME_TYPE, JWST_INSTRUMENT_NAMES_MIXEDCASE, JWST_INSTRUMENT_NAMES_SHORTHAND,
READPATT_PER_INSTRUMENT, IGNORED_SUFFIXES, SUBARRAYS_PER_INSTRUMENT, PUPILS_PER_INSTRUMENT,
LOOK_OPTIONS, SORT_OPTIONS, PROPOSAL_CATEGORIES)
from jwql.utils.utils import (get_config, get_rootnames_for_instrument_proposal, filename_parser, query_format)

from wtforms import SubmitField, StringField
from jwql.edb.engineering_database import is_valid_mnemonic
from jwql.utils.constants import (
ANOMALIES_PER_INSTRUMENT,
ANOMALY_CHOICES_PER_INSTRUMENT,
APERTURES_PER_INSTRUMENT,
DETECTOR_PER_INSTRUMENT,
EXP_TYPE_PER_INSTRUMENT,
FILTERS_PER_INSTRUMENT,
GENERIC_SUFFIX_TYPES,
GRATING_PER_INSTRUMENT,
GUIDER_FILENAME_TYPE,
IGNORED_SUFFIXES,
JWST_INSTRUMENT_NAMES_MIXEDCASE,
JWST_INSTRUMENT_NAMES_SHORTHAND,
LOOK_OPTIONS,
PROPOSAL_CATEGORIES,
PUPILS_PER_INSTRUMENT,
READPATT_PER_INSTRUMENT,
SORT_OPTIONS,
SUBARRAYS_PER_INSTRUMENT,
)
from jwql.utils.utils import filename_parser, get_config, get_rootnames_for_instrument_proposal, query_format
from jwql.website.apps.jwql.models import Anomalies, RootFileInfo


class BaseForm(forms.Form):
Expand All @@ -79,6 +92,26 @@ class BaseForm(forms.Form):
resolve_submit = SubmitField('Resolve Target')


class RootFileInfoCommentSubmitForm(forms.ModelForm):
"""Creates a ``Comment Form`` object that allows for text input in a form field.
This uses forms.ModelForm which is good for simplifying direct access to
Django Model database information
"""
class Meta:
model = RootFileInfo
fields = ['comment']


class RootFileInfoExposureCommentSubmitForm(forms.ModelForm):
"""Creates a ``Comment Form`` object that allows for text input in a form field.
This uses forms.ModelForm which is good for simplifying direct access to
Django Model database information
"""
class Meta:
model = RootFileInfo
fields = ['exp_comment']


class JwqlQueryForm(BaseForm):
"""Form validation for the JWQL Query viewing tool"""

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 5.0.7 on 2024-07-24 21:12

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('jwql', '0024_nirspecmsatastats_nirspecwatastats_and_more'),
]

operations = [
migrations.AddField(
model_name='rootfileinfo',
name='comment',
field=models.TextField(blank=True, default='', help_text='Anomaly Comment Field'),
),
migrations.AddField(
model_name='rootfileinfo',
name='exp_comment',
field=models.TextField(blank=True, default='', help_text='Anomaly Exposure Comment Field'),
),
]
3 changes: 3 additions & 0 deletions jwql/website/apps/jwql/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

from jwql.utils.constants import (
DEFAULT_MODEL_CHARFIELD,
DEFAULT_MODEL_COMMENT,
MAX_LEN_APERTURE,
MAX_LEN_DETECTOR,
MAX_LEN_FILTER,
Expand Down Expand Up @@ -130,6 +131,8 @@ class RootFileInfo(models.Model):
pupil = models.CharField(max_length=MAX_LEN_PUPIL, help_text="Pupil", default=DEFAULT_MODEL_CHARFIELD, null=True, blank=True)
exp_type = models.CharField(max_length=MAX_LEN_TYPE, help_text="Exposure Type", default=DEFAULT_MODEL_CHARFIELD, null=True, blank=True)
expstart = models.FloatField(help_text='Exposure Start Time', default=0.0)
comment = models.TextField(help_text="Anomaly Comment Field", default=DEFAULT_MODEL_COMMENT, null=False, blank=True)
exp_comment = models.TextField(help_text="Anomaly Comment Field", default=DEFAULT_MODEL_COMMENT, null=False, blank=True)

# Metadata
class Meta:
Expand Down
6 changes: 6 additions & 0 deletions jwql/website/apps/jwql/static/css/jwql.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
background-color: #f2f2f2;
}

.anomaly_form button {
display: block; /* Makes the button a block element so it takes a new line */
margin-top: 10px; /* Space between the comment field and the button */
margin-left: 0; /* Align button to left, full width of parent div */
}

.anomaly_choice {
list-style: none;
}
Expand Down
Loading

0 comments on commit 2cb0a06

Please sign in to comment.