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/facility.py
CHANGED
@@ -3,9 +3,12 @@
|
|
3
3
|
__all__ = []
|
4
4
|
|
5
5
|
import os
|
6
|
+
from tkinter.constants import TRUE
|
6
7
|
import traceback
|
7
8
|
import logging
|
9
|
+
import re
|
8
10
|
|
11
|
+
import p2api
|
9
12
|
|
10
13
|
from a2p2.facility import Facility
|
11
14
|
from a2p2.vlti.gui import VltiUI
|
@@ -80,53 +83,142 @@ class VltiFacility(Facility):
|
|
80
83
|
self.username = None
|
81
84
|
self.api = None
|
82
85
|
|
86
|
+
# store ob to insert after container selection if not done previously
|
87
|
+
self.ob_to_confirm=[]
|
88
|
+
self.ob_to_submit=[]
|
89
|
+
|
90
|
+
|
83
91
|
def getName():
|
84
92
|
return "VLTI"
|
85
93
|
|
86
94
|
def autologin(self):
|
95
|
+
self.ui.addToLog("\nAutologin into P2 API please wait...\n")
|
87
96
|
self.ui.loginFrame.on_loginbutton_clicked()
|
88
97
|
self.a2p2client.ui.showFacilityUI(self.ui)
|
89
98
|
self.ui.showTreeFrame()
|
90
99
|
|
91
100
|
def processOB(self, ob):
|
101
|
+
# process last accepted OB if ob is None
|
102
|
+
if not ob:
|
103
|
+
self.processLastAcceptedOB()
|
104
|
+
return
|
105
|
+
|
92
106
|
# give focus on last updated UI
|
93
107
|
self.a2p2client.ui.showFacilityUI(self.ui)
|
94
108
|
# show ob dict for debug
|
95
109
|
self.ui.addToLog(str(ob), False)
|
96
110
|
|
97
111
|
# OB is checked and submitted by instrument
|
112
|
+
period = ob.getPeriod()
|
113
|
+
self.ui.addToLog(f"Request received for OB creation for P{period} - please press Submit or Abort button")
|
114
|
+
|
115
|
+
# always stores OB and pop them if everything is ok or later selecting containers in the tree
|
116
|
+
self.pushOB(ob)
|
117
|
+
self.tryLastOB()
|
118
|
+
|
119
|
+
def tryLastOB(self):
|
120
|
+
self.showOBConfirmationButtons()
|
121
|
+
# stop if no ob has to be handled
|
122
|
+
if not self.hasOB():
|
123
|
+
return
|
124
|
+
|
125
|
+
ob = self.ob_to_confirm[-1]
|
126
|
+
# run checkOB which may raise some error before connection request
|
127
|
+
# TODO we should test much more because lot of stuff still are beeing processed on live ob creations
|
128
|
+
|
129
|
+
period = ob.getPeriod()
|
98
130
|
instrument = self.getInstrument(ob.instrumentConfiguration.name)
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
#
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
self.
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
131
|
+
insname = instrument.getShortName()
|
132
|
+
|
133
|
+
# performs operation
|
134
|
+
if not self.isConnected():
|
135
|
+
self.ui.showLoginFrame(ob)
|
136
|
+
elif not self.isReadyToSubmit(ob):
|
137
|
+
# self.a2p2client.ui.addToLog("Receive OB for
|
138
|
+
# '"+ob.instrumentConfiguration.name+"'")
|
139
|
+
self.ui.addToLog(
|
140
|
+
f"Selected folder must not be a concatenation. please choose another container for '{insname}' P'{period}'.")
|
141
|
+
elif insname.lower() != self.containerInfo.getInstrument().lower():
|
142
|
+
self.ui.addToLog(
|
143
|
+
f"'{self.containerInfo.getInstrument()}' selection in not applicable for received OB. Please choose another one for '{insname}' P'{period}'.")
|
144
|
+
elif period != int(self.containerInfo.getIpVersion()):
|
145
|
+
self.ui.addToLog(
|
146
|
+
f"Container's IpVersion '{int(self.containerInfo.getIpVersion())}' is not applicable for received OB's one '{period}', please select another '{insname}' container or change it in Aspro2.")
|
147
|
+
else:
|
148
|
+
self.showOBConfirmationButtons(ob, insname=="GRAVITY")
|
149
|
+
|
150
|
+
def showOBConfirmationButtons(self, ob=None, showAcqButtons=False):
|
151
|
+
self.ui.showOBConfirmationButtons(ob, showAcqButtons)
|
152
|
+
|
153
|
+
def isReadyToSubmit(self, ob):
|
154
|
+
return self.api and self.containerInfo.isOk(ob)
|
155
|
+
|
156
|
+
def isConnected(self):
|
157
|
+
return self.connected
|
158
|
+
|
159
|
+
def setConnected(self, flag):
|
160
|
+
self.connected = flag
|
161
|
+
|
162
|
+
def pushOB(self,ob):
|
163
|
+
self.ob_to_confirm.append(ob)
|
164
|
+
|
165
|
+
def hasOB(self):
|
166
|
+
return len(self.ob_to_confirm)>0
|
167
|
+
|
168
|
+
def popOB(self, ignore=False):
|
169
|
+
""" Called as last step : ob is for the selected container and User click on submit!
|
170
|
+
If ignore is set to True, last ob is simply removed else it is forwarded to P2 and removed from stack. """
|
171
|
+
|
172
|
+
if not self.hasOB():
|
173
|
+
return
|
174
|
+
|
175
|
+
# pop last OB
|
176
|
+
o = self.ob_to_confirm.pop()
|
177
|
+
self.showOBConfirmationButtons()
|
178
|
+
|
179
|
+
if ignore:
|
180
|
+
self.ui.addToLog("Last received OB has been aborted")
|
181
|
+
return
|
182
|
+
else:
|
183
|
+
self.ob_to_submit.append(o)
|
184
|
+
|
185
|
+
def processLastAcceptedOB(self):
|
186
|
+
if len(self.ob_to_submit)==0:
|
187
|
+
return
|
120
188
|
|
121
|
-
|
122
|
-
|
189
|
+
o=self.ob_to_submit.pop()
|
190
|
+
try:
|
191
|
+
instrument = self.getInstrument(o.instrumentConfiguration.name)
|
192
|
+
instrument.checkOB(o)
|
193
|
+
# check for warnings and abort if requested by user
|
194
|
+
# this implementation should be more detailled so we can have various VERSION associated to various warning
|
195
|
+
if len(instrument.warnings)>0:
|
196
|
+
linesep="\n - "
|
197
|
+
header=""
|
198
|
+
try:
|
199
|
+
# some ditTable may have a VERSION entry some not...
|
200
|
+
version=instrument.getDitTable()["VERSION"]
|
201
|
+
header= f"According to the template manual {version} :\n \n - \n"
|
202
|
+
except:
|
203
|
+
pass
|
204
|
+
answer = self.ui.AskYesNoMessage(f"{header} {linesep.join(instrument.warnings)}\n\nClick \"Yes\" to proceed \"No\" to abort the submission")
|
205
|
+
instrument.warnings.clear()
|
206
|
+
if answer == False:
|
207
|
+
self.ui.addToLog("Submission has been marked to be aborted - do not proceed")
|
208
|
+
return
|
209
|
+
|
210
|
+
self.ui.addToLog("Everything ready! Request OB creation inside selected container")
|
211
|
+
instrument.submitOB(o, self.containerInfo)
|
212
|
+
|
213
|
+
# TODO add P2Error handling P2Error(r.status_code, method, url,
|
214
|
+
# r.json()['error'])
|
123
215
|
except ValueError as e:
|
124
216
|
traceback.print_exc()
|
125
217
|
trace = traceback.format_exc(limit=1)
|
126
218
|
# ui.ShowErrorMessage("Value error :\n %s \n%s\n\n%s" % (e, trace,
|
127
219
|
# "Aborting submission to P2. Look at the whole traceback in the log."))
|
128
220
|
self.ui.ShowErrorMessage("Value error :\n %s \n\n%s" %
|
129
|
-
|
221
|
+
(e, "Aborting submission to P2. Please check LOG and fix before new submission."))
|
130
222
|
trace = traceback.format_exc()
|
131
223
|
self.ui.addToLog(trace, False)
|
132
224
|
self.ui.setProgress(0)
|
@@ -141,28 +233,23 @@ class VltiFacility(Facility):
|
|
141
233
|
self.ui.addToLog(trace, False)
|
142
234
|
self.ui.setProgress(0)
|
143
235
|
|
144
|
-
def isReadyToSubmit(self):
|
145
|
-
return self.api and self.containerInfo.isOk()
|
146
|
-
|
147
|
-
def isConnected(self):
|
148
|
-
return self.connected
|
149
|
-
|
150
|
-
def setConnected(self, flag):
|
151
|
-
self.connected = flag
|
152
|
-
|
153
236
|
def getStatus(self):
|
237
|
+
ob_info=""
|
238
|
+
if self.hasOB():
|
239
|
+
ob_info=f" / {len(self.ob_to_confirm)} OB in stack"
|
154
240
|
if self.isConnected():
|
155
|
-
return " P2API connected with
|
241
|
+
return f" P2API({self.apitype}) connected with {self.username} { ob_info }"
|
156
242
|
|
157
243
|
def connectAPI(self, username, password, ob):
|
158
|
-
if username == TUTORIAL_LOGIN or username == JMMC_LOGIN:
|
244
|
+
if username == TUTORIAL_LOGIN: # or username == JMMC_LOGIN:
|
159
245
|
self.apitype = DEMO_APITYPE
|
160
246
|
else:
|
161
247
|
self.apitype = PRODUCTION_APITYPE
|
162
248
|
try:
|
163
|
-
logger.info("Connecting to p2 api
|
164
|
-
self.api =
|
165
|
-
|
249
|
+
logger.info("Connecting to p2 api...")
|
250
|
+
self.api = p2api.ApiConnection(self.apitype, username, password)
|
251
|
+
|
252
|
+
self.ui.addToLog("Connected to p2 api (%s)"%self.apitype)
|
166
253
|
# TODO test that api is ok and handle error if any...
|
167
254
|
|
168
255
|
self.refreshTree()
|
@@ -194,6 +281,10 @@ class VltiFacility(Facility):
|
|
194
281
|
"""
|
195
282
|
return CONFDIR
|
196
283
|
|
284
|
+
def getIssVltiTypes(self):
|
285
|
+
return ['snapshot','imaging','time-series','astrometry']
|
286
|
+
|
287
|
+
|
197
288
|
|
198
289
|
# TODO Move code out of this class
|
199
290
|
|
@@ -219,23 +310,46 @@ class P2Container:
|
|
219
310
|
self.containerId = item['containerId']
|
220
311
|
self.log()
|
221
312
|
|
313
|
+
def reset(self):
|
314
|
+
self.run = None # dict returned by p2api
|
315
|
+
self.containerId = None
|
316
|
+
self.item = None
|
317
|
+
|
222
318
|
def log(self):
|
319
|
+
logmsg = f"*** Working with {self} ***"
|
223
320
|
if self.run != self.item and self.item['itemType'] == ITEMTYPE_CONCATENATION:
|
321
|
+
self.reset()
|
224
322
|
self.facility.ui.addToLog(
|
225
|
-
"*** Please do not select a Concatenation and select another container. ***")
|
323
|
+
"*** Please do not select a Concatenation and select another container. Or just append a calibrator. ***", )
|
324
|
+
logger.info(logmsg)
|
226
325
|
else:
|
227
|
-
self.facility.ui.addToLog(
|
326
|
+
self.facility.ui.addToLog(logmsg)
|
228
327
|
|
229
|
-
def isOk(self):
|
328
|
+
def isOk(self, ob):
|
329
|
+
""" Tell if given container is ready to receive content of given OB."""
|
230
330
|
if self.run == None:
|
231
331
|
return False
|
232
332
|
if self.run == self.item:
|
233
333
|
return True
|
234
|
-
|
334
|
+
|
335
|
+
has_calib = False
|
336
|
+
has_science = False
|
337
|
+
for obsConf in ob.observationConfiguration:
|
338
|
+
if 'SCIENCE' in obsConf.type:
|
339
|
+
has_science = True
|
340
|
+
else:
|
341
|
+
has_calib = True
|
342
|
+
|
343
|
+
if has_science:
|
344
|
+
return self.item['itemType'] != ITEMTYPE_CONCATENATION
|
345
|
+
return has_calib
|
235
346
|
|
236
347
|
def getInstrument(self):
|
237
348
|
return self.run['instrument']
|
238
349
|
|
350
|
+
def getIpVersion(self):
|
351
|
+
return self.run['ipVersion']
|
352
|
+
|
239
353
|
def isRoot(self):
|
240
354
|
return self.item.keys() != None and 'pi' in self.item.keys()
|
241
355
|
|
@@ -244,79 +358,3 @@ class P2Container:
|
|
244
358
|
|
245
359
|
def __str__(self):
|
246
360
|
return """instrument:'%s', containerId:'%s'""" % (self.run['instrument'], self.containerId)
|
247
|
-
|
248
|
-
|
249
|
-
# Add wrapper to wrapp p2api/requsts code and enhance
|
250
|
-
import p2api
|
251
|
-
from p2api import P2Error
|
252
|
-
import json
|
253
|
-
|
254
|
-
import requests
|
255
|
-
from requests.adapters import HTTPAdapter
|
256
|
-
from urllib3.util.retry import Retry
|
257
|
-
# Retry Handling for requests
|
258
|
-
# See https://www.peterbe.com/plog/best-practice-with-retries-with-requests
|
259
|
-
# Or https://findwork.dev/blog/advanced-usage-python-requests-timeouts-retries-hooks/
|
260
|
-
def requests_retry_session(
|
261
|
-
retries=3,
|
262
|
-
backoff_factor=1.0,
|
263
|
-
status_forcelist=(500, 502, 504)):
|
264
|
-
|
265
|
-
adapter = HTTPAdapter()
|
266
|
-
adapter.max_retries = Retry(
|
267
|
-
total=retries,
|
268
|
-
read=retries,
|
269
|
-
connect=retries,
|
270
|
-
backoff_factor=backoff_factor,
|
271
|
-
status_forcelist=status_forcelist,
|
272
|
-
respect_retry_after_header=False
|
273
|
-
)
|
274
|
-
session = requests.Session()
|
275
|
-
session.mount('http://', adapter)
|
276
|
-
session.mount('https://', adapter)
|
277
|
-
return session
|
278
|
-
|
279
|
-
|
280
|
-
requests_session = requests_retry_session()
|
281
|
-
|
282
|
-
class P2ApiConnectionWrapper(p2api.ApiConnection):
|
283
|
-
|
284
|
-
def __init__(self, apitype, username, password):
|
285
|
-
p2api.ApiConnection.__init__(self, apitype, username, password)
|
286
|
-
logger.debug("init P2ApiConnectionWrapper")
|
287
|
-
|
288
|
-
def request(self, method, url, data=None, etag=None):
|
289
|
-
self.request_count += 1
|
290
|
-
|
291
|
-
# configure request headers
|
292
|
-
assert self.access_token is not None
|
293
|
-
headers = {
|
294
|
-
'Authorization': 'Bearer ' + self.access_token,
|
295
|
-
'Accept': 'application/json'
|
296
|
-
}
|
297
|
-
if data is not None:
|
298
|
-
headers['Content-Type'] = 'application/json'
|
299
|
-
if etag is not None:
|
300
|
-
headers['If-Match'] = etag
|
301
|
-
|
302
|
-
# make request
|
303
|
-
url = self.apiUrl + url
|
304
|
-
if self.debug:
|
305
|
-
print(method, url, data)
|
306
|
-
# using session makes some calls more than 2.5 X faster
|
307
|
-
# r = requests.request(method, url, headers=headers, data=json.dumps(data))
|
308
|
-
r = requests_session.request(method, url, headers=headers, data=json.dumps(data))
|
309
|
-
content_type = r.headers['Content-Type'].split(';')[0]
|
310
|
-
etag = r.headers.get('ETag', None)
|
311
|
-
|
312
|
-
# handle response
|
313
|
-
if 200 <= r.status_code < 300:
|
314
|
-
if content_type == 'application/json':
|
315
|
-
data = r.json()
|
316
|
-
return data, etag
|
317
|
-
return None, etag
|
318
|
-
elif content_type == 'application/json' and 'error' in r.json():
|
319
|
-
raise P2Error(r.status_code, method, url, r.json()['error'])
|
320
|
-
else:
|
321
|
-
raise P2Error(r.status_code, method, url, 'oops unknown error '+r)
|
322
|
-
# TODO handle 401 ? should we do something to reconnect API ...
|