13 """Convert a string into a date object"""
14 return datetime.datetime.strptime(dateString,
"%Y-%m-%d").date()
18 """Task that will parse the filename and/or its contents to get the
19 required information to populate the calibration registry."""
21 """Return a a known calibration dataset type using
22 the observation type in the header keyword OBSTYPE
24 @param filename: Input filename
27 if not md.exists(
"OBSTYPE"):
28 raise RuntimeError(
"Unable to find the required header keyword OBSTYPE")
29 obstype = md.get(
"OBSTYPE").strip().lower()
32 elif "zero" in obstype
or "bias" in obstype:
34 elif "dark" in obstype:
36 elif "fringe" in obstype:
42 """Configuration for the CalibsRegisterTask"""
43 tables = ListField(dtype=str, default=[
"bias",
"dark",
"flat",
"fringe"], doc=
"Names of tables")
44 calibDate = Field(dtype=str, default=
"calibDate", doc=
"Name of column for calibration date")
45 validStart = Field(dtype=str, default=
"validStart", doc=
"Name of column for validity start")
46 validEnd = Field(dtype=str, default=
"validEnd", doc=
"Name of column for validity stop")
47 detector = ListField(dtype=str, default=[
"filter",
"ccd"],
48 doc=
"Columns that identify individual detectors")
49 validityUntilSuperseded = ListField(dtype=str, default=[
"defect"],
50 doc=
"Tables for which to set validity for a calib from when it is "
51 "taken until it is superseded by the next; validity in other tables "
52 "is calculated by applying the validity range.")
55 """Task that will generate the calibration registry for the Mapper"""
56 ConfigClass = CalibsRegisterConfig
58 def openRegistry(self, directory, create=False, dryrun=False, name="calibRegistry.sqlite3"):
59 """Open the registry and return the connection handle"""
60 return RegisterTask.openRegistry(self, directory, create, dryrun, name)
63 """Create the registry tables"""
64 for table
in self.config.tables:
65 RegisterTask.createTable(self, conn, table=table)
67 def addRow(self, conn, info, *args, **kwargs):
68 """Add a row to the file table"""
69 info[self.config.validStart] =
None
70 info[self.config.validEnd] =
None
71 RegisterTask.addRow(self, conn, info, *args, **kwargs)
74 """Loop over all tables, filters, and ccdnums,
75 and update the validity ranges in the registry.
77 @param conn: Database connection
78 @param validity: Validity range (days)
80 conn.row_factory = sqlite3.Row
81 cursor = conn.cursor()
82 for table
in self.config.tables:
83 sql =
"SELECT DISTINCT %s FROM %s" % (
", ".join(self.config.detector), table)
85 rows = cursor.fetchall()
90 """Update the validity ranges among selected rows in the registry.
92 For defects, the products are valid from their start date until
93 they are superseded by subsequent defect data.
94 For other calibration products, the validity ranges are checked and
95 if there are overlaps, a midpoint is used to fix the overlaps,
96 so that the calibration data with whose date is nearest the date
97 of the observation is used.
99 @param conn: Database connection
100 @param table: Name of table to be selected
101 @param detectorData: Values identifying a detector (from columns in self.config.detector)
102 @param validity: Validity range (days)
104 columns =
", ".join([self.config.calibDate, self.config.validStart, self.config.validEnd])
105 sql =
"SELECT id, %s FROM %s" % (columns, table)
106 sql +=
" WHERE " +
" AND ".join(col +
"=?" for col
in self.config.detector)
107 sql +=
" ORDER BY " + self.config.calibDate
108 cursor = conn.cursor()
109 cursor.execute(sql, detectorData)
110 rows = cursor.fetchall()
113 valids = collections.OrderedDict([(
_convertToDate(row[self.config.calibDate]), [
None,
None])
for
115 except Exception
as e:
116 det =
" ".join(
"%s=%s" % (k, v)
for k, v
in zip(self.config.detector, detectorData))
118 self.log.warn(str(
"Skipped setting the validity overlaps for %s %s: missing calibration dates" %
121 dates = valids.keys()
122 if table
in self.config.validityUntilSuperseded:
124 for thisDate, nextDate
in itertools.izip(dates[:-1], dates[1:]):
125 valids[thisDate][0] = thisDate
126 valids[thisDate][1] = nextDate - datetime.timedelta(1)
127 valids[dates[-1]][0] = dates[-1]
132 valids[dd] = [dd - datetime.timedelta(validity), dd + datetime.timedelta(validity)]
135 midpoints = [t1 + (t2 - t1)//2
for t1, t2
in itertools.izip(dates[:-1], dates[1:])]
136 for i, (date, midpoint)
in enumerate(itertools.izip(dates[:-1], midpoints)):
137 if valids[date][1] > midpoint:
138 nextDate = dates[i + 1]
139 valids[nextDate][0] = midpoint + datetime.timedelta(1)
140 valids[date][1] = midpoint
146 validStart = valids[calibDate][0].isoformat()
147 validEnd = valids[calibDate][1].isoformat()
148 sql =
"UPDATE %s" % table
149 sql +=
" SET %s=?, %s=?" % (self.config.validStart, self.config.validEnd)
151 conn.execute(sql, (validStart, validEnd, row[
"id"]))
155 """Argument parser to support ingesting calibration images into the repository"""
157 InputOnlyArgumentParser.__init__(self, *args, **kwargs)
158 self.add_argument(
"-n",
"--dry-run", dest=
"dryrun", action=
"store_true",
159 default=
False, help=
"Don't perform any action?")
160 self.add_argument(
"--create", action=
"store_true", help=
"Create new registry?")
161 self.add_argument(
"--validity", type=int, required=
True, help=
"Calibration validity period (days)")
162 self.add_argument(
"--calibType", type=str, default=
None,
163 choices=[
None,
"bias",
"dark",
"flat",
"fringe",
"defect"],
164 help=
"Type of the calibration data to be ingested;" +
165 " if omitted, the type is determined from" +
166 " the file header information")
167 self.add_argument(
"files", nargs=
"+", help=
"Names of file")
171 """Configuration for IngestCalibsTask"""
172 parse = ConfigurableField(target=CalibsParseTask, doc=
"File parsing")
173 register = ConfigurableField(target=CalibsRegisterTask, doc=
"Registry entry")
177 """Task that generates registry for calibration images"""
178 ConfigClass = IngestCalibsConfig
179 ArgumentParser = IngestCalibsArgumentParser
180 _DefaultName =
"ingestCalibs"
183 """Ingest all specified files and add them to the registry"""
184 calibRoot = args.calib
if args.calib
is not None else "."
185 filenameList = sum([glob(filename)
for filename
in args.files], [])
186 with self.register.openRegistry(calibRoot, create=args.create, dryrun=args.dryrun)
as registry:
187 for infile
in filenameList:
188 fileInfo, hduInfoList = self.parse.getInfo(infile)
189 if args.calibType
is None:
190 calibType = self.parse.getCalibType(infile)
192 calibType = args.calibType
193 if calibType
not in self.register.config.tables:
194 self.log.warn(str(
"Skipped adding %s of observation type '%s' to registry" %
195 (infile, calibType)))
197 for info
in hduInfoList:
198 self.register.addRow(registry, info, dryrun=args.dryrun,
199 create=args.create, table=calibType)
201 self.register.updateValidityRanges(registry, args.validity)
203 self.log.info(
"Would update validity ranges here, but dryrun")
boost::shared_ptr< daf::base::PropertySet > readMetadata(std::string const &fileName, int hdu=0, bool strip=false)
Return the metadata (header entries) from a FITS file.