def insertOrUpdate(db: str, tableName: str, dataFile: str, conReplaceRules=None, writeToNewFormat: bool = True): """ inserta o actualiza en contenido del fichero json dataFile en tableName de la base de datos db args: ... conReplaceRules: connexión a una base de datos sqlite3 dataFile es el fichero json que se descarga de aemet. Cada registro de dataFile puede tener un contenido variable de columnas (campos), dentro de las definidas en el fichero de metadatos; no haycampos nulos Algún campo puede tener valores que no coinciden con la definición dada por aemet -por ej. el campo prec se declara como real, pero puede tener valores Ip-; en este caso se sustituye el valor incorrecto por otro definido en FILEPARAM. Estas reglas de sustitución se pasan en el arg. conReplaceRules writeToNewFormat. si True, comprueba e inserta los datos en dataFile; si False sólo comprueba que se leen bien los datos """ import json from os.path import split import sqlite3 columnsInTable = namesTypesSqliteTable(db, tableName) stm1 = f'REPLACE INTO {tableName} ' con = sqlite3.connect(db) cur = con.cursor() ierr = 0 with open(dataFile) as fi: pathName = split(dataFile) data = json.load(fi) iline = 0 for row in data: iline += 1 columns = ','.join(row.keys()) try: if conReplaceRules is None: values = valuesInRow0(row, columnsInTable) else: values = valuesInRow(row, columnsInTable, conReplaceRules) stm3 = stm1 + f'({columns}) VALUES {values}' if writeToNewFormat: cur.execute(stm3) except ValueError as err: ierr += 1 logging.append(f'File {pathName[1]}, line {iline:d}, '+\ f'error {err}') con.commit() con.close() print(f'{pathName[1]}, {ierr:d} errores en {iline:d} lineas')
def __check_fks(self): """ tests if foreign key columns values if file are in foregn keys """ fk_errors = 0 tcols = self.__table_column_get() names_table = [col1 for col1 in tcols] names_df = [col1 for col1 in self.data.columns] efks = self.table.findall('fk') for element in efks: # query column names q_cols = element.find('col').text.split(',') q_cols = [col1.strip() for col1 in q_cols] # check if q_cols are in names_df n = 0 for col1 in q_cols: if col1 in names_df: n += 1 if len(names_df) != n: continue # foreign table column names fk_cols = element.find('fk_col').text.split(',') fk_cols = [col1.strip() for col1 in fk_cols] table = element.find('fk_table').text cols_in_select = ','.join(fk_cols) cols_in_where = [f'{col1}=%s' for col1 in fk_cols] cols_in_where = ','.join(cols_in_where) s1 = f'select {cols_in_select} from {table} where {cols_in_where}' if len(q_cols) == 1: values = set(df1['a'].values) else: values = [] for i in range(len(self.data.index)): r = tuple([self.data[col1].values[i] for col1 in q_cols]) values.append(r) values = tuple(set(values)) not_found = 0 for row in values: self.cur.execute(s1, row) r = self.cur.fetchone() if not r: not_found += 1 logging.append(f'{row} not in {table}', False) tname = self.table.get('name') print(f'tabla {tname}: {not_found:n}/{len(values):n} not found') if not_found > 0: fk_errors + = 1 if fk_errors > 0: raise ValueError(f'Errores de foreign keys in {fk_errors} ' \ 'relaciones')
def _fill_with_median(self, fidi, date_nodata, con, cur, insert): """ fill not interpolated values using idw in dates date_nodata using medians """ select1 = \ """select value from {IDW.table_name_interpolated_values} where fid=? order by value""" select2 = \ """select value from {IDW.table_name_interpolated_values} order by value """ if len(date_nodata) == 0: return [] med = np.empty((fidi.size, 12), np.float32) med[:] = np.NAN missed = [] for date1 in date_nodata: imonth = int(date1[5:7]) - 1 for i, fidi1 in enumerate(fidi): if np.isnan(med[i, imonth]): IDW._median_set(cur, select1, imonth, i, med, fidi1) if np.isnan(med[i, imonth]): IDW._median_set(cur, select2, imonth, i, med) if np.isnan(med[i, imonth]): logging.append(f'{date1} no median available ' +\ f'for point {fidi1}', False) if not np.isnan(med[i, imonth]): cur.execute(insert, (fidi1, date1, float(med[i, imonth]))) a = f'point {fidi1}, fecha {date1} interpolated with ' +\ 'median' logging.append(f'{a}', False) else: missed.append(f'point {fidi1}, fecha {date1}') con.commit() return missed
def taskGet(path: str, nameTask: str, estaciones: list = None, dateRange: list = None): """ comprueba si existe una tarea previa y devuelve la tarea adecuada para la ejecución; tanto si se quiere ejecutar una tarea ya finalizada como si se quiere ejecutar una tarea donde ya hay otra diferente devuelve None """ from datetime import date from os.path import isfile from aemetOpenDataTask import Task from aemetOD_constants import MSGCONTINUE if estaciones == None: estaciones = [ 'todas', ] if dateRange is None: D1 = date(1900, 1, 1) D2 = date(1900, 1, 2) dateRange = [D1, D2] task = Task(path, dateRange, estaciones, downloadType='estaciones', name=nameTask) if isfile(Task.nameFileTaskGet(path)): previousTask = Task.readFile(path) if previousTask.hasFinished(): logging.append(f'El directorio {path} contiene una tarea '+\ 'ya finalizada.\nInténtalo en un nuevo directorio') _ = input(MSGCONTINUE) return None if task != previousTask: logging.append('Estás tratando de utilizar el directorio' + 'de otra tarea') _ = input(MSGCONTINUE) return None return previousTask return task
def z_get(self, xy): """ returns the z value given a list of x, y coordinates Parameters ---------- xy : List([int-str, float, float) list of coordinates as [gid1, x1, y1], [gid2, x2, y2]... Returns z : List(List)), each element: gid, x1, y1, z1 """ z = [] tiny = 0.1 for ii, xy1 in enumerate(xy): gid = xy1[0] x1 = xy1[1] y1 = xy1[2] if not self.point_in_grid(x1, y1): msg = f'{gid} {x1} {y1} NO está en {self.filename}' logging.append(msg, False) continue if x1 >= self.xmax: x1 = self.xmax - tiny if y1 >= self.ymax: y1 = self.ymax - tiny if x1 <= self.xmin: x1 = self.xmin if y1 <= self.ymin: y1 = self.ymin xi = ((y1 - self.ymin) * self.keys['nrows']) / (self.ymax - self.ymin) xj = ((x1 - self.xmin) * self.keys['ncols']) / (self.xmax - self.xmin) i = self.keys['nrows'] - int(xi) - 1 j = int(xj) z.append([gid, self.Z[i, j]]) return z
def to_str(col: str, file: str, line: int, col_name: str, exception: bool, cols_with_null_values: list, required: bool = True): if col.lower() == 'null': if col_name not in cols_with_null_values.keys(): cols_with_null_values[col_name] = 1 else: cols_with_null_values[col_name] += 1 msg = f'file {file}, line {i:n} {col_name} is null' logging.append(msg, toScreen=False) if required and exception: raise ValueError(msg) return '' else: return col
def get_z_drv(filepoints, dtypes, dir_asc_files, filenames, output_file): """ driver to assign its z value to a set of points in filepoints Parameters ---------- filepoints : str csv file with the following structure "gid","x","y" gid1 (int or str), x1 (float), y1 (float) ... gidn, xn, yn dtypes : Dictionary with 3 elements, {'gid': type, 'x': type, 'y': type}, examples: dtypes = {'gid': 'int32', 'x': 'float32', 'y': 'float32'} dtypes = {'gid': 'str', 'x': 'float32', 'y': 'float32'} dtypes = {'gid': 'int32', 'x': 'float64', 'y': 'float64'} dir_asc_files : str directory where filenames are found filenames : List(str) list in wich each element is the name of a standar asc file output_file : Path to a csv fil with the results of computations Returns ------- dfp : pandas dataframe dateframe with data and z values """ dfp = pd.read_csv(filepoints, dtype=dtypes) dfp['z'] = np.nan dfp['file'] = '' xys = [[row.gid, row.x, row.y] for _, row in dfp.iterrows()] for fn in filenames: grd = MDT_asc(dir_asc_files, fn) zs = grd.z_get(xys) for z1 in zs: dfp.loc[dfp['gid'] == z1[0], 'z'] = z1[-1] dfp.loc[dfp['gid'] == z1[0], 'file'] = fn dfp2 = dfp.loc[dfp.z.isnull()] xys = [[row.gid, row.x, row.y] for _, row in dfp2.iterrows()] if len(dfp2.index) > 0: msg = f'there are {len(dfp2.index)} points without assigned z' logging.append(msg) msg = dfp2.to_string() print(msg) else: msg = 'All points have been assigned' logging.append(msg) name = basename(output_file) path = dirname(filepoints) dfp.to_csv(join(path, name), sep=',', encoding='utf-8', index=False) msg = f'output file {join(path, name)}' logging.append(msg) return dfp
# points = ((688892-4, 4208042-4),) if __name__ == "__main__": try: startTime = time() grd = mdt.MDT_asc(dir_input, filenames[0]) z = grd.z_get(points) for z1 in z: print(z1) # grd.asc2csv() #axy.asc2xy(filename, fileout) except ValueError: msg = traceback.format_exc() logging.append(msg) except ImportError: msg = traceback.format_exc() logging.append(msg) except Exception: msg = traceback.format_exc() logging.append(msg) finally: logging.dump() xtime = time() - startTime print(f'El script tardó {xtime:0.1f} s')
def insert(csvfiles: list, csvpath: str, db: str, sep: str = ';', update_puntos: bool = True, exception: bool = True, insert_update: bool = True) -> None: """ Parameters ---------- csvfiles : list lista de ficheros csv o txt csvpath : str directorio de los ficheros csvfile db : str nombre y dirección de la db sqlite; si no existe la crea sep : text separador de columnas en los ficheros txt update_puntos : bool si True actualiza las filas de las tablas exception: si True lanza un raise si encuentra una tabla con un campo requerido con contenido null insert_update: si True realiza un insert/update de las tablas a partir de csvfiles Returns None """ select_puntos = """ select * from puntos where fid = ? """ insert_puntos = """ insert into puntos(fid, xetrs89, yetrs89, id_mas, tm, prov, id_uh, acu, prof) values (?, ?, ?, ?, ?, ?, ?, ?, ?) """ select_masub = """ select * from masub where fid = ? """ insert_masub = """ insert into masub(fid, name) values (?, ?) """ select_param = """ select * from param where fid = ? """ insert_param = """ insert into param(fid, name) values (?, ?) """ select_uh = """ select * from uh where fid = ? """ insert_uh = """ insert into uh(fid, name) values (?, ?) """ select_analisis = """ select * from analisis where fid = ? and fecha = ? and param = ? """ insert_analisis = """ insert into analisis(fid, fecha, param, valor, uds) values (?, ?, ?, ?, ?) """ def to_float(col: str, file: str, line: int, col_name: str, exception: bool, cols_with_null_values: list, required: bool = True): if col.lower() == 'null': if col_name not in cols_with_null_values.keys(): cols_with_null_values[col_name] = 1 else: cols_with_null_values[col_name] += 1 msg = f'file {file}, line {i:n} {col_name} is null' logging.append(msg, toScreen=False) if required and exception: raise ValueError(msg) return '' else: return float(col.replace(',', '.')) def to_str(col: str, file: str, line: int, col_name: str, exception: bool, cols_with_null_values: list, required: bool = True): if col.lower() == 'null': if col_name not in cols_with_null_values.keys(): cols_with_null_values[col_name] = 1 else: cols_with_null_values[col_name] += 1 msg = f'file {file}, line {i:n} {col_name} is null' logging.append(msg, toScreen=False) if required and exception: raise ValueError(msg) return '' else: return col cols_with_null_values = {} try: connected = False con = sqlite3.connect(db) connected = True cur = con.cursor() for file in csvfiles: print(file) with open(join(csvpath, file), 'r', encoding='utf-8') as fi: csv_reader = csv.reader(fi, delimiter=sep) for i, row in enumerate(csv_reader): if i == 0: continue col = row for j in range(len(col)): if j == 0: col[j] = to_str(col[j], file, i, 'fid analisis', exception, cols_with_null_values) elif j == 1: col[j] = to_str(col[j], file, i, 'fecha', exception, cols_with_null_values) elif j == 2: col[j] = to_str(col[j], file, i, 'fid param', exception, cols_with_null_values) elif j == 3: col[j] = to_str(col[j], file, i, 'param', exception, cols_with_null_values) elif j == 4: col[j] = to_str(col[j], file, i, 'uds', exception, cols_with_null_values, False) elif j == 5: col[j] = to_float(col[j], file, i, 'valor', exception, cols_with_null_values) elif j == 6: col[j] = to_float(col[j], file, i, 'xetrs89', exception, cols_with_null_values) elif j == 7: col[j] = to_float(col[j], file, i, 'yetrs89', exception, cols_with_null_values) elif j == 8: col[j] = to_str(col[j], file, i, 'id_mas', exception, cols_with_null_values) elif j == 9: col[j] = to_str(col[j], file, i, 'mas name', exception, cols_with_null_values, False) elif j == 10: col[j] = to_str(col[j], file, i, 'tm', exception, cols_with_null_values, False) elif j == 11: col[j] = to_str(col[j], file, i, 'prov', exception, cols_with_null_values, False) elif j == 12: col[j] = to_str(col[j], file, i, 'id_uh', exception, cols_with_null_values) elif j == 13: col[j] = to_str(col[j], file, i, 'uh name', exception, cols_with_null_values, False) elif j == 14: col[j] = to_str(col[j], file, i, 'acu', exception, cols_with_null_values, False) elif j == 15: col[j] = to_float(col[j], file, i, 'prof', exception, cols_with_null_values, False) if not insert_update: continue cur.execute(select_puntos, (col[0], )) if cur.fetchone() is None: cur.execute(insert_puntos, (col[0], col[6], col[7], col[8], col[10], col[11], col[12], col[14], col[15])) cur.execute(select_masub, (col[8], )) if cur.fetchone() is None: cur.execute(insert_masub, (col[8], col[9])) cur.execute(select_param, (col[2], )) if cur.fetchone() is None: cur.execute(insert_param, (col[2], col[3])) cur.execute(select_uh, (col[12], )) if cur.fetchone() is None: cur.execute(insert_uh, (col[12], col[13])) cur.execute(select_analisis, (col[0], col[1], col[2])) if cur.fetchone() is None: cur.execute(insert_analisis, (col[0], col[1], col[2], col[5], col[4])) print('\ncols with null values') print('column, null values number') for key, value in cols_with_null_values.items(): print(f'{key}, {value}') except Error: raise ValueError(Error) except ValueError: msg = traceback.format_exc() logging.append(f'ValueError exception\n{msg}') finally: if connected: con.commit() con.close()
def meteoroMesGet(path: str, year1: int, year2: int, estaciones: list, name: str): """ devuelve los datos meteorológicos mensuales de las estaciones en un rango de años. En cada llamada aemet solo permite descargar una estación por año Se hace una primera petición get en la función request_to_aemet con el argumento meteoro=False; esta función devuelve un fichero json con 4 parámetros (estado, descripcion, datos y metadatos). Estado es un estándar de respuestas del servidor; descripción es la idem de estado; datos es la url para descargar los datos solicitados; y metadatos es la url con los metadatos. Si la respuesta tieneéxito se hace una segunda petición con request_to_aemet con el argumento meteoro=True y utilizando las url devueltas en la primera petición; si tiene éxito se graban los datos Esta función tiene la misma estructura que meteoroDiaGet; si haces algún cambio aquí quizás lo tengas que trasladar a meteoroDiaGet args: path. carpeta donde se grabarán los resultados year1, year2. Años inicial y final de la petición de datos mensuales estaciones. lista de strings con los códigos de estaciones cuyos datos se desean descargar name. nombre de la tarea """ from os.path import isfile, join from aemetOpenDataTaskMonth import TaskMonth from aemetOD_constants import MSGCONTINUE, URLBASE, meteoro_mes_url, \ FMETADATA_METEORO_MES, STOP_REQUESTS, ANOTHER_REQUEST, SUCCESS_REQUEST, \ MAXREQUEST try: task = TaskMonth.getTask(path, [year1, year2], estaciones, name, 'meteoros mensuales') except ValueError as err: logging.append(err) _ = input(MSGCONTINUE) return NYEARSTEP = 1 # aemet no admite otro valor url_template = URLBASE + meteoro_mes_url file_metadatos = join(path, FMETADATA_METEORO_MES) wstations = [ estacion for estacion in estaciones if estacion not in task.downloadedStations ] nestaciones = len(wstations) if nestaciones == 0: logging.append('Las estaciones ya se han descargado previamente') return start_time = time() for i, estacion in enumerate(wstations): if estacion in task.downloadedStations: continue year = year1 nerrors, ndatos, ntry = 0, 0, 0 ne = i + 1 print(f'{estacion} {ne:d}/{nestaciones:d}') while year <= year2: nameFile = f'{estacion}_{year:d}_m.json' if isfile(join(path, nameFile)): year = year + NYEARSTEP continue url1 = url_template.format(f'{year:d}', f'{year:d}', estacion) code, r = request_to_aemet(url1, f'{estacion} {year:d}', False) if code == STOP_REQUESTS: nerrors += 1 break elif code == ANOTHER_REQUEST: year = year + NYEARSTEP continue try: url2 = r.json()['datos'] except ValueError as er: ntry += 1 if ntry <= MAXREQUEST: continue else: logging.append(f'{er}', toScreen=False) year = year + NYEARSTEP continue code, r2 = request_to_aemet(url2, f'{estacion} {year:d}', True) if code == STOP_REQUESTS: nerrors += 1 break elif code == ANOTHER_REQUEST: year = year + NYEARSTEP continue dst = join(path, nameFile) try: with open(dst, 'wb') as f: for chunk in r2.iter_content(chunk_size=128): f.write(chunk) ndatos += 1 except Exception as er: logging.append(f'{er}', toScreen=False) nerrors += 1 year = year + NYEARSTEP continue if not isfile(file_metadatos): url2 = r.json()['metadatos'] code, r3 = request_to_aemet(url2, 'metadatos mensuales', True) if code == SUCCESS_REQUEST: try: with open(file_metadatos, 'wb') as f: for chunk in r3.iter_content(chunk_size=128): f.write(chunk) except Exception as er: logging.append(f'{er}', toScreen=False) year = year + NYEARSTEP ellapsed_time(start_time, nestaciones, ne, ndatos) logging.dump() if nerrors == 0: task.appendDownloadedStation(estacion) task.write() task.statusGet() logging.append('Descarga meteoros mensuales finalizada') _ = input(MSGCONTINUE)
def upsert(self, update: bool = True, check: bool = True) -> None: """ args: update: si ==True hace un update cuando la fila ya existe check: si True chekea los datos antes de insert-update inserta fila a fila si existe ya la fila con la clave primaria que se desea insertar actualiza sus contenidos en el caso de que update == True, en caso contrario no hace nada la operación se hace fila a fila y se contabiliza el número de insertados y el de actualizados, por lo que no se utiliza la sentencia upsert de postgres """ s0 = "select fid from met.interpolation_points where fid = %s" s1 = \ """ select fid from met.interpolated_tseries where fid=%s and variable=%s and fecha=%s""" insert = \ """ insert into met.interpolated_tseries (fid, variable, fecha, value, metodo) values (%s, %s, %s, %s, %s); """ update = \ """ update met.interpolated_tseries set value=%s, metodo=%s where fid=%s and variable=%s and fecha=%s """ if check and not self.checked_data_in_tables: ok = self.check_data_in_tables() if not ok: return cur = self.con.cursor() nrows = self.data.shape[0] - 1 not_found = inserted = updated = 0 for index, row in self.data.iterrows(): print(f'{index}/{nrows}') cur.execute(s0, (row[self.fid], )) row1 = cur.fetchone() if row1 is None: logging.append(f"{index} {row[self.fid]} not found") not_found += 1 continue cur.execute(s1, (row[self.fid], row[self.variable], row[self.fecha])) row1 = cur.fetchone() msg_key_columns = f"{index:n} {row[self.fid]} " \ f"{row[self.variable]} {row[self.value]}" if row1 is None: try: cur.execute( insert, (row[self.fid], row[self.variable], row[self.fecha], row[self.value], row[self.metodo])) logging.append(f"{msg_key_columns} inserted", False) inserted += 1 except: msg = traceback.format_exc() logging.append(f"{msg_key_columns} error inserting " \ f'\n{msg}') return else: if not update: logging.append(f"{msg_key_columns} not updated", False) continue try: cur.execute( update, (row[self.value], row[self.metodo], row[self.fid], row[self.variable], row[self.fecha])) logging.append(f"{msg_key_columns} updated", False) updated += 1 except: msg = traceback.format_exc() logging.append(f"{msg_key_columns} error updating " \ f'\n{msg}') return self.con.commit() print(f'cod not found: {not_found:n}') print(f'inserted: {inserted:n}') print(f'updated: {updated}')
def upsert(self, update: bool = True, check: bool = True) -> None: """ args: update: si ==True hace un update cuando la fila ya existe check: si True chekea los datos antes de insert-update inserta fila a fila si existe ya la fila con la clave primaria que se desea insertar actualiza sus contenidos en el caso de que update == True, en caso contrario no hace nada la operación se hace fila a fila y se contabiliza el número de insertados y el de actualizados, por lo que no se utiliza la sentencia upsert de postgres """ s0 = "select indic from met.pexistencias where indic = %s" s1 = 'select indic from met.pmes where indic=%s and fecha=%s' insert = \ """ insert into met.pmes (indic, fecha, prec) values (%s, %s, %s); """ update = \ """ update met.pmes set prec=%s where indic=%s and fecha=%s """ if check and not self.checked_data_in_tables: ok = self.check_data_in_tables() if not ok: return cur = self.con.cursor() nrows = self.data.shape[0] - 1 not_found = inserted = updated = 0 for index, row in self.data.iterrows(): print(f'{index}/{nrows}') cur.execute(s0, (row[self.indic], )) row1 = cur.fetchone() if row1 is None: logging.append(f"{index} {row[self.indic]} not found") not_found += 1 continue fecha1 = self.last_date_of_the_month(row[self.year], row[self.month]) cur.execute(s1, (row[self.indic], fecha1)) row1 = cur.fetchone() if row1 is None: try: cur.execute(insert, (row[self.indic], fecha1, row[self.prec])) logging.append(f"{index:n} {row[self.indic]} " \ f"{fecha1} inserted", False) inserted += 1 except: msg = traceback.format_exc() logging.append(f"{index:n} {row[self.indic]} " \ f"{fecha1} error " +\ f'inserting\n{msg}') return else: if not update: logging.append(f"{index:n} {row[self.indic]} " \ f"{fecha1} not updated", False) continue try: cur.execute(update, (row[self.prec], row[self.indic], fecha1)) logging.append(f"{index:n} {row[self.indic]} " \ f"{fecha1} updated", False) updated += 1 except: msg = traceback.format_exc() logging.append(f"{index:n} {row[self.indic]} " \ f"{fecha1} error " +\ f'updating\n{msg}') return self.con.commit() print(f'cod not found: {not_found:n}') print(f'inserted: {inserted:n}') print(f'updated: {updated}')
def request_to_aemet(url: str, identifier: str, meteoro: bool): """ Hace una petición al servidor de aemet open data args url. url identifier. texto con los args de petición, se utiliza sólo como un texto aclaratorio para los loggings meteoro. Si False es la llamada al servirdor donde responde con los descriptores de aemet 'estado','descripcion','datos' y 'metadatos' Si True devuelve una lista en que cada elemento es un diccionario de datos returns 2 objetos un integer de valor STOP_REQUESTS, ANOTHER_REQUEST, SUCCESS_REQUEST un objeto request o None El integer que devuelve depende de lo que quiero hacer con la respuesta La respuesta de request la examina de 2 maneras Primero compruebo las excepciones Si no hay excepciones y meteoro==True devuelve SUCCESS_REQUEST y la respuesta Si no hay excepciones y meteoro==False, entonces examina el fichero json que devuelve aemet con 4 valores - estado: request.status_code - descripcion: de estado - datos: una url con los de datos meteorológicos solicitados en la petición - metadatos: una url con los metatadatos de datos En este caso (no hay excepciones y meteoro==False) puede ocurrir que no tenga excepcion HTTPError y la clave "estado" no devuelve RESPONSEOK. Debe haber una coherencia entre las acciones que programas ante la misma respuesta del servidor cuando analizas la excepción y cuando analizas la clave "estado" SI MODIFICAS LA ACCIÓN A REALIZAR hazlo en los 2 sitios. """ import requests from requests.exceptions import HTTPError from time import sleep from aemetOD_constants import headers, querystring, REQUEST_TIMEOUT, \ UNAUTHORIZED, NOTFOUND, TOOMANYREQUESTS, MAXREQUEST, RESPONSEOK, \ SLEEPSECONDS, STOP_REQUESTS, ANOTHER_REQUEST, SUCCESS_REQUEST nrequest = 0 while True: try: r = requests.get(url, headers=headers, params=querystring, timeout=REQUEST_TIMEOUT) r.raise_for_status() except HTTPError as er: logging.append(f'{er}', toScreen=False) if er.response.status_code == UNAUTHORIZED: return STOP_REQUESTS, None elif er.response.status_code == NOTFOUND: return ANOTHER_REQUEST, None elif er.response.status_code == TOOMANYREQUESTS: nrequest += 1 if nrequest <= MAXREQUEST: sleep(SLEEPSECONDS) continue else: return ANOTHER_REQUEST, None else: nrequest += 1 if nrequest <= MAXREQUEST: sleep(SLEEPSECONDS) continue else: return STOP_REQUESTS, None except Exception as er: logging.append(f'{er}', toScreen=False) return STOP_REQUESTS, None r.encoding = r.apparent_encoding try: rjson = r.json() except ValueError as er: nrequest += 1 if nrequest <= MAXREQUEST: continue else: logging.append(f'{er}', toScreen=False) return ANOTHER_REQUEST, None if meteoro: return SUCCESS_REQUEST, r if rjson['estado'] == RESPONSEOK: return SUCCESS_REQUEST, r logging.append(f'{identifier}, estado {rjson["descripcion"]}', toScreen=False) if rjson['estado'] == UNAUTHORIZED: return STOP_REQUESTS, None elif rjson['estado'] == NOTFOUND: return ANOTHER_REQUEST, None elif rjson['estado'] == TOOMANYREQUESTS: nrequest += 1 if nrequest <= MAXREQUEST: sleep(SLEEPSECONDS) continue else: return ANOTHER_REQUEST, None else: nrequest += 1 if nrequest <= MAXREQUEST: sleep(SLEEPSECONDS) continue else: return STOP_REQUESTS, None
def upsert(self, update: bool=True, check: bool=True) ->None: """ args: update: si ==True hace un update cuando la fila ya existe check: si True chekea los datos antes de insert-update inserta fila a fila si existe ya la fila con la clave primaria que se desea insertar actualiza sus contenidos en el caso de que update == True, en caso contrario no hace nada la operación se hace fila a fila y se contabiliza el número de insertados y el de actualizados, por lo que no se utiliza la sentencia upsert de postgres """ s0 = "select cod from ipas.ipa1 where cod = %s" s1 = 'select cod from ipas.ipa2_h where cod=%s and fecha=%s' insert = \ """ insert into ipas.ipa2_h (cod, fecha, situacion, h, proyecto, medidor) values (%s, %s, %s, %s, %s, %s); """ update = \ """ update ipas.ipa2_h set situacion=%s, h=%s, proyecto=%s, medidor=%s where cod=%s and fecha=%s """ if check and not self.checked_data_in_tables: ok = self.check_data_in_tables() if not ok: return cur = self.con.cursor() nrows = self.data.shape[0] - 1 not_found = inserted = updated = 0 for index, row in self.data.iterrows(): print(f'{index}/{nrows}') cur.execute(s0, (row[self.cod],)) row1 = cur.fetchone() if row1 is None: logging.append(f"{index} {row[self.cod]} not found", False) not_found += 1 continue cur.execute(s1, (row[self.cod], row[self.fecha])) row1 = cur.fetchone() if row1 is None: try: cur.execute(insert, (row[self.cod], row[self.fecha], row[self.situacion], row[self.h], row[self.proyecto], row[self.medidor])) logging.append(f"{index:n} {row[self.cod]} " \ f"{row[self.fecha]} inserted", False) inserted += 1 except: msg = traceback.format_exc() logging.append(f"{index:n} {row[self.cod]} " \ f"{row[self.fecha]} error " +\ f'inserting\n{msg}') return else: if not update: logging.append(f"{index:n} {row[self.cod]} " \ f"{row[self.fecha]} not updated", False) continue try: cur.execute(update, (row[self.situacion], row[self.h], row[self.proyecto], row[self.medidor], row[self.cod], row[self.fecha])) logging.append(f"{index:n} {row[self.cod]} " \ f"{row[self.fecha]} updated", False) updated += 1 except: msg = traceback.format_exc() logging.append(f"{index:n} {row[self.cod]} " \ f"{row[self.fecha]} error " +\ f'updating\n{msg}') return self.con.commit() print(f'cod not found: {not_found:n}') print(f'inserted: {inserted:n}') print(f'updated: {updated}')
def idw_ts(self, xygraph: bool): """ Interpolación de una serie temporal en una serie de puntos sin datos xygraph: True si se graba un gráficos con cada punto interpolado """ import sqlite3 from os.path import join from time import time from scipy import spatial from db_connection import con_get import idw create_table1 = \ f""" create table if not exists {IDW.table_name_interpolated_values} ( fid TEXT, fecha TEXT, value REAL, PRIMARY KEY (fid, fecha))""" insert1 = \ f""" insert into {IDW.table_name_interpolated_values} (fid, fecha, value) values(?, ?, ?)""" start_time = time() # datos donde hay que interpolar, devuelve numpy array of objects rows = IDW.__read_file_points(join(self.pathin, self.fpoints), self.skip_lines) # array con los id de los puntos donde se quiere interpolar fidi = rows[:, 0] # array con las coord. de los puntos xi = np.array(rows[:, [1, 2]], np.float32) # array para los valores interpolados en xi en cada fecha zi = np.empty((len(xi)), np.float32) # cursor a los datos para hacer las interpolaciones con = con_get(self.dbtype, self.db) cur = con.cursor() # cursor para los valores interpolados con1 = sqlite3.connect(':memory:') cur1 = con1.cursor() cur1.execute(create_table1) t0 = IDW.PRINT_SECS if self.tstep_type == 2: datei = IDW.__month_lastday(self.datei) else: datei = self.datei # los puntos con datos cambian de una fecha a otra por lo que hay que # hacer una select para cada fecha; esto hace que el proceso sea # sea largo para series temporales largas date_nodata = [] while datei <= self.datefin: date_str = datei.strftime(self.date_format) if time() - t0 > IDW.PRINT_SECS: t0 = time() print(date_str) # datos en la fecha datei cur.execute(self.select, (datei, )) data = np.array([row for row in cur], np.float32) # puede haber fechas sin datos if data.size == 0: date_nodata.append(date_str) logging.append(f'{date_str} no data', False) datei = self.__next_date(datei) continue # builds kdtree tree and set distances xydata = data[:, [0, 1]] tree = spatial.cKDTree(xydata) dist, ii = tree.query(xi, k=min(self.kidw, xydata.shape[0])) # sort data by distance values0 = data[:, 2] values = np.empty((dist.shape), np.float32) idw.sortn(values0, ii, values) # idw interpolation idw.idwn(self.poweridw, dist, values, self.epsidw, zi) # insert interpolated values in sqlite for i in range(len(fidi)): cur1.execute(insert1, (fidi[i], date_str, float(zi[i]))) datei = self.__next_date(datei) con.close() elapsed_time = time() - start_time print(f'La interpolación idw tardó {elapsed_time:0.1f} s') con1.commit() # las fechas sin datos se tratan de interpolar con medianas missed = self._fill_with_median(fidi, date_nodata, con1, cur1, insert1) # graba interpolación en ficheros de texto self.__write_interpolated_values(cur1) # graba fichero con los metadatos de la interpolación self.__write_idw_metadata(fidi.size, elapsed_time, missed) # graba el fichero de puntos donde se realizó la interpolación self.__write_interpolated_points(fidi, xi) # graba los gráficos if xygraph: self.__xy(cur1)
now = datetime.now() startTime = time() b = BHIMES(project, et_proc) b.aquifer_upsert_from_file() # controlled in xml b.outcrop_upsert_from_file() b.met_upsert_from_file01() b.swb01() if annual_graphs: b.save_annual_graphs() # xy recharge, runoff & ret b.save_annual_data_graphs() # xy p, tmax, tmin, tavg b.save_annual_eth_graphs() # xy pet (hargreaves) xtime = time() - startTime print(f'El script tardó {xtime:0.1f} s') except ValueError: msg = traceback.format_exc() logging.append(f'ValueError exception\n{msg}') except ImportError: msg = traceback.format_exc() logging.append(f'ImportError exception\n{msg}') except Exception: msg = traceback.format_exc() logging.append(f'Exception\n{msg}') finally: logging.dump() print('\nFin')
def meteoroDiaGet(path: str, fecha1: date, fecha2: date, estaciones: list, name: str): """ devuelve los datos meteorológicos diarios de las estaciones en un rango de fechas. En cada llamada aemet solo permite descargar hasta 5 años por estación Esta función tiene la misma estructura que meteoroMesGet; si haces algún cambio aquí quizás lo tengas que trasladar a meteoroMesGet args: path. carpeta donde se grabarán los resultados fecha1, fecha2. Fechas inicial y final de la descarga estaciones. lista de strings con los códigos de estaciones cuyos datos se desean descargar name. nombre de la tarea """ from dateutil.relativedelta import relativedelta from os.path import isfile, join from aemetOpenDataTask import Task from aemetOD_constants import MSGCONTINUE, URLBASE, meteoro_dia_url, \ FMETADATA_METEORO_DIA, STOP_REQUESTS, ANOTHER_REQUEST, SUCCESS_REQUEST, \ MAXREQUEST try: task = Task.getTask(path, [fecha1, fecha2], estaciones, name, 'meteoros diarios') except ValueError as err: logging.append(err) _ = input(MSGCONTINUE) return DAYSTEP = relativedelta(days=1) NYEARSTEP = 4 # aemet permite hasta 5 años por petición url_template = URLBASE + meteoro_dia_url file_metadatos = join(path, FMETADATA_METEORO_DIA) wstations = [ estacion for estacion in estaciones if estacion not in task.downloadedStations ] nestaciones = len(wstations) if nestaciones == 0: logging.append('Las estaciones ya se han descargado previamente') return start_time = time() for i, estacion in enumerate(wstations): if estacion in task.downloadedStations: continue fecha = fecha1 nerrors, ndatos, ntry = 0, 0, 0 ne = i + 1 print(f'{estacion} {ne:d}/{nestaciones:d}') while fecha <= fecha2: fecha1_request = fecha.strftime("%Y-%m-%d") fecha_last = fecha + relativedelta(years=NYEARSTEP) fecha2_request = fecha_last.strftime("%Y-%m-%d") nameFile = f'{estacion}_{fecha1_request}_{fecha2_request}_d.json' if isfile(join(path, nameFile)): fecha = fecha_last + DAYSTEP continue url1 = url_template.format(f'{fecha1_request}T00:00:00UTC', f'{fecha2_request}T00:00:00UTC', f'{estacion}') code, r = request_to_aemet(url1, f'{estacion} {fecha1_request} ' +\ f'{fecha2_request}', False) if code == STOP_REQUESTS: nerrors += 1 break elif code == ANOTHER_REQUEST: fecha = fecha_last + DAYSTEP continue try: url2 = r.json()['datos'] except ValueError as er: ntry += 1 if ntry <= MAXREQUEST: continue else: logging.append(f'{er}', toScreen=False) fecha = fecha_last + DAYSTEP continue code, r2 = request_to_aemet(url2, f'{estacion} {fecha1_request}' +\ f'{fecha2_request}', True) if code == STOP_REQUESTS: nerrors += 1 break elif code == ANOTHER_REQUEST: fecha = fecha_last + DAYSTEP continue dst = join(path, nameFile) try: with open(dst, 'wb') as f: for chunk in r2.iter_content(chunk_size=128): f.write(chunk) ndatos += 1 except Exception as er: logging.append(f'{er}', toScreen=False) nerrors += 1 fecha = fecha_last + DAYSTEP continue if not isfile(file_metadatos): url2 = r.json()['metadatos'] code, r3 = request_to_aemet(url2, 'metadatos diarios', True) if code == SUCCESS_REQUEST: try: with open(file_metadatos, 'wb') as f: for chunk in r3.iter_content(chunk_size=128): f.write(chunk) except Exception as er: logging.append(f'{er}', toScreen=False) fecha = fecha_last + DAYSTEP ellapsed_time(start_time, nestaciones, ne, ndatos) logging.dump() if nerrors == 0: task.appendDownloadedStation(estacion) task.write() task.statusGet() logging.append('Descarga meteoros diarios finalizada') _ = input(MSGCONTINUE)