a2p2 0.2.15__py3-none-any.whl → 0.7.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- a2p2/__main__.py +39 -0
- a2p2/chara/facility.py +51 -3
- a2p2/chara/gui.py +29 -5
- a2p2/client.py +144 -34
- a2p2/facility.py +11 -1
- a2p2/gui.py +32 -1
- a2p2/jmmc/__init__.py +7 -0
- a2p2/jmmc/catalogs.py +129 -0
- a2p2/jmmc/generated_models.py +191 -0
- a2p2/jmmc/models.py +104 -0
- a2p2/jmmc/services.py +16 -0
- a2p2/jmmc/utils.py +130 -0
- a2p2/jmmc/webservices.py +48 -0
- a2p2/ob.py +95 -9
- a2p2/samp.py +17 -0
- a2p2/version.py +205 -137
- a2p2/vlti/conf/GRAVITY_ditTable.json +21 -19
- a2p2/vlti/conf/GRAVITY_rangeTable.json +200 -28
- a2p2/vlti/conf/MATISSE_rangeTable.json +58 -22
- a2p2/vlti/conf/PIONIER_ditTable.json +1 -1
- a2p2/vlti/conf/PIONIER_rangeTable.json +16 -18
- a2p2/vlti/facility.py +156 -118
- a2p2/vlti/gravity.py +243 -311
- a2p2/vlti/gui.py +162 -40
- a2p2/vlti/instrument.py +264 -49
- a2p2/vlti/matisse.py +61 -147
- a2p2/vlti/pionier.py +34 -157
- {a2p2-0.2.15.dist-info → a2p2-0.7.4.dist-info}/METADATA +34 -20
- a2p2-0.7.4.dist-info/RECORD +39 -0
- {a2p2-0.2.15.dist-info → a2p2-0.7.4.dist-info}/WHEEL +1 -1
- {a2p2-0.2.15.dist-info → a2p2-0.7.4.dist-info}/entry_points.txt +0 -1
- a2p2/vlti/confP104/GRAVITY_ditTable.json +0 -122
- a2p2/vlti/confP104/GRAVITY_rangeTable.json +0 -202
- a2p2/vlti/confP104/MATISSE_ditTable.json +0 -2
- a2p2/vlti/confP104/MATISSE_rangeTable.json +0 -202
- a2p2/vlti/confP104/PIONIER_ditTable.json +0 -77
- a2p2/vlti/confP104/PIONIER_rangeTable.json +0 -118
- a2p2/vlti/confP105/GRAVITY_ditTable.json +0 -37
- a2p2/vlti/confP105/GRAVITY_rangeTable.json +0 -42
- a2p2/vlti/confP105/MATISSE_ditTable.json +0 -2
- a2p2/vlti/confP105/MATISSE_rangeTable.json +0 -44
- a2p2/vlti/confP105/PIONIER_ditTable.json +0 -25
- a2p2/vlti/confP105/PIONIER_rangeTable.json +0 -38
- a2p2-0.2.15.dist-info/RECORD +0 -44
- {a2p2-0.2.15.dist-info → a2p2-0.7.4.dist-info}/LICENSE +0 -0
- {a2p2-0.2.15.dist-info → a2p2-0.7.4.dist-info}/top_level.txt +0 -0
a2p2/vlti/instrument.py
CHANGED
@@ -12,11 +12,13 @@ import logging
|
|
12
12
|
|
13
13
|
import numpy as np
|
14
14
|
from astropy.coordinates import SkyCoord
|
15
|
+
import astropy.units as u
|
15
16
|
|
16
17
|
from a2p2.instrument import Instrument
|
17
18
|
|
18
19
|
logger = logging.getLogger(__name__)
|
19
20
|
|
21
|
+
|
20
22
|
class VltiInstrument(Instrument):
|
21
23
|
|
22
24
|
def __init__(self, facility, insname):
|
@@ -28,13 +30,41 @@ class VltiInstrument(Instrument):
|
|
28
30
|
self.rangeTable = None
|
29
31
|
self.ditTable = None
|
30
32
|
|
33
|
+
# warnings
|
34
|
+
self.warnings = []
|
35
|
+
self.errors = []
|
36
|
+
|
37
|
+
def isScience(self, objtype):
|
38
|
+
return "SCI" in objtype
|
39
|
+
|
40
|
+
def isCalibrator(self, objtype):
|
41
|
+
return "SCI" not in objtype
|
42
|
+
|
31
43
|
def get(self, obj, fieldname, defaultvalue):
|
32
44
|
if fieldname in obj._fields:
|
33
45
|
return getattr(obj, fieldname)
|
34
46
|
else:
|
35
47
|
return defaultvalue
|
36
48
|
|
37
|
-
|
49
|
+
def getSequence(self, ob):
|
50
|
+
# We may look at ob.observationSchedule but at present tuime schedule still is computed on a2p2 side
|
51
|
+
sci = []
|
52
|
+
cal = []
|
53
|
+
for observationConfiguration in ob.observationConfiguration:
|
54
|
+
if 'SCIENCE' in observationConfiguration.type:
|
55
|
+
sci.append(observationConfiguration)
|
56
|
+
else:
|
57
|
+
cal.append(observationConfiguration)
|
58
|
+
if len(sci)+len(cal) <= 2: # return [CAL] [SCI]
|
59
|
+
return cal+sci
|
60
|
+
# else return CAL1 SCI CAL2 [SCI CAL3] [SCI CAL4]
|
61
|
+
seq = []
|
62
|
+
for c in cal:
|
63
|
+
seq.append(c)
|
64
|
+
seq += sci
|
65
|
+
return seq[0:-1]
|
66
|
+
|
67
|
+
# checkOB() must be defined by each instruments
|
38
68
|
# def checkOB(self, ob, p2container=None):
|
39
69
|
# consider dryMode if p2container is None else check again and submit on p2 side
|
40
70
|
|
@@ -44,30 +74,49 @@ class VltiInstrument(Instrument):
|
|
44
74
|
|
45
75
|
# create new container
|
46
76
|
obsconflist = ob.observationConfiguration
|
77
|
+
|
78
|
+
# Aspro2 always send SCI at first position (or CAL if no SCI)
|
47
79
|
folderName = obsconflist[0].SCTarget.name
|
48
80
|
folderName = re.sub('[^A-Za-z0-9]+', '_', folderName.strip())
|
49
|
-
|
50
|
-
# force a top folder in demo.
|
81
|
+
# prefix with username to make test folder clearer
|
51
82
|
if p2container.isRoot() and self.facility.isTutorialAccount():
|
52
|
-
|
53
|
-
|
54
|
-
|
83
|
+
folderName += "_" + self.facility.a2p2client.preferences.getP2UserCommentName()
|
84
|
+
|
85
|
+
# do create a folder or concatenation only for SM or tutorial account
|
86
|
+
# should we create a concatenation only if we have multiple objects
|
87
|
+
if p2container.isRoot():
|
88
|
+
# create a concatenation if service mode (even if we do not have any cal at this stage)
|
89
|
+
if p2container.isServiceModeRun():
|
90
|
+
# TODO fix bug when we keep the last selection that is a Concatenation. Concatenation can't be nested.
|
91
|
+
# use parent container or ask user to choose another container location ?
|
92
|
+
folder, _ = api.createConcatenation(
|
93
|
+
p2container.containerId, folderName)
|
94
|
+
ui.addToLog(f"concatenation '{folderName}' created")
|
95
|
+
else:
|
96
|
+
folder, _ = api.createFolder(
|
97
|
+
p2container.containerId, folderName)
|
98
|
+
ui.addToLog(f"folder '{folderName}' created")
|
99
|
+
|
100
|
+
# update parent tree since we created a new subfolder
|
101
|
+
ui.updateTree(p2container.run, p2container.containerId)
|
102
|
+
|
55
103
|
p2container.containerId = folder['containerId']
|
56
104
|
|
57
|
-
# create a concatenation if service mode
|
58
|
-
if p2container.isServiceModeRun():
|
59
|
-
folder, _ = api.createConcatenation(
|
60
|
-
p2container.containerId, folderName)
|
61
105
|
else:
|
62
|
-
|
63
|
-
p2container.containerId = folder['containerId']
|
106
|
+
ui.addToLog("concatenation or folder not created")
|
64
107
|
|
65
108
|
ui.addToLog("OB checked / preparing submission...")
|
66
109
|
self.checkOB(ob, p2container)
|
67
110
|
|
111
|
+
# refresh and select last created element tree
|
112
|
+
ui.updateTree(p2container.run, p2container.containerId)
|
113
|
+
ui.selectTreeItem(p2container.containerId)
|
114
|
+
ui.addToLog(
|
115
|
+
"OB submitted! Please check logs and fix last details on P2 web.")
|
116
|
+
|
68
117
|
def getCoords(self, target, requirePrecision=True):
|
69
118
|
"""
|
70
|
-
Format coordinates from given target to be VLTI compliant.
|
119
|
+
Format HMS DMS coordinates from given target to be VLTI compliant.
|
71
120
|
Throws an exception if requirePrecision is true and given inputs have less than 3 (RA) or 2 (DEC) digits.
|
72
121
|
"""
|
73
122
|
|
@@ -95,36 +144,63 @@ class VltiInstrument(Instrument):
|
|
95
144
|
|
96
145
|
def getPMCoords(self, target, defaultPMRA=0.0, defaultPMDEC=0.0):
|
97
146
|
"""
|
98
|
-
Returns PMRA, PMDEC as float values rounded to 4 decimal digits. 0.0 is used as default if not present.
|
147
|
+
Returns PMRA, PMDEC in arcsec/year as float values rounded to 4 decimal digits. 0.0 is used as default if not present.
|
99
148
|
"""
|
100
149
|
PMRA = self.get(target, "PMRA", defaultPMRA)
|
101
150
|
PMDEC = self.get(target, "PMDEC", defaultPMDEC)
|
102
151
|
return round(float(PMRA) / 1000.0, 4), round(float(PMDEC) / 1000.0, 4)
|
103
152
|
|
153
|
+
def getPARALLAX(self, target, defaultPARALLAX=0.0):
|
154
|
+
"""
|
155
|
+
Returns PARALLAX in arcsec as float value rounded to 4 decimal digits. 0.0 is used as default if not present.
|
156
|
+
"""
|
157
|
+
PARALLAX = self.get(target, "PARALLAX", defaultPARALLAX)
|
158
|
+
return round(float(PARALLAX) / 1000.0, 4)
|
159
|
+
|
160
|
+
|
104
161
|
def getFlux(self, target, flux):
|
105
162
|
"""
|
106
163
|
Returns Flux as float values rounded to 3 decimal digits.
|
107
164
|
|
108
165
|
flux in 'V', 'J', 'H'...
|
109
166
|
"""
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
return
|
121
|
-
elif "K0" in baseline:
|
122
|
-
return "astrometric"
|
123
|
-
elif "U" in baseline:
|
124
|
-
return "UTs"
|
167
|
+
try:
|
168
|
+
return round(float(getattr(target, "FLUX_" + flux)), 3)
|
169
|
+
except:
|
170
|
+
raise ValueError(
|
171
|
+
f"Missing {flux} flux for target { getattr(target,'name') }") from None
|
172
|
+
|
173
|
+
def getBaselineCode(self, ob):
|
174
|
+
confAltName = ob.get(ob.interferometerConfiguration, "confAltName")
|
175
|
+
stations = ob.get(ob.interferometerConfiguration, "stations")
|
176
|
+
if confAltName:
|
177
|
+
return confAltName
|
125
178
|
else:
|
126
179
|
raise ValueError(
|
127
|
-
"Can't detect Interferometric Array type from given baseline : %s)" % (
|
180
|
+
"Can't detect alt name of Interferometric Array type from given baseline : %s)" % (stations))
|
181
|
+
|
182
|
+
def getAcquisitionType(self):
|
183
|
+
return self.facility.ui.getAcquisitionType()
|
184
|
+
return "onaxis"
|
185
|
+
return "offaxis"
|
186
|
+
return "wide"
|
187
|
+
|
188
|
+
def checkIssVltiType(self, acqTSF):
|
189
|
+
# TODO improve handling of this keyword using input from Aspro2's OB
|
190
|
+
|
191
|
+
# use GUI live checkboxes instead of preferences
|
192
|
+
vltitypesVars = self.facility.ui.getIssVltitypeVars()
|
193
|
+
vltitypes = [v for v in vltitypesVars
|
194
|
+
if vltitypesVars[v].get() and self.isInRange(acqTSF.tpl, "ISS.VLTITYPE", v)]
|
195
|
+
if vltitypes:
|
196
|
+
self.ui.addToLog(
|
197
|
+
f"Set template's ISS_VLTITYPE to {vltitypes}")
|
198
|
+
acqTSF.ISS_VLTITYPE = vltitypes
|
199
|
+
else:
|
200
|
+
# TODO throw an warning ?
|
201
|
+
self.ui.addToLog(
|
202
|
+
f"Warning: no compatible ISS_VLTITYPE selected in the GUI")
|
203
|
+
pass
|
128
204
|
|
129
205
|
def getA2p2Comments(self):
|
130
206
|
return 'Generated by ' + self.facility.a2p2client.preferences.getP2UserCommentName() + \
|
@@ -149,7 +225,7 @@ class VltiInstrument(Instrument):
|
|
149
225
|
self.ditTable = json.load(open(f))
|
150
226
|
return self.ditTable
|
151
227
|
|
152
|
-
def getDit(self, tel, spec, pol, K, dualFeed=False, showWarning=False):
|
228
|
+
def getDit(self, tel, spec, pol, K, dualFeed=False, targetName="", showWarning=False):
|
153
229
|
"""
|
154
230
|
finds DIT according to ditTable and K magnitude K
|
155
231
|
|
@@ -174,7 +250,7 @@ class VltiInstrument(Instrument):
|
|
174
250
|
if tel == "UT":
|
175
251
|
dK += ditTable["AT"]['Kut']
|
176
252
|
for i, d in enumerate(dits):
|
177
|
-
if mags[i]
|
253
|
+
if mags[i] <= (K - dK) and (K - dK) <= mags[i + 1]:
|
178
254
|
return d
|
179
255
|
|
180
256
|
# handle out of bounds
|
@@ -183,11 +259,19 @@ class VltiInstrument(Instrument):
|
|
183
259
|
for i, d in enumerate(dits):
|
184
260
|
kmin = min(kmin, mags[i] + dK)
|
185
261
|
kmax = max(kmax, mags[i + 1] + dK)
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
262
|
+
|
263
|
+
if showWarning:
|
264
|
+
self.warnings.append(
|
265
|
+
f"K mag ({K}) is out of range [{kmin},{kmax}]\n mode ( ∆K={dK} tel={tel}, spec={spec}, pol={pol}, dualFeed={dualFeed}) \n for target '{targetName}'")
|
266
|
+
|
267
|
+
# always return a value to avoid unsupported operations
|
268
|
+
if K < kmin:
|
269
|
+
return min(dits)
|
270
|
+
return max(dits)
|
271
|
+
|
272
|
+
# raise ValueError(
|
273
|
+
# "K mag (%f) of '%s' is out of ranges [%f,%f]\n for this mode (tel=%s, spec=%s, pol=%s, dualFeed=%s)" % (
|
274
|
+
# K, targetName, kmin, kmax, tel, spec, pol, dualFeed))
|
191
275
|
|
192
276
|
def getRangeTable(self):
|
193
277
|
if self.rangeTable:
|
@@ -222,6 +306,14 @@ class VltiInstrument(Instrument):
|
|
222
306
|
return value >= rangeTable[_tpl][key]['min'] and \
|
223
307
|
value <= rangeTable[_tpl][key]['max']
|
224
308
|
if 'list' in rangeTable[_tpl][key].keys():
|
309
|
+
#
|
310
|
+
# hack set to check for coordinates convention define by p2
|
311
|
+
# vlue will be checked by p2 later...
|
312
|
+
if "ra" in rangeTable[_tpl][key]['list']:
|
313
|
+
return True
|
314
|
+
if "dec" in rangeTable[_tpl][key]['list']:
|
315
|
+
return True
|
316
|
+
|
225
317
|
# return value in rangeTable[_tpl][key]['list']
|
226
318
|
if type(value) is list:
|
227
319
|
for v in value:
|
@@ -282,7 +374,7 @@ class VltiInstrument(Instrument):
|
|
282
374
|
if not key in rangeTable[_tpl].keys():
|
283
375
|
raise ValueError(
|
284
376
|
"unknown keyword '%s' in template '%s'" % (key, tpl))
|
285
|
-
if 'default' in rangeTable[_tpl][key].keys()
|
377
|
+
if 'default' in rangeTable[_tpl][key].keys():
|
286
378
|
return rangeTable[_tpl][key]['default']
|
287
379
|
return None
|
288
380
|
|
@@ -306,13 +398,15 @@ class VltiInstrument(Instrument):
|
|
306
398
|
res[key] = rangeTable[_tpl][key]["default"]
|
307
399
|
return res
|
308
400
|
|
401
|
+
def getSkySeparation(self, ra1, dec1, ra2, dec2):
|
402
|
+
target1 = SkyCoord(ra1, dec1, frame='icrs', unit=(u.hourangle, u.deg))
|
403
|
+
target2 = SkyCoord(ra2, dec2, frame='icrs', unit=(u.hourangle, u.deg))
|
404
|
+
return target1.separation(target2)
|
309
405
|
|
310
|
-
def
|
311
|
-
science = SkyCoord(ra, dec, frame='icrs', unit=
|
312
|
-
|
313
|
-
|
314
|
-
dec_offset = (science.dec - ft.dec)
|
315
|
-
return [ra_offset.deg * 3600 * 1000, dec_offset.deg * 3600 * 1000] # in mas
|
406
|
+
def getSkyOffset(self, ra, dec, originRa, originDec):
|
407
|
+
science = SkyCoord(ra, dec, frame='icrs', unit=(u.hourangle, u.deg))
|
408
|
+
origin = SkyCoord(originRa, originDec, frame='icrs', unit=(u.hourangle, u.deg))
|
409
|
+
return origin.spherical_offsets_to(science)
|
316
410
|
|
317
411
|
def getSiderealTimeConstraints(self, LSTINTERVAL):
|
318
412
|
# by default, above 40 degree. Will generate a WAIVERABLE ERROR if not.
|
@@ -332,9 +426,10 @@ class VltiInstrument(Instrument):
|
|
332
426
|
intervals.append({'from': lstStartSex, 'to': lstEndSex})
|
333
427
|
return intervals
|
334
428
|
|
335
|
-
def saveSiderealTimeConstraints(self, api,
|
429
|
+
def saveSiderealTimeConstraints(self, api, ob, LSTINTERVAL):
|
336
430
|
# wait for next Aspro release to only send constraints set by user
|
337
431
|
return
|
432
|
+
obId = ob['obId']
|
338
433
|
intervals = self.getSiderealTimeConstraints(LSTINTERVAL)
|
339
434
|
if intervals:
|
340
435
|
sidTCs, stcVersion = api.getSiderealTimeConstraints(obId)
|
@@ -364,7 +459,35 @@ class VltiInstrument(Instrument):
|
|
364
459
|
|
365
460
|
return s
|
366
461
|
|
367
|
-
def
|
462
|
+
def formatRangeTable(self):
|
463
|
+
rangeTable = self.getRangeTable()
|
464
|
+
buffer = ""
|
465
|
+
for l in rangeTable.keys():
|
466
|
+
buffer += l + "\n"
|
467
|
+
for k in rangeTable[l].keys():
|
468
|
+
constraint = rangeTable[l][k]
|
469
|
+
keys = constraint.keys()
|
470
|
+
buffer += ' %30s :' % (k)
|
471
|
+
if 'min' in keys and 'max' in keys:
|
472
|
+
buffer += ' %f ... %f ' % (
|
473
|
+
constraint['min'], constraint['max'])
|
474
|
+
elif 'list' in keys:
|
475
|
+
buffer += str(constraint['list'])
|
476
|
+
elif "spaceseparatedlist" in keys:
|
477
|
+
buffer += ' ' + " ".join(constraint['spaceseparatedlist'])
|
478
|
+
if 'default' in keys:
|
479
|
+
buffer += ' (' + str(constraint['default']) + ')'
|
480
|
+
else:
|
481
|
+
buffer += ' -no default-'
|
482
|
+
buffer += "\n"
|
483
|
+
return buffer
|
484
|
+
|
485
|
+
def showP2Response(self, response, ob,):
|
486
|
+
if not ob:
|
487
|
+
return
|
488
|
+
|
489
|
+
obId = ob['obId']
|
490
|
+
|
368
491
|
if response['observable']:
|
369
492
|
msg = 'OB ' + \
|
370
493
|
str(obId) + ' submitted successfully on P2\n' + \
|
@@ -373,9 +496,93 @@ class VltiInstrument(Instrument):
|
|
373
496
|
msg = 'OB ' + str(obId) + ' submitted successfully on P2\n' + ob[
|
374
497
|
'name'] + ' has WARNING.\n see LOG for details.'
|
375
498
|
self.ui.addToLog('\n')
|
376
|
-
self.ui.ShowInfoMessage(msg)
|
499
|
+
# self.ui.ShowInfoMessage(msg)
|
500
|
+
self.ui.addToLog(msg)
|
377
501
|
self.ui.addToLog('\n'.join(response['messages']) + '\n\n')
|
378
502
|
|
503
|
+
def createOB(self, p2container, obTarget, obConstraints, OBJTYPE, instrumentMode, LSTINTERVAL, tsfs):
|
504
|
+
""" Creates an OB on P2 and attach a template for every given tsf."""
|
505
|
+
|
506
|
+
ui = self.ui
|
507
|
+
ui.setProgress(0.1)
|
508
|
+
|
509
|
+
goodName = re.sub('[^A-Za-z0-9]+', '_', obTarget.name)
|
510
|
+
OBS_DESCR = '_'.join(
|
511
|
+
(OBJTYPE[0:3], goodName, self.getName(), instrumentMode))
|
512
|
+
# removed from template name acqTSF.ISS_BASELINE[0]
|
513
|
+
|
514
|
+
# dev code to debug without interracting with P2
|
515
|
+
if False:
|
516
|
+
self.ui.addToLog(f"Skip ob creation : {OBS_DESCR}")
|
517
|
+
for tsf in tsfs:
|
518
|
+
self.ui.addToLog(f"Skip {tsf.getP2Name()} template creation")
|
519
|
+
ui.setProgress(1.0)
|
520
|
+
return None
|
521
|
+
else:
|
522
|
+
self.ui.addToLog(f"Creating new ob from p2 : {OBS_DESCR}")
|
523
|
+
|
524
|
+
api = self.facility.getAPI()
|
525
|
+
|
526
|
+
ob, obVersion = api.createOB(p2container.containerId, OBS_DESCR)
|
527
|
+
|
528
|
+
# we use obId to populate OB
|
529
|
+
ob['obsDescription']['name'] = OBS_DESCR[0:min(len(OBS_DESCR), 31)]
|
530
|
+
ob['obsDescription']['userComments'] = self.getA2p2Comments()
|
531
|
+
|
532
|
+
# copy target info
|
533
|
+
targetInfo = obTarget.getDict()
|
534
|
+
for key in targetInfo:
|
535
|
+
ob['target'][key] = targetInfo[key]
|
536
|
+
|
537
|
+
# copy constraints info
|
538
|
+
constraints = obConstraints.getDict()
|
539
|
+
for k in constraints:
|
540
|
+
ob['constraints'][k] = constraints[k]
|
541
|
+
|
542
|
+
self.ui.addToLog("New OB saved to p2\n%s" % ob, False)
|
543
|
+
ob, obVersion = api.saveOB(ob, obVersion)
|
544
|
+
|
545
|
+
# set time constraints if present
|
546
|
+
self.saveSiderealTimeConstraints(api, ob, LSTINTERVAL)
|
547
|
+
ui.addProgress()
|
548
|
+
|
549
|
+
for tsf in tsfs:
|
550
|
+
ui.addProgress()
|
551
|
+
self.createTemplate(ob,tsf)
|
552
|
+
|
553
|
+
# verify OB online
|
554
|
+
response = self.verifyOB(ob)
|
555
|
+
ui.setProgress(1.0)
|
556
|
+
|
557
|
+
self.showP2Response(response, ob)
|
558
|
+
|
559
|
+
return ob
|
560
|
+
|
561
|
+
def createTemplate(self, ob, tsf, templateName=None):
|
562
|
+
if not templateName:
|
563
|
+
templateName = tsf.getP2Name()
|
564
|
+
|
565
|
+
if not ob:
|
566
|
+
self.ui.addToLog(f"Request for new template ignored '{templateName}'")
|
567
|
+
return
|
568
|
+
self.ui.addToLog(f"Creating new template '{templateName}'")
|
569
|
+
obId = ob['obId']
|
570
|
+
api = self.facility.getAPI()
|
571
|
+
tpl, tplVersion = api.createTemplate(obId, templateName)
|
572
|
+
values = tsf.getDict()
|
573
|
+
tpl, tplVersion = api.setTemplateParams(
|
574
|
+
obId, tpl, values, tplVersion)
|
575
|
+
|
576
|
+
def verifyOB(self, ob):
|
577
|
+
if not ob:
|
578
|
+
self.ui.addToLog(f"Request to verify OB ignored")
|
579
|
+
return
|
580
|
+
|
581
|
+
api = self.facility.getAPI()
|
582
|
+
response, _=api.verifyOB(ob['obId'], True)
|
583
|
+
return response
|
584
|
+
|
585
|
+
|
379
586
|
|
380
587
|
# TemplateSignatureFile
|
381
588
|
# use new style class to get __getattr__ advantage
|
@@ -412,6 +619,9 @@ class TSF(object):
|
|
412
619
|
def getDict(self):
|
413
620
|
return self.tsfParams
|
414
621
|
|
622
|
+
def getName(self):
|
623
|
+
return self.tpl
|
624
|
+
|
415
625
|
def getP2Name(self):
|
416
626
|
return self.tpl[0:-4]
|
417
627
|
|
@@ -490,9 +700,14 @@ class FixedDict(object):
|
|
490
700
|
|
491
701
|
class OBTarget(FixedDict):
|
492
702
|
|
493
|
-
def __init__(self):
|
703
|
+
def __init__(self, instrument, scienceTarget):
|
494
704
|
FixedDict.__init__(
|
495
705
|
self, ('name', 'ra', 'dec', 'properMotionRa', 'properMotionDec'))
|
706
|
+
# Target name can include any alphanumeric character, and space, dot, plus or minus signs [a-z][A-Z][0-9][.+- ]
|
707
|
+
# ( https://www.eso.org/sci/observing/phase2/p2intro/p2-tutorials/p2-ImportTargetList.html )
|
708
|
+
self.name = re.sub(r'[^a-zA-Z0-9.\+\- ]+','',scienceTarget.name.strip())
|
709
|
+
self.ra, self.dec = instrument.getCoords(scienceTarget)
|
710
|
+
self.properMotionRa, self.properMotionDec = instrument.getPMCoords(scienceTarget)
|
496
711
|
|
497
712
|
|
498
713
|
class OBConstraints(TSF):
|
@@ -500,4 +715,4 @@ class OBConstraints(TSF):
|
|
500
715
|
def __init__(self, instrument):
|
501
716
|
TSF.__init__(self, instrument, "instrumentConstraints.tsf")
|
502
717
|
# WORKARROUND missing param in p2 json but valid and used in our code
|
503
|
-
instrument.getRangeTable()[self.tpl]["name"]={}
|
718
|
+
instrument.getRangeTable()[self.tpl]["name"] = {}
|