Coverage for /wheeldirectory/casa-6.7.0-12-py3.10.el8/lib/py/lib/python3.10/site-packages/casatasks/private/jyperk.py: 79%
381 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-10-31 19:10 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-10-31 19:10 +0000
1import collections
2import csv
3import json
4import os
5import re
6from socket import timeout as socket_timeout
7import ssl
8import string
9from time import sleep
10from urllib.error import HTTPError, URLError
11from urllib.parse import urlencode
12from urllib.request import urlopen
14import certifi
15import numpy as np
17from casatasks import casalog
18from casatasks.private.sdutil import (table_manager, table_selector,
19 tool_manager)
20from casatools import measures
21from casatools import ms as mstool
22from casatools import msmetadata, quanta
25# Jy/K DB part
26def gen_factor_via_web_api(vis, spw='*',
27 endpoint='asdm',
28 timeout=180, retry=3, retry_wait_time=5):
29 """Generate factors via Jy/K Web API.
31 This function will be used task_gencal.
33 Arguments:
34 vis {str} -- The file path of the visibility data.
35 spw {str} -- Spectral windows.
36 endpoint {str} -- The endpoint of Jy/K DB Web API to access. Options are
37 'asdm' (default), 'model-fit', 'interpolation'.
38 timeout {int} -- Maximum waiting time [sec] for the Web API access, defaults
39 to 180 sec.
40 retry {int} -- Number of retry when the Web API access fails, defaults to 3
41 times.
42 retry_wait_time {int} -- Waiting time [sec] until next query when the Web API
43 access fails, defaults to 5 sec.
44 """
45 if spw == '':
46 spw = '*'
48 assert endpoint in ['asdm', 'model-fit', 'interpolation'], \
49 'The JyPerKDatabaseClient class requires one of endpoint: asdm, model-fit or interpolation'
51 return __factor_creator_via_jy_per_k_db(endpoint=endpoint, vis=vis, spw=spw,
52 factory=__jyperk_factory[endpoint],
53 timeout=timeout, retry=retry,
54 retry_wait_time=retry_wait_time)
57def __factor_creator_via_jy_per_k_db(endpoint='', vis=None, spw='*',
58 factory=None,
59 timeout=180, retry=3, retry_wait_time=5):
60 params_generator = factory[0]
61 response_translator = factory[1]
63 params = params_generator.get_params(vis, spw=spw)
64 client = JyPerKDatabaseClient(endpoint,
65 timeout=timeout, retry=retry,
66 retry_wait_time=retry_wait_time)
67 manager = RequestsManager(client)
68 resps = manager.get(params)
69 return response_translator.convert(resps, vis, spw=spw)
72QueryStruct = collections.namedtuple('QueryStruct', ['param', 'subparam'])
73ResponseStruct = collections.namedtuple('ResponseStruct', ['response', 'subparam'])
76class ASDMParamsGenerator():
77 """A class to generate required parameters for Jy/K Web API with asdm.
79 Usage:
80 vis = "./uid___A002_X85c183_X36f.ms"
81 params = ASDMParamsGenerator.get_params(vis)
82 """
84 @classmethod
85 def get_params(cls, vis, spw=None):
86 """Generate required parameters for Jy/K Web API.
88 Arguments:
89 vis {str} -- The file path of the visibility data.
90 spw {str} -- This parameter is not used. It is provided to align the
91 calling method with other classes (InterpolationParamsGenerator and
92 ModelFitParamsGenerator).
94 Returns:
95 Generator Object -- yield QueryStruct() object. A sample like below.
96 QueryStruct(
97 param={'uid': 'uid://A002/X85c183/X36f'},
98 subparam='./uid___A002_X85c183_X36f.ms'
99 )
100 """
101 yield QueryStruct(param={'uid': cls._vis_to_uid(vis)}, subparam=vis)
103 @staticmethod
104 def _vis_to_uid(vis):
105 """Convert MS name like 'uid___A002_Xabcd_X012 into uid://A002/Xabcd/X012'.
107 Arguments:
108 vis {str} -- The file path of the visibility data.
110 Returns:
111 str -- Corresponding ASDM uid.
112 """
113 basename = os.path.basename(os.path.abspath(vis))
114 pattern = r'^uid___A[0-9][0-9][0-9]_X[0-9a-f]+_X[0-9a-f]+\.ms$'
115 if re.match(pattern, basename):
116 return basename.replace('___', '://').replace('_', '/').replace('.ms', '')
117 else:
118 raise RuntimeError('MS name is not appropriate for DB query: {}'.format(basename))
121class InterpolationParamsGenerator():
122 """A class to generate required parameters for Jy/K Web API with interpolation.
124 Usage:
125 vis = './uid___A002_X85c183_X36f.ms'
126 params = InterpolationParamsGenerator.get_params(vis)
127 """
129 @classmethod
130 def get_params(cls, vis, spw='*'):
131 if spw == '':
132 spw = '*'
134 if spw == '*':
135 spw = cls._get_available_spw(vis, spw)
137 params = {}
139 science_windows = cls._get_science_windows(vis, spw)
140 timerange, antenna_names, basebands, mean_freqs, spwnames = \
141 cls._extract_msmetadata(science_windows, vis)
143 mean_freqs = cls._get_mean_freqs(vis, science_windows)
144 bands = Bands.get(science_windows, spwnames, mean_freqs, vis)
146 params['date'] = cls._mjd_to_datestring(timerange['begin'])
147 params['temperature'] = cls._get_mean_temperature(vis)
148 params.update(cls._get_aux_params())
150 for antenna_id, antenna_name in enumerate(antenna_names):
151 params['antenna'] = antenna_name
152 params['elevation'] = MeanElevation.get(vis, antenna_id)
154 for sw_id in science_windows:
155 params['band'] = bands[sw_id]
156 params['baseband'] = basebands[sw_id]
157 params['frequency'] = mean_freqs[sw_id]
158 subparam = {'vis': vis, 'spwid': int(sw_id)}
159 yield QueryStruct(param=params, subparam=subparam)
161 @staticmethod
162 def _get_science_windows(vis, spw):
163 ms = mstool()
164 selected = ms.msseltoindex(vis, spw=spw)
165 science_windows = selected['spw']
166 return science_windows
168 @staticmethod
169 def _extract_msmetadata(science_windows, vis):
170 with tool_manager(vis, msmetadata) as msmd:
171 timerange = msmd.timerangeforobs(0)
172 antenna_names = msmd.antennanames()
173 basebands = dict((i, msmd.baseband(i)) for i in science_windows)
174 mean_freqs = dict((i, msmd.meanfreq(i)) for i in science_windows)
175 spwnames = msmd.namesforspws(science_windows)
177 return timerange, antenna_names, basebands, mean_freqs, spwnames
179 @staticmethod
180 def _get_available_spw(vis, spw):
181 science_windows = InterpolationParamsGenerator._get_science_windows(vis, spw=spw)
182 with tool_manager(vis, msmetadata) as msmd:
183 spwnames = msmd.namesforspws(science_windows)
185 spw = ','.join(
186 map(str, [i for i, name in enumerate(spwnames)
187 if not name.startswith('WVR')]))
188 return spw
190 @staticmethod
191 def _mjd_to_datestring(epoch):
192 me = measures()
193 qa = quanta()
195 if epoch['refer'] != 'UTC':
196 try:
197 epoch = me.measure(epoch, 'UTC')
198 finally:
199 me.done()
201 datestring = qa.time(epoch['m0'], form='fits')
202 return datestring[0]
204 @staticmethod
205 def _get_mean_temperature(vis):
206 with table_manager(os.path.join(vis, 'WEATHER')) as tb:
207 valid_temperatures = np.ma.masked_array(
208 tb.getcol("TEMPERATURE"),
209 tb.getcol("TEMPERATURE_FLAG")
210 )
211 return valid_temperatures.mean()
213 @staticmethod
214 def _get_mean_freqs(vis, science_windows):
215 with table_manager(os.path.join(vis, 'SPECTRAL_WINDOW')) as tb:
216 mean_freqs = dict((i, tb.getcell('CHAN_FREQ', i).mean()) for i in science_windows)
217 return mean_freqs
219 @staticmethod
220 def _get_aux_params():
221 return {'delta_days': 1000}
224class ModelFitParamsGenerator(InterpolationParamsGenerator):
225 """A class to generate required parameters for Jy/K Web API with model-fit."""
227 @staticmethod
228 def _get_aux_params():
229 return {}
232class Bands():
233 """A class to extract all bands corresponding from VIS file.
235 Usage:
236 bands = Bands.get(science_windows, spwnames, mean_freqs, vis)
237 """
239 @classmethod
240 def get(cls, science_windows, spwnames, mean_freqs, vis):
241 """Return all bands corresponding to the 'science_window' given in the input.
243 First the method scan 'spwnames', if the band can be detect, the method will
244 adopt this value. In other case, the method compare the freq with the 'mean_freqs'
245 at which the band was detect, the method detect the band from the frequencies
246 that are closest to the result.
247 """
248 bands = cls._extract_bands_from_spwnames(science_windows, spwnames)
249 mean_freqs_with_undetected_band = cls._filter_mean_freqs_with_undetected_band(
250 science_windows, spwnames, mean_freqs)
251 if len(mean_freqs_with_undetected_band) > 0:
252 bands.update(
253 cls._detect_bands_from_mean_freqs(mean_freqs_with_undetected_band, vis)
254 )
255 return bands
257 @staticmethod
258 def _extract_bands_from_spwnames(science_windows, spwnames):
259 """Extract bands that contain band information in the spwname.
261 The spwnames is like 'X835577456#ALMA_RB_06#BB_2#SW-01#CH_AVG'.
262 """
263 bands = {}
264 for sw, spwname in zip(science_windows, spwnames):
265 if 'ALMA_RB_' in spwname:
266 bands[sw] = int(re.findall(r'^.*?ALMA_RB_(\d+)#.*', spwname)[0])
267 return bands
269 @staticmethod
270 def _filter_mean_freqs_with_undetected_band(science_windows, spwnames, mean_freqs):
271 """Filter mean freqs without 'ALMA_RB_'."""
272 filtered_mean_freqs = {}
273 for sw, spwname in zip(science_windows, spwnames):
274 if 'ALMA_RB_' not in spwname:
275 filtered_mean_freqs[sw] = mean_freqs[sw]
276 return filtered_mean_freqs
278 @staticmethod
279 def _detect_bands_from_mean_freqs(target_mean_freqs, vis):
280 """Extract bands using the mean freqs.
282 Params:
283 target_mean_freqs {dict} -- The mean freqs which does not been detected the bands.
284 vis {str} -- The file path of the visibility data.
285 """
286 known_bands = Bands._get_known_bands(vis)
287 science_windows = list(known_bands.keys())
288 mean_freqs = Bands._extract_mean_freqs(science_windows, vis)
290 extracted_bands = {}
291 for spw, target_mean_freq in target_mean_freqs.items():
292 nearest_spw = Bands._calc_nearest_spw(mean_freqs, target_mean_freq)
293 extracted_bands[spw] = known_bands[nearest_spw]
294 return extracted_bands
296 @staticmethod
297 def _calc_nearest_spw(available_mean_freqs, mean_freq):
298 available_mean_freqs_list = list(available_mean_freqs.values())
299 available_spw = list(available_mean_freqs.keys())
300 nearest_i = np.argmin(np.abs(np.array(available_mean_freqs_list) - mean_freq))
301 return available_spw[nearest_i]
303 @staticmethod
304 def _get_spwnames(vis, science_windows):
305 with tool_manager(vis, msmetadata) as msmd:
306 spwnames = msmd.namesforspws(science_windows)
307 return spwnames
309 @staticmethod
310 def _get_known_bands(vis):
311 science_windows = Bands._get_all_science_windows(vis)
312 with tool_manager(vis, msmetadata) as msmd:
313 spwnames = msmd.namesforspws(science_windows)
314 bands = Bands._extract_bands_from_spwnames(science_windows, spwnames)
315 return bands
317 @staticmethod
318 def _get_all_science_windows(vis):
319 ms = mstool()
320 selected = ms.msseltoindex(vis, spw='*')
321 science_windows = selected['spw']
322 return science_windows
324 @staticmethod
325 def _extract_mean_freqs(science_windows, vis):
326 with tool_manager(vis, msmetadata) as msmd:
327 mean_freqs = dict((i, msmd.meanfreq(i)) for i in science_windows)
328 return mean_freqs
331class MeanElevation():
332 """A class to extract elevations from the VIS file and calcurate elevations average.
334 Usage:
335 mean_elevation = MeanElevation.get(vis, antenna_id)
336 """
338 @classmethod
339 def get(cls, vis, antenna_id):
340 """Get elevations aveage."""
341 stateid = cls._get_stateid(vis)
342 science_dd = cls._get_science_dd(vis)
343 rows = cls._query_rows(vis, science_dd, stateid.tolist(), antenna_id)
345 return cls._calc_elevation_mean(rows, vis)
347 @staticmethod
348 def _get_stateid(vis):
349 with tool_manager(vis, mstool) as ms:
350 ms.msselect({'scanintent': 'OBSERVE_TARGET#ON_SOURCE'})
351 selected = ms.msselectedindices()
353 stateid = selected['stateid']
354 return stateid
356 @staticmethod
357 def _get_science_dd(vis):
358 with tool_manager(vis, msmetadata) as msmd:
359 science_spw = list(np.intersect1d(
360 msmd.almaspws(tdm=True, fdm=True),
361 msmd.spwsforintent('OBSERVE_TARGET#ON_SOURCE')
362 ))
363 science_dd = [msmd.datadescids(spw=i)[0] for i in science_spw]
365 return science_dd
367 @staticmethod
368 def _query_rows(vis, science_dd, stateid, antenna_id):
369 query = f'ANTENNA1=={antenna_id}&&ANTENNA2=={antenna_id}&&DATA_DESC_ID=={science_dd[0]}' + \
370 f'&&STATE_ID IN {list(stateid)}'
371 with table_selector(vis, query) as tb:
372 rows = tb.rownumbers()
374 return rows
376 @staticmethod
377 def _calc_elevation_mean(rows, vis):
378 elevations = []
379 qa = quanta()
381 with tool_manager(vis, msmetadata) as msmd:
382 for row in rows:
383 dirs = msmd.pointingdirection(row, initialrow=row)
384 assert dirs['antenna1']['pointingdirection']['refer'].startswith('AZEL')
385 el_deg = qa.convert(dirs['antenna1']['pointingdirection']['m1'], 'deg')
386 elevations.append(el_deg['value'])
388 elevations = np.asarray(elevations)
390 if len(elevations) == 0:
391 elevation_mean = np.nan
392 else:
393 elevation_mean = elevations.mean()
395 return elevation_mean
398class RequestsManager():
399 """A class to manage the Jy/K Database access by the param.
401 Usage:
402 vis = "./uid___A002_Xb32033_X9067.ms"
403 client = JyPerKDatabaseClient('asdm')
404 params = ASDMParamsGenerator.get_params(vis)
405 manager = RequestsManager(client)
406 manager.get(params)
407 """
409 def __init__(self, client):
410 """Set client."""
411 self.client = client
413 def get(self, params):
414 """Get the responses of the Jy/K DB."""
415 dataset = [
416 {'response': self.client.get(param.param), 'aux': param.subparam}
417 for param in params]
418 return self._filter_success_is_true(dataset)
420 def _filter_success_is_true(self, dataset):
421 return [data for data in dataset if data['response']['success']]
424class JyPerKDatabaseClient():
425 """A class to get values from Jy/K Web API.
427 The Jy/K Web API address is 'https://asa.alma.cl/science/jy-kelvins'. The address
428 can be changed with the environment variable 'JYPERKDB_URL'.
429 """
431 BASE_URL = os.getenv('JYPERKDB_URL', 'https://asa.alma.cl/science/jy-kelvins')
433 def __init__(self, endpoint, timeout=180, retry=3, retry_wait_time=5):
434 """Set the parameters to be used when accessing the Web API.
436 Arguments:
437 endpoint {str} -- The endpoint of Jy/K DB Web API to access. Options are
438 'asdm' (default), 'model-fit', 'interpolation'.
439 timeout {int} --- Maximum waiting time [sec] for the Web API access, defaults
440 to 180 sec.
441 retry {int} -- Number of retry when the Web API access fails, defaults to 3
442 times.
443 retry_wait_time {int} -- Waiting time [sec] until next query when the Web API
444 access fails, defaults to 5 sec.
445 """
446 assert endpoint in ['asdm', 'model-fit', 'interpolation'], \
447 'The JyPerKDatabaseClient class requires one of endpoint: ' \
448 'asdm, model-fit or interpolation'
449 self.web_api_url = self._generate_web_api_url(endpoint)
450 self.timeout = timeout
451 self.retry = retry
452 self.retry_wait_time = retry_wait_time
454 def get(self, param):
455 """Get the Web API response.
457 Arguments:
458 param {dict} -- The parameters used in the Web API.
459 """
460 request_url = self._generate_url(param)
461 body = self._try_to_get_response(request_url)
462 retval = self._convert_to_json(body)
463 self._check_retval(retval)
464 return retval
466 def _generate_web_api_url(self, endpoint_type):
467 web_api_url = '/'.join([self.BASE_URL, endpoint_type])
468 if not web_api_url.endswith('/'):
469 web_api_url += '/'
470 return web_api_url
472 def _generate_url(self, param):
473 # encode params
474 encoded = urlencode(param)
475 query = '?'.join([self.web_api_url, encoded])
476 return query
478 def _retrieve(self, url):
479 """Access to Jy/K DB and return response.
481 Arguments:
482 url {str} -- url to retrieve in the Jy/K Web API.
484 Returns:
485 dict -- If the request is successfull, dictionary contain below information.
486 success {bool} -- Boolean stating whether the request succeeded or not.
487 timestamp {str} -- Timestamp of when the request was received by the Jy/K Service.
488 elapsed {bool} -- Boolean stating whether the request succeeded or not.
489 error {dict} -- Error field which has a message describing the problem.
490 query {dict} -- Dictionary with all the parameters passed to the request.
491 data {dict} -- Data field which varies for each endpoint.
493 Please check the following source for details.
494 https://confluence.alma.cl/pages/viewpage.action?pageId=35258466
495 """
496 try:
497 ssl_context = ssl.create_default_context(cafile=certifi.where())
498 with urlopen(url, context=ssl_context, timeout=self.timeout) as resp:
499 body = resp.read()
500 return {'status': 'Success', 'err_msg': None, 'body': body}
501 except HTTPError as e: # 4xx, 5xx
502 msg = 'Failed to load URL: {0}\n'.format(url) \
503 + 'Error Message: HTTPError(code={0}, Reason="{1}")\n'.format(e.code, e.reason)
504 casalog.post(msg)
505 return {'status': 'HTTPError', 'err_msg': msg}
506 except URLError as e: # not connect
507 msg = 'Failed to load URL: {0}\n'.format(url) \
508 + 'Error Message: URLError(Reason="{0}")\n'.format(e.reason)
509 casalog.post(msg)
510 return {'status': 'URLError', 'err_msg': msg}
511 except socket_timeout as e: # not connect
512 msg = 'Failed to load URL: {0}\n'.format(url) \
513 + 'Error Message: URLError(Reason="{0}")\n'.format(e)
514 casalog.post(msg)
515 return {'status': 'URLError', 'err_msg': msg}
517 def _try_to_get_response(self, url):
518 casalog.post(f'Accessing Jy/K DB: request URL is "{url}"')
519 for i in range(self.retry):
520 response_with_tag = self._retrieve(url)
521 if response_with_tag['status'] == 'Success':
522 casalog.post('Got a response successfully')
523 return response_with_tag['body']
525 if i < self.retry - 1:
526 casalog.post(response_with_tag['err_msg'])
527 casalog.post(
528 f'Sleeping for {str(self.retry_wait_time)} seconds because the request failed')
529 sleep(self.retry_wait_time)
530 casalog.post(f'Retry to access Jy/K DB ({str(i + 2)}/{str(self.retry)})')
532 if response_with_tag['status'] != 'Success':
533 raise RuntimeError(response_with_tag['err_msg'])
535 def _convert_to_json(self, response):
536 try:
537 return json.loads(response)
539 except json.JSONDecodeError as e:
540 msg = 'Failed to get Jy/K factors from DB: JSON Syntax error. {}'.format(e)
541 casalog.post(msg)
542 raise RuntimeError(msg)
544 def _check_retval(self, retval):
545 """Check if 'success' of retval dict is True.
547 This method only checks if the api was able to complete the process successfully or not.
548 It is expected that 'success' will be False as a response in case if query was successful
549 but response contains nothing due to any error in the server. In that case, this method
550 raises RuntimeError to propagate server side error to the user.
552 Arguments:
553 retval: dictionary translated from JSON response from the server
555 Raises:
556 RuntimeError: response contains nothing due to any error in the server
557 """
558 if not retval['success']:
559 msg = 'Failed to get Jy/K factors from DB: {}'.format(retval['error'])
560 casalog.post(msg, priority='ERROR')
561 raise RuntimeError(msg)
564class Translator():
565 """A class containing the methods required to convert Jy/K DB responses into factors."""
567 @staticmethod
568 def format_cal_table_format(factors): # _format_jyperk
569 """Format given dictionary to the formatted list.
571 Sample formated list:
572 [['MS_name', 'antenna_name', 'spwid', 'pol string', 'factor'],
573 ['MS_name', 'antenna_name', 'spwid', 'pol string', 'factor'],
574 ...
575 ['MS_name', 'antenna_name', 'spwid', 'pol string', 'factor']]
577 Arguments:
578 factors {dict} -- Dictionary containing Jy/K factors with meta data.
580 Returns:
581 list -- Formatted list of Jy/K factors.
582 """
583 template = string.Template('$MS $Antenna $Spwid I $factor')
584 factors = [list(map(str, template.safe_substitute(**factor).split())) for factor in factors]
585 return factors
587 @staticmethod
588 def filter_jyperk(vis, factors, spw):
589 """Filter factors using spw."""
590 ms = mstool()
591 selected = ms.msseltoindex(vis=vis, spw=spw)
592 science_windows = selected['spw']
593 filtered = [
594 i for i in factors if (len(i) == 5)
595 and (
596 i[0] == os.path.basename(os.path.abspath(vis)) and (int(i[2]) in science_windows)
597 )
598 ]
599 return filtered
602class ASDMRspTranslator():
603 """A class to convert the response for asdm from the Jy/K DB to factors."""
605 @classmethod
606 def convert(cls, data, vis, spw='*'):
607 """Convert from the response to list with factor.
609 Arguments:
610 spw {str} -- This parameter is not used. It is provided to align the
611 calling method with other classes (InterpolationRspTranslator and
612 ModelFitRspTranslator).
614 Returns:
615 list -- List of Jy/K conversion factors with meta data.
616 """
617 assert len(data) == 1
618 data = data[0]
620 factors = cls._extract_factors(data)
621 formatted = Translator.format_cal_table_format(factors)
623 return Translator.filter_jyperk(vis, formatted, spw)
625 @staticmethod
626 def _extract_factors(data):
627 return data['response']['data']['factors']
630class InterpolationRspTranslator():
631 """A class to convert the responses for interpolation from the Jy/K DB to factors."""
633 @classmethod
634 def convert(cls, data_set, vis, spw='*'):
635 """Convert from the response to list with factor.
637 Arguments:
638 data_set {dict} -- The result of the Web API.
639 vis {str} -- The file path of the visibility data.
640 spw {str} -- Spectral windows.
642 Returns:
643 list -- List of Jy/K conversion factors with meta data.
644 """
645 cal_dict = cls._dataset_to_cal_dict(data_set, cls._extract_factor)
646 formatted = Translator.format_cal_table_format(cal_dict)
648 return Translator.filter_jyperk(vis, formatted, spw)
650 @staticmethod
651 def _extract_factor(data):
652 if data['response']['query']['elevation'] == 'nan':
653 casalog.post('The elevation mean is np.nan, so the factor is set 1.',
654 priority='WARN',
655 origin='jyperk.MeanElevation._calc_elevation_mean')
656 return 1
658 return data['response']['data']['factor']['mean']
660 @staticmethod
661 def _dataset_to_cal_dict(dataset, _extract_factor):
662 return_data = []
664 for data in dataset:
665 # aux is dictionary holding vis and spw id
666 aux = data['aux']
667 if not isinstance(aux, dict):
668 raise TypeError('The response.aux in the JSON obtained from Jy/K db must be dict.')
670 if 'vis' not in aux:
671 raise KeyError(
672 'The response.aux in the JSON obtained from Jy/K db must contain vis.')
674 if 'spwid' not in aux:
675 raise KeyError(
676 'The response.aux in the JSON obtained from Jy/K db must contain spwid.')
678 spwid = aux['spwid']
679 if not isinstance(spwid, int):
680 raise TypeError(
681 'The response.aux.spwid in the JSON obtained from Jy/K db must be int.')
683 vis = aux['vis']
684 if not isinstance(vis, str):
685 raise TypeError(
686 'The response.aux.vis in the JSON obtained from Jy/K db must be str.')
688 basename = os.path.basename(os.path.abspath(vis))
690 factor = _extract_factor(data)
691 polarization = 'I'
692 antenna = data['response']['query']['antenna']
694 return_data.append({'MS': basename, 'Antenna': antenna, 'Spwid': spwid,
695 'Polarization': polarization, 'factor': factor})
696 return return_data
699class ModelFitRspTranslator(InterpolationRspTranslator):
700 """A class to convert the responses for model-fit from the Jy/K DB to factors."""
702 @staticmethod
703 def _extract_factor(data):
704 return data['response']['data']['factor']
707__jyperk_factory = {
708 'asdm': (ASDMParamsGenerator, ASDMRspTranslator),
709 'interpolation': (InterpolationParamsGenerator, InterpolationRspTranslator),
710 'model-fit': (ModelFitParamsGenerator, ModelFitRspTranslator),
711}
714# file part
715class JyPerKReader4File():
716 """A class to read from CSV file and format factors.
718 This function will be used task_gencal.
719 """
721 def __init__(self, infile):
722 """Set a parameter.
724 Arguments:
725 infile {str} -- The file path of CSV which the factors are stored.
726 """
727 self.infile = infile
729 def get(self):
730 """Read jyperk factors from a file and returns a string list.
732 Returns:
733 list -- [['MS','ant','spwid','polid','factor'], ...]
734 """
735 if not os.path.isfile(self.infile):
736 raise OSError(f'There is no Jy/K db-derived factor file: {self.infile}')
738 with open(self.infile, 'r') as f:
739 return list(self._extract_jyperk_from_csv(f))
741 def _extract_jyperk_from_csv(self, stream):
742 reader = csv.reader(stream)
743 # Check if first line is header or not
744 line = next(reader)
745 if len(line) == 0 or line[0].strip().upper() == 'MS' or line[0].strip()[0] == '#':
746 # must be a header, commented line, or empty line
747 pass
748 elif len(line) == 5:
749 # may be a data record
750 yield line
751 else:
752 casalog.post('Jy/K factor file is invalid format')
753 for line in reader:
754 if len(line) == 0 or len(line[0]) == 0 or line[0][0] == '#':
755 continue
756 elif len(line) == 5:
757 yield line
758 else:
759 casalog.post('Jy/K factor file is invalid format')