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.
Files changed (46) hide show
  1. a2p2/__main__.py +39 -0
  2. a2p2/chara/facility.py +51 -3
  3. a2p2/chara/gui.py +29 -5
  4. a2p2/client.py +144 -34
  5. a2p2/facility.py +11 -1
  6. a2p2/gui.py +32 -1
  7. a2p2/jmmc/__init__.py +7 -0
  8. a2p2/jmmc/catalogs.py +129 -0
  9. a2p2/jmmc/generated_models.py +191 -0
  10. a2p2/jmmc/models.py +104 -0
  11. a2p2/jmmc/services.py +16 -0
  12. a2p2/jmmc/utils.py +130 -0
  13. a2p2/jmmc/webservices.py +48 -0
  14. a2p2/ob.py +95 -9
  15. a2p2/samp.py +17 -0
  16. a2p2/version.py +205 -137
  17. a2p2/vlti/conf/GRAVITY_ditTable.json +21 -19
  18. a2p2/vlti/conf/GRAVITY_rangeTable.json +200 -28
  19. a2p2/vlti/conf/MATISSE_rangeTable.json +58 -22
  20. a2p2/vlti/conf/PIONIER_ditTable.json +1 -1
  21. a2p2/vlti/conf/PIONIER_rangeTable.json +16 -18
  22. a2p2/vlti/facility.py +156 -118
  23. a2p2/vlti/gravity.py +243 -311
  24. a2p2/vlti/gui.py +162 -40
  25. a2p2/vlti/instrument.py +264 -49
  26. a2p2/vlti/matisse.py +61 -147
  27. a2p2/vlti/pionier.py +34 -157
  28. {a2p2-0.2.15.dist-info → a2p2-0.7.4.dist-info}/METADATA +34 -20
  29. a2p2-0.7.4.dist-info/RECORD +39 -0
  30. {a2p2-0.2.15.dist-info → a2p2-0.7.4.dist-info}/WHEEL +1 -1
  31. {a2p2-0.2.15.dist-info → a2p2-0.7.4.dist-info}/entry_points.txt +0 -1
  32. a2p2/vlti/confP104/GRAVITY_ditTable.json +0 -122
  33. a2p2/vlti/confP104/GRAVITY_rangeTable.json +0 -202
  34. a2p2/vlti/confP104/MATISSE_ditTable.json +0 -2
  35. a2p2/vlti/confP104/MATISSE_rangeTable.json +0 -202
  36. a2p2/vlti/confP104/PIONIER_ditTable.json +0 -77
  37. a2p2/vlti/confP104/PIONIER_rangeTable.json +0 -118
  38. a2p2/vlti/confP105/GRAVITY_ditTable.json +0 -37
  39. a2p2/vlti/confP105/GRAVITY_rangeTable.json +0 -42
  40. a2p2/vlti/confP105/MATISSE_ditTable.json +0 -2
  41. a2p2/vlti/confP105/MATISSE_rangeTable.json +0 -44
  42. a2p2/vlti/confP105/PIONIER_ditTable.json +0 -25
  43. a2p2/vlti/confP105/PIONIER_rangeTable.json +0 -38
  44. a2p2-0.2.15.dist-info/RECORD +0 -44
  45. {a2p2-0.2.15.dist-info → a2p2-0.7.4.dist-info}/LICENSE +0 -0
  46. {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
- try:
100
- # run checkOB which may raise some error before connection request
101
- instrument.checkOB(ob)
102
-
103
- insname = instrument.getShortName()
104
-
105
- # performs operation
106
- if not self.isConnected():
107
- self.ui.showLoginFrame(ob)
108
- elif not self.isReadyToSubmit():
109
- # self.a2p2client.ui.addToLog("Receive OB for
110
- # '"+ob.instrumentConfiguration.name+"'")
111
- self.ui.addToLog(
112
- "Please select a folder (not a concatenation) in the above list. OBs are not shown")
113
- elif insname.lower() != self.containerInfo.getInstrument().lower():
114
- self.ui.ShowErrorMessage("Aborting: container's instrument '%s' in not applicable for received OB's one '%s'." %(self.containerInfo.getInstrument(),insname))
115
- else:
116
- self.ui.addToLog(
117
- "everything ready! Request OB creation inside selected container ")
118
- instrument.submitOB(ob, self.containerInfo)
119
- self.refreshTree()
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
- # TODO add P2Error handling P2Error(r.status_code, method, url,
122
- # r.json()['error'])
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
- (e, "Aborting submission to P2. Please check LOG and fix before new submission."))
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 " + self.username
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(%s).."%self.apitype)
164
- self.api = P2ApiConnectionWrapper(self.apitype, username, password)
165
- logger.info("Connected to p2 api")
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("*** Working with %s ***" % self)
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
- return self.item['itemType'] != ITEMTYPE_CONCATENATION
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 ...