def scan_archive_struct(self, rootdir, progress_bar=None): self.archiveroot = rootdir rnx = [] path2rnx = [] fls = [] for path, _, files in scandir.walk(rootdir): for file in files: file_path = os.path.join(path, file) crinex = file_path.rsplit(rootdir + '/')[1] if progress_bar is not None: progress_bar.set_postfix(crinex=crinex) progress_bar.update() try: RinexNameFormat(file) # except if invalid # only add valid rinex files (now allows the full range) fls.append(file) rnx.append(crinex) path2rnx.append(file_path) except pyRinexName.RinexNameException: if file.endswith('DS_Store') or file.startswith('._'): # delete the stupid mac files file_try_remove(file_path) return rnx, path2rnx, fls
def scan_archive_struct(self, rootdir, progress_bar=None): self.archiveroot = rootdir rnx = [] path2rnx = [] fls = [] for path, _, files in scandir.walk(rootdir): for file in files: if progress_bar is not None: progress_bar.set_postfix( crinex=os.path.join(path, file).rsplit(rootdir + '/')[1]) progress_bar.update() try: _ = RinexNameFormat(file) # only add valid rinex files (now allows the full range) fls.append(file) rnx.append( os.path.join(path, file).rsplit(rootdir + '/')[1]) path2rnx.append(os.path.join(path, file)) except pyRinexName.RinexNameException: if file.endswith('DS_Store') or file[0:2] == '._': # delete the stupid mac files try: os.remove(os.path.join(path, file)) except Exception: sys.exc_clear() return rnx, path2rnx, fls
def parse_archive_keys(self, path_filename, key_filter=()): """ based on a path and filename, this function parses the data and organizes the information in a dictionary key_filter allows to select which keys you want to get a hold on. The order of the keys in the path is given by the database table rinex_tank_struct :param path: :param key_filter: :return: """ keys_out = dict() try: path = os.path.dirname(path_filename).split('/') filename = os.path.basename(path_filename) # check the number of levels in path parts against the number of expected levels if len(path) != len(self.levels): return False, {} # now look in the different levels to match more data (or replace filename keys) for key in self.levels: if len(path[key['Level'] - 1]) != key['TotalChars']: return False, {} if key['isnumeric'] == '1': keys_out[key['KeyCode']] = int(path[key['Level'] - 1]) else: keys_out[key['KeyCode']] = path[key['Level'] - 1].lower() if not filename.endswith('.info'): fileparts = RinexNameFormat(filename) # fill in all the possible keys_out using the crinex file info keys_out['station'] = fileparts.StationCode keys_out['doy'] = fileparts.date.doy keys_out['session'] = fileparts.session keys_out['year'] = fileparts.date.year # check date is valid and also fill day and month keys_out keys_out['day'] = fileparts.date.day keys_out['month'] = fileparts.date.month return True, { key: keys_out[key] for key in keys_out.keys() if key in key_filter } except Exception: return False, {}
def insert_rinex(self, record=None, rinexobj=None): """ Insert a RINEX record and file into the database and archive. If only record is provided, only insert into db If only rinexobj is provided, then RinexRecord of rinexobj is used for the insert. If both are given, then RinexRecord overrides the passed record. :param record: a RinexRecord dictionary to make the insert to the db :param rinexobj: the pyRinex object containing the file being processed :param rnxaction: accion to perform to rinexobj. :return: True if insertion was successful. False if no insertion was done. """ if record is None and rinexobj is None: raise ValueError( 'insert_rinex exception: both record and rinexobj cannot be None.' ) if rinexobj is not None: record = rinexobj.record copy_succeeded = False archived_crinex = '' # check if record exists in the database if not self.get_rinex_record(NetworkCode=record['NetworkCode'], StationCode=record['StationCode'], ObservationYear=record['ObservationYear'], ObservationDOY=record['ObservationDOY'], Interval=record['Interval'], Completion=float( '%.3f' % record['Completion'])): # no record, proceed # check if we need to perform any rinex operations. We might be inserting a new record, but it may just be # a ScanRinex op where we don't copy the file into the archive if rinexobj is not None: # is the rinex object correctly named? rinexobj.apply_file_naming_convention() # update the record to the (possible) new name record['Filename'] = rinexobj.rinex self.cnn.begin_transac() try: self.cnn.insert('rinex', record) if rinexobj is not None: # a rinexobj was passed, copy it into the archive. path2archive = os.path.join( self.Config.archive_path, self.build_rinex_path(record['NetworkCode'], record['StationCode'], record['ObservationYear'], record['ObservationDOY'], with_filename=False, rinexobj=rinexobj)) # copy fixed version into the archive (in case another session exists for RINEX v2) archived_crinex = rinexobj.compress_local_copyto( path2archive) copy_succeeded = True # get the rinex filename to update the database rnx = RinexNameFormat(archived_crinex).to_rinex_format( pyRinexName.TYPE_RINEX, no_path=True) if rnx != rinexobj.rinex: # update the table with the filename (always force with step) self.cnn.query( 'UPDATE rinex SET "Filename" = \'%s\' ' 'WHERE "NetworkCode" = \'%s\' ' 'AND "StationCode" = \'%s\' ' 'AND "ObservationYear" = %i ' 'AND "ObservationDOY" = %i ' 'AND "Interval" = %i ' 'AND "Completion" = %.3f ' 'AND "Filename" = \'%s\'' % (rnx, record['NetworkCode'], record['StationCode'], record['ObservationYear'], record['ObservationDOY'], record['Interval'], record['Completion'], record['Filename'])) event = pyEvents.Event( Description='A new RINEX was added to the archive: %s' % record['Filename'], NetworkCode=record['NetworkCode'], StationCode=record['StationCode'], Year=record['ObservationYear'], DOY=record['ObservationDOY']) else: event = pyEvents.Event( Description= 'Archived CRINEX file %s added to the database.' % record['Filename'], NetworkCode=record['NetworkCode'], StationCode=record['StationCode'], Year=record['ObservationYear'], DOY=record['ObservationDOY']) self.cnn.insert_event(event) except: self.cnn.rollback_transac() if rinexobj and copy_succeeded: # transaction rolled back due to error. If file made into the archive, delete it. os.remove(archived_crinex) raise self.cnn.commit_transac() return True else: # record already existed return False
def build_rinex_path(self, NetworkCode, StationCode, ObservationYear, ObservationDOY, with_filename=True, filename=None, rinexobj=None): """ Function to get the location in the archive of a rinex file. It has two modes of operation: 1) retrieve an existing rinex file, either specific or the rinex for processing (most complete, largest interval) or a specific rinex file (already existing in the rinex table). 2) To get the location of a potential file (probably used for injecting a new file in the archive. No this mode, filename has no effect. :param NetworkCode: NetworkCode of the station being retrieved :param StationCode: StationCode of the station being retrieved :param ObservationYear: Year of the rinex file being retrieved :param ObservationDOY: DOY of the rinex file being retrieved :param with_filename: if set, returns a path including the filename. Otherwise, just returns the path :param filename: name of a specific file to search in the rinex table :param rinexobj: a pyRinex object to pull the information from (to fill the achive keys). :return: a path with or without filename """ if not rinexobj: # not an insertion (user wants the rinex path of existing file) # build the levels struct sql_string = ", ".join( ['"' + level['rinex_col_in'] + '"' for level in self.levels] + ['"Filename"']) if filename: filename = RinexNameFormat(filename).to_rinex_format( pyRinexName.TYPE_RINEX) # if filename is set, user requesting a specific file: query rinex table rs = self.cnn.query('SELECT ' + sql_string + ' FROM rinex WHERE "NetworkCode" = \'' + NetworkCode + '\' AND "StationCode" = \'' + StationCode + '\' AND "ObservationYear" = ' + str(ObservationYear) + ' AND "ObservationDOY" = ' + str(ObservationDOY) + ' AND "Filename" = \'' + filename + '\'') else: # if filename is NOT set, user requesting a the processing file: query rinex_proc rs = self.cnn.query( 'SELECT ' + sql_string + ' FROM rinex_proc WHERE "NetworkCode" = \'' + NetworkCode + '\' AND "StationCode" = \'' + StationCode + '\' AND "ObservationYear" = ' + str(ObservationYear) + ' AND "ObservationDOY" = ' + str(ObservationDOY)) if not rs.ntuples(): return None field = rs.dictresult()[0] path = "/".join( str(field[level['rinex_col_in']]).zfill(level['TotalChars']) for level in self.levels) if with_filename: rnx_name = RinexNameFormat(field['Filename']) # database stores rinex, we want crinez return path + "/" + rnx_name.to_rinex_format( pyRinexName.TYPE_CRINEZ) else: return path else: # new file (get the path where it's supposed to go) keys = [] for level in self.levels: kk = str(rinexobj.record[level['rinex_col_in']]) if level['isnumeric'] == '1': kk = kk.zfill(level['TotalChars']) if len(kk) != level['TotalChars']: raise ValueError('Invalid record \'%s\' for key \'%s\'' % (kk, level['KeyCode'])) keys += [kk] path = '/'.join(keys) crinez_path = os.path.join(path, rinexobj.crinez) valid, _ = self.parse_archive_keys( crinez_path, tuple(item['KeyCode'] for item in self.levels)) if not valid: raise ValueError('Invalid path result: %s' % path) elif with_filename: return crinez_path else: return path