apb_cx_oracle_spatial.gestor_oracle
1# coding=utf-8 2# # 3# Author: Ernesto Arredondo Martinez (ernestone@gmail.com) 4# File: gestor_oracle.py 5# Created: 05/04/2020, 00:38 6# Last modified: 10/11/2019, 11:24 7# Copyright (c) 2020 8 9import csv 10import datetime 11from functools import wraps 12import inspect 13import itertools 14import json 15import os 16import shutil 17import sys 18from collections import namedtuple, OrderedDict 19from subprocess import Popen, PIPE 20from tempfile import SpooledTemporaryFile 21from zipfile import ZipFile, ZIP_DEFLATED 22 23import cx_Oracle 24import lxml.etree as etree 25 26from apb_extra_utils import utils_logging 27from apb_extra_utils.utils_logging import logger_path_logs 28from apb_spatial_utils import topojson_utils 29from . import sdo_geom as m_sdo_geom 30from apb_extra_utils.sql_parser import x_sql_parser 31 32# Nombres tipo geometria GTYPE oracle por orden valor GTYPE 33GTYPES_ORA = ["DEFAULT", 34 "POINT", 35 "LINE", 36 "POLYGON", 37 "COLLECTION", 38 "MULTIPOINT", 39 "MULTILINE", 40 "MULTIPOLYGON"] 41 42# Se inicializan tipos de geometria Oracle 43__class_tips_geom_ora = {} 44for nom_tip in GTYPES_ORA: 45 __class_tips_geom_ora[nom_tip] = namedtuple("gtype_" + nom_tip.upper(), 46 ['TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID']) 47 48 49def class_tip_geom(gtype_ora="DEFAULT"): 50 """ 51 Retorna NAMEDTUPLE 'cursor_desc_tip_geom_GTYPE' con columnas 52 'TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID' 53 54 Args: 55 gtype_ora: tipo geometrias como claves en __class_tips_geom_ora 56 57 Returns: 58 namedtuple('TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID') 59 60 """ 61 gtype_ora = gtype_ora.upper() 62 63 if gtype_ora not in __class_tips_geom_ora: 64 gtype_ora = "DEFAULT" 65 66 return __class_tips_geom_ora.get(gtype_ora) 67 68 69# Caches para atributos de tablas_o_vistas de Oracle 70__cache_pks_tab = {} 71__cache_row_desc_tab = {} 72__cache_tips_geom_tab = {} 73__cache_row_class_tab = {} 74 75 76def del_cache_rel_con_db(con_db_name: str): 77 """ 78 Borra las Caches para atributos de tablas_o_vistas de Oracle 79 Args: 80 con_db_name: nom de la connexió 81 82 Returns: 83 84 """ 85 all_cache = [__cache_pks_tab, __cache_row_desc_tab, __cache_tips_geom_tab, __cache_row_class_tab] 86 for cache_dic in all_cache: 87 # alamcenamos las claves porque no se puede eliminar del diccionario mientras se itera 88 keys_remove = [] 89 for key in cache_dic: 90 if key.startswith(con_db_name): 91 keys_remove.append(key) 92 for key_r in keys_remove: 93 del cache_dic[key_r] 94 95 96def get_oracle_connection(user_ora, psw_ora, dsn_ora=None, call_timeout=None, schema_ora=None): 97 """ 98 Return cx_Oracle Connection 99 Args: 100 user_ora (str): 101 psw_ora (str): 102 dsn_ora (str=None): 103 call_timeout (int=None): miliseconds 104 schema_ora(str=None): indicate scheme when it is different from the user, default schema = user 105 106 Returns: 107 cx_Oracle.Connection 108 """ 109 connection = cx_Oracle.Connection(user_ora, psw_ora, dsn_ora, encoding="UTF-8", nencoding="UTF-8") 110 if call_timeout: 111 connection.call_timeout = call_timeout 112 113 if schema_ora: 114 connection.current_schema = schema_ora 115 116 return connection 117 118 119def get_nom_conexion(con_db): 120 """ 121 Devuelve el nombre de la conexion a Oracle 122 123 Args: 124 con_db: 125 126 Returns: 127 128 """ 129 return "@".join((con_db.username.upper(), con_db.dsn.upper())) 130 131 132def new_cursor(con_db, input_handler=None, output_handler=None): 133 """ 134 Retorna cx_Oracle.Cursor con los handlers pasados por parámetro 135 136 Args: 137 con_db: 138 input_handler: 139 output_handler: 140 141 Returns: 142 cx_Oracle.cursor 143 """ 144 try: 145 curs = con_db.cursor() 146 147 if input_handler: 148 curs.inputtypehandler = input_handler 149 150 if output_handler: 151 curs.outputtypehandler = output_handler 152 153 return curs 154 155 except cx_Oracle.Error as exc: 156 print("!!ERROR!! - Error al instanciar Cursor para la conexion Oracle {}\n" 157 " Error: {}".format(get_nom_conexion(con_db), exc)) 158 raise 159 160 161def get_row_descriptor(curs, nom_base_desc=None): 162 """ 163 Retorna instancia namedtuple indexada por las columnas de la query ejecutada en el cursor con los 164 tipos de columna por valores. 165 166 Si se pasa nom_base_desc se hará heredar el namedtuple de esa clase 167 168 Args: 169 curs: 170 nom_base_desc: 171 172 Returns: 173 174 """ 175 ora_desc = None 176 dd_reg = curs.description 177 dict_camps = OrderedDict.fromkeys([def_col[0].replace(" ", "_") for def_col in dd_reg]) 178 if nom_base_desc: 179 # Por si viene nombre de tabla con esquema delante nos quedamos solo con la ultima parte 180 nom_base_desc = nom_base_desc.split(".")[-1] 181 nom_base_desc += "_cursor" 182 else: 183 nom_base_desc = "cursor_" + str(id(curs)) 184 185 try: 186 nt_class = namedtuple(nom_base_desc, 187 list(dict_camps)) 188 ora_desc = nt_class(*[def_cols[1] for def_cols in dd_reg]) 189 except ValueError: 190 raise Exception("!ERROR! - No se puede crear descriptor de fila para el SQL especificado. " 191 "El nombre de la columna {} no es válido".format(str(sys.exc_info()[1]).split(":")[1]), 192 sys.exc_info()) 193 194 return ora_desc 195 196 197def get_row_class_cursor(cursor_desc, con_db): 198 """ 199 Retorna clase de fila por defecto (class row_cursor) para un cursor 200 201 Args: 202 cursor_desc: (opcional) clase de la que se quiera heredar 203 con_db: conexión cx_Oracle 204 205 Returns: 206 class (default = row_cursor) 207 """ 208 cls_curs_dsc = type(cursor_desc) 209 210 class row_cursor(cls_curs_dsc): 211 """ 212 Clase para fila devuelta por query SQL (para queries que NO sean sobre una tabla/vista) 213 """ 214 ora_descriptor = cursor_desc 215 con_ora = con_db 216 217 def vals(self): 218 """ 219 Valores de la fila 220 221 Returns: 222 OrderedDict 223 """ 224 return self._asdict() 225 226 def as_xml(self, 227 nom_root_xml="root_reg_sql", 228 atts_root_xml=None, 229 excluded_cols=None, 230 as_etree_elem=False): 231 """ 232 Devuelve fila en formato XML 233 234 Args: 235 nom_root_xml: define el nombre del TAG root del XML. Por defecto 'root_reg_sql' 236 atts_root_xml: lista que define las columnas que se asignarán 237 como atributos del TAG root 238 excluded_cols: lista con nombres de columnas que no se quieran incluir 239 as_etree_elem (default=False): Si True devuelve registro como objeto etree.Element. 240 Si False (por defecto) como str en formato XML 241 242 Returns: 243 str (formato XML) OR etree.Element 244 """ 245 if not excluded_cols: 246 excluded_cols = list() 247 248 atts_xml = None 249 d_xml_elems = self.xml_elems() 250 if atts_root_xml: 251 atts_xml = OrderedDict({att.lower(): d_xml_elems[att.lower()] 252 for att in atts_root_xml}) 253 254 tab_elem = etree.Element(nom_root_xml.lower(), atts_xml) 255 excluded_cols = [c.lower() for c in excluded_cols] 256 for tag, xml_val in d_xml_elems.items(): 257 if tag in excluded_cols: 258 continue 259 260 try: 261 camp_elem = etree.SubElement(tab_elem, tag.lower()) 262 camp_elem.text = xml_val 263 except: 264 print("!ERROR! - No se ha podido añadir el campo '", 265 tag, "' al XML", sep="") 266 267 ret = tab_elem 268 269 if not as_etree_elem: 270 ret = etree.tostring(tab_elem, encoding="unicode") 271 272 return ret 273 274 def xml_elems(self): 275 """ 276 Itera los campos del registro y devuelve su nombre y su valor parseado para XML 277 278 Yields: 279 n_camp, val_camp 280 """ 281 d_xml_elems = dict() 282 for camp, val in self.vals().items(): 283 ret_val = "" 284 if isinstance(val, datetime.datetime): 285 ret_val = val.strftime("%Y-%m-%dT%H:%M:%S") 286 elif isinstance(val, m_sdo_geom.sdo_geom): 287 ret_val = val.as_wkt() 288 elif val: 289 ret_val = str(val) 290 291 d_xml_elems[camp.lower()] = ret_val 292 293 return d_xml_elems 294 295 def as_json(self): 296 """ 297 Retorna la fila como JSON con las geometrias en formato GeoJson 298 299 Returns: 300 str con la fila en formato json 301 """ 302 return json.dumps(self.vals(), 303 ensure_ascii=False, 304 cls=geojson_encoder) 305 306 def as_geojson(self): 307 """ 308 Retorna la fila como GEOJSON 309 310 Returns: 311 str con la fila en formato geojson 312 """ 313 return json.dumps(self.__geo_interface__, 314 ensure_ascii=False, 315 cls=geojson_encoder) 316 317 @property 318 def __geo_interface__(self): 319 """ 320 GeoJSON-like protocol for geo-spatial para toda una fila devuelta por query SQL 321 322 Si la fila tiene varias geometrias devolverá geojson como "GeometryCollection" 323 324 Returns: 325 dict con las geometrias y campos alfanuméricos (properties de Feature) en formato geojson 326 """ 327 geos = dict(**self.sdo_geoms) 328 num_geos = len(geos) 329 geom = None 330 if num_geos == 1: 331 geom = next(iter(geos.values())) 332 elif num_geos > 1: 333 geom = {"type": "GeometryCollection", 334 "geometries": list(geos.values())} 335 336 if geom: 337 return dict({"type": "Feature", 338 "geometry": geom, 339 "properties": {nc: val for nc, val in self.vals().items() if nc not in geos}}) 340 341 @property 342 def sdo_geoms(self): 343 """ 344 Devuelve diccionario indexado por los nombres de columnas que tienen valor geometrico de la clase sdo_geom 345 346 Returns: 347 dict {nom_column:sdo_geom} 348 """ 349 return {nc: g.__geo_interface__ 350 for nc, g in self.vals().items() if isinstance(g, m_sdo_geom.sdo_geom)} 351 352 def geometries(self, as_format=None): 353 """ 354 Itera por los campos geométricos informados 355 356 Args: 357 as_format (default=None): Se puede informar con nombre funcion 'as_XXXX' a la que responda SDO_GEOM 358 359 Yields: 360 nom_camp, geom 361 """ 362 for ng, g in self.vals().items(): 363 if isinstance(g, m_sdo_geom.sdo_geom): 364 g_val = g 365 if as_format: 366 g_val = getattr(g, as_format)() 367 368 yield ng, g_val 369 370 @property 371 def cols_pts_angles(self): 372 """ 373 Devuelve diccionario de campos angulo para geometrias puntuales con la clave el nombre del campo angulo y 374 el valor el nombre del campo puntual al que hace referencia 375 376 Returns: 377 dict 378 """ 379 sufix_ang = "_ANG" 380 cols = self.cols 381 cols_ang = {} 382 for c in (c for c, gd in self.geoms_vals().items() if gd.GTYPE.endswith('POINT')): 383 col_ang = x_sql_parser.get_nom_obj_sql(c, sufix=sufix_ang) 384 if col_ang in cols: 385 cols_ang[col_ang] = c 386 387 return cols_ang 388 389 @property 390 def cols(self): 391 """ 392 Devuelve lista con el nombre de la columnas de la fila 393 394 Returns: 395 list con nombres columnas 396 """ 397 return self._fields 398 399 return row_cursor 400 401 402def get_pk_tab(con_db, nom_tab_or_view): 403 """ 404 Retorna la PRIMARY KEY de una tabla o vista de Oracle 405 406 Args: 407 con_db: 408 nom_tab_or_view: 409 410 Returns: 411 lista nombre columnas clave 412 """ 413 nom_pk_cache = get_nom_conexion(con_db) + ":" + nom_tab_or_view.upper() 414 noms_camps_pk = __cache_pks_tab.get(nom_pk_cache) 415 if not noms_camps_pk: 416 sql_keys = "SELECT cols.column_name " \ 417 "FROM user_constraints cons, user_cons_columns cols " \ 418 "WHERE cols.table_name = :1 " \ 419 "AND cons.constraint_type = 'P' " \ 420 "AND cons.constraint_name = cols.constraint_name " \ 421 "AND cons.owner = cols.owner " \ 422 "ORDER BY cols.table_name, cols.position" 423 424 noms_camps_pk = [fila_val.COLUMN_NAME for fila_val in 425 iter_execute_fetch_sql(con_db, sql_keys, nom_tab_or_view.upper())] 426 __cache_pks_tab[nom_pk_cache] = noms_camps_pk 427 428 return noms_camps_pk 429 430 431def get_tips_geom_tab(con_db, nom_tab_or_view): 432 """ 433 Retorna diccionario con nombre del campo como indice y los tipos de campos geométricos 434 (class_tip_geom) registrados en la global __class_tips_geom_ora como clases 'c_desc_tip_geom_?' siendo ? 435 el LAYER_GTYPE del indice de Oracle 436 437 Args: 438 con_db: 439 nom_tab_or_view: 440 441 Returns: 442 dict indexado por nombre columnas geometria y la clase de geometria (__class_tips_geom_ora) 443 """ 444 nom_con = get_nom_conexion(con_db) 445 446 tips_geom_con = __cache_tips_geom_tab.get(nom_con) 447 if not tips_geom_con: 448 tips_geom_con = __cache_tips_geom_tab[nom_con] = {} 449 sql_tip_geom = "select /*+ result_cache */ " \ 450 " T_COLS.TABLE_NAME, " \ 451 " T_COLS.COLUMN_NAME, " \ 452 " V_IDX_META.SDO_LAYER_GTYPE GTYPE, " \ 453 " V_G_META.SRID " \ 454 "from user_tab_columns t_cols," \ 455 " user_sdo_geom_metadata v_g_meta," \ 456 " user_sdo_index_info v_idx_info," \ 457 " user_sdo_index_metadata v_idx_meta " \ 458 "where v_g_meta.table_name = t_cols.table_name and " \ 459 "v_g_meta.column_name = t_cols.column_name and" \ 460 " v_idx_info.table_name = t_cols.table_name and " \ 461 "v_idx_info.column_name = t_cols.column_name and" \ 462 " v_idx_meta.sdo_index_name = v_idx_info.index_name" 463 464 for reg_tip_geom in iter_execute_fetch_sql(con_db, sql_tip_geom): 465 nom_cache = reg_tip_geom.TABLE_NAME.upper() 466 cache_tips_geom = tips_geom_con.get(nom_cache) 467 if not cache_tips_geom: 468 cache_tips_geom = {} 469 tips_geom_con[nom_cache] = cache_tips_geom 470 471 tip_geom = class_tip_geom(reg_tip_geom.GTYPE)(*reg_tip_geom.vals().values()) 472 473 nom_camp_geom = reg_tip_geom.COLUMN_NAME.upper() 474 cache_tips_geom[nom_camp_geom] = tip_geom 475 476 tips_geom = tips_geom_con.get(nom_tab_or_view.upper()) 477 478 return tips_geom if tips_geom else {} 479 480 481def get_row_desc_tab(con_db, nom_tab_or_view): 482 """ 483 Retorna el descriptor (cursor con los tipos de campo para cada columna) de una tabla o vista de Oracle 484 485 Args: 486 con_db: 487 nom_tab_or_view: 488 489 Returns: 490 namedtuple ó clase fila con las columnas y sus tipos 491 """ 492 nom_dd_cache = get_nom_conexion(con_db) + ":" + nom_tab_or_view.upper() 493 tab_desc = __cache_row_desc_tab.get(nom_dd_cache) 494 if not tab_desc: 495 row_class_tab = get_row_class_tab(con_db, nom_tab_or_view) 496 497 camps_dd = row_class_tab.ora_descriptor 498 dict_camps = camps_dd._asdict() 499 dict_camps_geom = row_class_tab.geoms() 500 if dict_camps_geom: 501 for nom_camp, tip_camp_geom in dict_camps_geom.items(): 502 dict_camps[nom_camp] = tip_camp_geom 503 504 tab_desc = row_class_tab(*dict_camps.values()) 505 __cache_row_desc_tab[nom_dd_cache] = tab_desc 506 507 return tab_desc 508 509 510def get_tip_camp(con_db, nom_tab_or_view, nom_camp): 511 """ 512 Devuelve el tipo de campo para la tabla_vista y campo especificado. En el caso de campos alfanuméricos 513 devuelve los tipos de cx_Oracle relacionados (cx_Oracle.NUMBER, cx_Oracle.STRING, ...) y para las 514 geométricas del tipo CLASS_TIP_GEOM 515 516 Args: 517 con_db: 518 nom_tab_or_view: 519 nom_camp: 520 521 Returns: 522 object: (cx_Oracle.NUMBER, cx_Oracle.STRING, ...) o si geometria tipo en __class_tips_geom_ora 523 """ 524 nom_tab_or_view = nom_tab_or_view.upper() 525 nom_camp = nom_camp.upper() 526 527 dd_tab = get_row_desc_tab(con_db, nom_tab_or_view) 528 if dd_tab: 529 return getattr(dd_tab, nom_camp, None) 530 531 532def sql_tab(nom_tab_or_view, filter_sql=None, columns=None): 533 """ 534 Devuelve sql para tabla o vista 535 536 Args: 537 nom_tab_or_view: 538 filter_sql: 539 columns (list): Lista nombre de columnas a mostrar. Default '*' (todas) 540 541 Returns: 542 str: con select sql para tabla o vista 543 """ 544 cols = "*" 545 if columns: 546 cols = ",".join(columns) 547 548 sql_tab = "select {cols} from {taula} TAB".format( 549 taula=nom_tab_or_view, 550 cols=cols) 551 552 if filter_sql: 553 sql_tab += " where " + filter_sql 554 555 return sql_tab 556 557 558def get_row_class_tab(con_db, nom_tab_or_view): 559 """ 560 Retorna clase que hereda de namedtuple para instanciar con los valores de un nuevo registro 561 562 Args: 563 con_db: 564 nom_tab_or_view: 565 566 Returns: 567 object: clase que hereda del namedtuple para una fila 568 """ 569 nom_tab_or_view = nom_tab_or_view.upper() 570 nom_row_class_tab_cache = get_nom_conexion(con_db) + ":" + nom_tab_or_view 571 row_class_tab = __cache_row_class_tab.get(nom_row_class_tab_cache) 572 573 if not row_class_tab: 574 curs = con_db.cursor() 575 curs.execute(sql_tab(nom_tab_or_view)) 576 ora_desc = get_row_descriptor(curs, nom_tab_or_view) 577 578 row_cursor_cls = get_row_class_cursor(ora_desc, con_db) 579 580 class row_table(row_cursor_cls): 581 """ 582 Clase para fila devuelta por SQL sobre tabla 583 """ 584 nom_tab = nom_tab_or_view 585 586 def __repr__(self): 587 """ 588 built_in que actua cuando se representa clase como STRING 589 590 Returns: 591 str 592 """ 593 repr_txt = nom_tab_or_view + "({pk_vals})" 594 pk_txt = [] 595 for i, v in self.pk_vals().items(): 596 pk_txt.append(str(i) + "=" + str(v)) 597 598 return repr_txt.format(pk_vals=",".join(pk_txt)) 599 600 @staticmethod 601 def pk(): 602 """ 603 Retorna lista con los nombres de campos clave 604 605 Returns: 606 list con nombres campos clave 607 """ 608 return get_pk_tab(con_db, nom_tab_or_view) 609 610 def pk_vals(self): 611 """ 612 Retorna OrderedDict con los pares nombres:valor para los campos clave 613 614 Returns: 615 OrderedDict 616 """ 617 pk_vals = OrderedDict.fromkeys(self.pk()) 618 for k in self.pk(): 619 pk_vals[k] = self.vals().get(k) 620 621 return pk_vals 622 623 @staticmethod 624 def geoms(): 625 """ 626 Devuelve diccionario {nom_camp_geom:tip_geom} para todas las columnas de la tabla que son de 627 tipo geométrico. 628 Lo deduce de la definición de la tabla independientemente de si la columna está informada o no 629 630 Returns: 631 dict {nom_geom:tip_geom} (vease funcion clas_tip_geom()) 632 """ 633 return get_tips_geom_tab(con_db, nom_tab_or_view) 634 635 def geoms_vals(self): 636 """ 637 Valores de los campos geometricos 638 639 Returns: 640 dict 641 """ 642 vals = self.vals() 643 return {nc: vals[nc] for nc in self.geoms()} 644 645 def as_xml(self, excluded_cols=[], 646 as_etree_elem=False): 647 """ 648 Devuelve row como XML 649 650 Args: 651 excluded_cols: lista de nombre de columnas que se quieren excluir 652 as_etree_elem {bool} (default=False): Si True devuelve registro como objeto etree.Element. Si False (por defecto) como str en formato XML 653 654 Returns: 655 str (formato XML) OR etree.Element 656 """ 657 return super(row_table, self).as_xml(nom_tab_or_view, 658 self.pk_vals(), 659 excluded_cols, 660 as_etree_elem) 661 662 @staticmethod 663 def get_row_desc(): 664 return get_row_desc_tab(con_db, nom_tab_or_view) 665 666 @staticmethod 667 def alfas(include_pk=True): 668 """ 669 Devuelve diccionario {nom_camp:tip_camp} para las columnas alfanuméricas (NO son geométricas) 670 Lo deduce de la definición de la tabla independientemente de si la columna está informada o no 671 672 Args: 673 include_pk: incluir primary key 674 675 Returns: 676 dict {nom_camp:tip_camp} 677 """ 678 return {nc: tc 679 for nc, tc in get_row_desc_tab(con_db, nom_tab_or_view).vals().items() 680 if nc not in get_tips_geom_tab(con_db, nom_tab_or_view) and 681 (include_pk or nc not in get_pk_tab(con_db, nom_tab_or_view))} 682 683 def alfas_vals(self): 684 """ 685 Devuelve diccionario con valores campos alfanuméricos 686 687 Returns: 688 dict {nom_camp:val_camp} 689 """ 690 vals = self.vals() 691 return {nc: vals[nc] for nc in self.alfas()} 692 693 row_class_tab = row_table 694 __cache_row_class_tab[nom_row_class_tab_cache] = row_class_tab 695 696 return row_class_tab 697 698 699def get_row_factory(curs, a_row_class=None): 700 """ 701 Retorna funcion para crear instancia clase a partir de los valores de una fila 702 703 Args: 704 curs: 705 a_row_class: 706 707 Returns: 708 function 709 """ 710 if not a_row_class: 711 cursor_desc = get_row_descriptor(curs) 712 con_db = curs.connection 713 a_row_class = get_row_class_cursor(cursor_desc, con_db) 714 715 def row_factory_func(*vals_camps): 716 return a_row_class(*vals_camps) 717 718 return row_factory_func 719 720 721def get_row_cursor(curs, rowfactory=None): 722 """ 723 Retorna fila como clase que construye el rowfactory. Por defecto si no se informa esta clase heredará 724 de 'namedtuple' con los campos como items más funcionalidad relacionada con la conexion y el descriptor de la fila 725 con los tipos de campo para cada columna 726 727 Args: 728 curs: 729 rowfactory: 730 731 Returns: 732 733 """ 734 if not rowfactory and not curs.rowfactory: 735 rowfactory = get_row_factory(curs) 736 737 if rowfactory: 738 curs.rowfactory = rowfactory 739 740 row = curs.fetchone() 741 742 return row 743 744 745def iter_execute_fetch_sql(con_db, sql_str, *args_sql, **extra_params): 746 """ 747 Itera y devuelve cada fila devuelta para la consulta sql_str 748 749 Args: 750 con_db: 751 sql_str: 752 *args_sql: 753 **extra_params: { "row_class": clase que se utilizará para cada fila. Vease get_row_factory() 754 "as_format": formato en el que se devuelve cada fila. Las clases base row_cursor y row_table 755 (vease get_row_class_cursor() y get_row_class_tab()) responden por defecto a 756 "as_xml()" y "as_json()" } 757 758 Returns: 759 row_class o string en formato especificado en **extra_params["as_format"] 760 """ 761 curs = None 762 763 try: 764 curs = new_cursor(con_db, 765 input_handler=extra_params.pop("input_handler", 766 m_sdo_geom.get_sdo_input_handler()), 767 output_handler=extra_params.pop("output_handler", 768 m_sdo_geom.get_output_handler( 769 con_db, 770 extra_params.pop("geom_format", None)))) 771 772 curs.execute(sql_str, 773 args_sql) 774 775 row_class = extra_params.pop("row_class", None) 776 row_factory = None 777 if row_class: 778 row_factory = get_row_factory(curs, row_class) 779 780 reg = get_row_cursor(curs, rowfactory=row_factory) 781 while reg is not None: 782 if "as_format" in extra_params: 783 f_format = extra_params["as_format"] 784 if f_format: 785 reg = getattr(reg, f_format)() 786 787 yield reg 788 789 reg = get_row_cursor(curs) 790 791 except: 792 if curs is not None: 793 curs.close() 794 raise 795 796 if curs is not None: 797 curs.close() 798 799 800def execute_fetch_sql(con_db, sql_str, *args_sql, **extra_params): 801 """ 802 Devuelve la primera iteración sobre la consulta sql_str 803 804 Args: 805 con_db: 806 sql_str: 807 *args_sql: 808 **extra_params: { "row_class": clase que se utilizará para cada fila. Vease get_row_factory() 809 "as_format": formato en el que se devuelve cada fila. Las clases base row_cursor y row_table 810 (vease get_row_class_cursor() y get_row_class_tab()) responden por defecto a 811 "as_xml()" y "as_json()" } 812 813 Returns: 814 row_class o string en formato especificado en **extra_params["as_format"] 815 """ 816 reg = None 817 for row in iter_execute_fetch_sql(con_db, 818 sql_str, 819 *args_sql, 820 input_handler=extra_params.pop("input_handler", None), 821 output_handler=extra_params.pop("output_handler", None), 822 row_class=extra_params.pop("row_class", None), 823 **extra_params): 824 reg = row 825 break 826 827 return reg 828 829 830def dict_as_sql_bind_and_params(dict_vals, bool_rel="and", filter_oper="="): 831 """ 832 A partir de un dict de {nom_camps:value_camps} devuelve un string en forma 833 de SQL FILTER bindind (camp_a = :1 and camp_b = :2) y la lista de params que se asignarán via binding 834 El BOOL_REL podrá ser 'AND', 'OR' o ',' para el SET de los updates 835 El FILTER_OPER podrá ser '=', '!=' o ':=' para el SET de los updates 836 837 Args: 838 dict_vals: 839 bool_rel: 840 filter_oper: 841 842 Returns: 843 str, [params*] 844 """ 845 query_elems = {} 846 for nom_camp, val_clau in dict_vals.items(): 847 sql_str = str(nom_camp) + " " + filter_oper + " :" + str(nom_camp) 848 query_elems[sql_str] = val_clau 849 850 sql = (" " + bool_rel.strip().upper() + " ").join(query_elems.keys()) 851 852 return sql, list(query_elems.values()) 853 854 855class SerializableGenerator(list): 856 """Generator that is serializable by JSON 857 858 It is useful for serializing huge data by JSON 859 It can be used in a generator of json chunks used e.g. for a stream 860 ('[1', ']') 861 # >>> for chunk in iter_json: 862 # ... stream.write(chunk) 863 # >>> SerializableGenerator((x for x in range(3))) 864 # [<generator object <genexpr> at 0x7f858b5180f8>] 865 """ 866 867 def __init__(self, iterable): 868 super().__init__() 869 tmp_body = iter(iterable) 870 try: 871 self._head = iter([next(tmp_body)]) 872 self.append(tmp_body) 873 except StopIteration: 874 self._head = [] 875 876 def __iter__(self): 877 return itertools.chain(self._head, *self[:1]) 878 879 880def geojson_from_gen_ora_sql(generator_sql, as_string=False): 881 """ 882 Devuelve diccionario geojson para un generator de rows de Oracle 883 884 Args: 885 generator_sql (function generator): 886 as_string (bool): (opcional) indica si se querrá el geojson como un string 887 888 Returns: 889 geojson (dict ó str) 890 """ 891 vals = {"type": "FeatureCollection", 892 "features": [getattr(r, "__geo_interface__") 893 for r in generator_sql 894 if getattr(r, "__geo_interface__")]} 895 896 ret = vals 897 if as_string: 898 ret = json.dumps(vals, 899 ensure_ascii=False) 900 901 return ret 902 903 904def vector_file_from_gen_ora_sql(file_path, vector_format, func_gen, zipped=False, indent_json=None, cols_csv=None, 905 tip_cols_csv=None): 906 """ 907 A partir del resultado de una query SQL devuelve un file_object formateado segun formato 908 909 Args: 910 file_path (str): path del fichero a grabar 911 vector_format (str): tipo formato (CSV, JSON, GEOJSON) 912 func_gen (generator function): funcion que devuelva filas sql en forma de row_cursor o row_table 913 (vease funciones generator_rows_sql() o generator_rows_table()) 914 zipped (bool=False): Devuelve fichero en un fichero comprimido (.zip) 915 indent_json (int): 916 cols_csv (list): Lista de columnes del CSV 917 tip_cols_csv (list): Lista de tipos de columnas para CSV. 918 Revisar especificacion en https://giswiki.hsr.ch/GeoCSV 919 Returns: 920 str: pathfile del fichero generado 921 """ 922 vector_format = vector_format.lower() 923 newline = None if vector_format != "csv" else "" 924 file_path_csvt = None 925 str_csvt = None 926 dir_base = os.path.dirname(file_path) 927 if dir_base: 928 os.makedirs(dir_base, exist_ok=True) 929 930 # Se hace en memoria o recursos locales (SpooledTemporaryFile) para evitar trabajar lo minimo en la red 931 # si se da el caso en el path fichero del fichero indicado. Cuando se quiere grabar en recurso local el tiempo 932 # perdido por utilizar un fichero temporal debería ser despreciable comparado con la complejidad añadida al código 933 # para decidir si usar o no el SpooledTemporaryFile 934 with SpooledTemporaryFile(mode="w+", encoding="utf-8", newline=newline) as temp_file: 935 if vector_format == "geojson": 936 json.dump(geojson_from_gen_ora_sql(func_gen), 937 temp_file, 938 ensure_ascii=False, 939 indent=indent_json, 940 cls=geojson_encoder) 941 elif vector_format == "json": 942 json.dump(SerializableGenerator((r.vals() for r in func_gen)), 943 temp_file, 944 ensure_ascii=False, 945 indent=indent_json, 946 cls=geojson_encoder) 947 elif vector_format == "csv": 948 writer = csv.DictWriter(temp_file, fieldnames=cols_csv) 949 writer.writeheader() 950 951 for r in func_gen: 952 writer.writerow(r.vals()) 953 954 temp_file.seek(0) 955 if tip_cols_csv: 956 file_path_csvt = ".".join((os.path.splitext(file_path)[0], "csvt")) 957 str_csvt = ",".join(tip_cols_csv) 958 959 if zipped: 960 file_path_res = "{}.zip".format(os.path.splitext(file_path)[0]) 961 with SpooledTemporaryFile() as zip_temp_file: 962 with ZipFile(zip_temp_file, "w", compression=ZIP_DEFLATED, allowZip64=True) as my_temp_zip: 963 my_temp_zip.writestr(zinfo_or_arcname=os.path.basename(file_path), data=temp_file.read()) 964 if str_csvt: 965 my_temp_zip.writestr(zinfo_or_arcname=os.path.basename(file_path_csvt), data=str_csvt) 966 967 zip_temp_file.seek(0) 968 with open(file_path_res, mode="wb") as file_res: 969 shutil.copyfileobj(zip_temp_file, file_res) 970 else: 971 file_path_res = file_path 972 with open(file_path_res, mode="w", encoding="utf-8") as file_res: 973 shutil.copyfileobj(temp_file, file_res) 974 if str_csvt: 975 with open(file_path_csvt, mode="w") as csvt_file: 976 csvt_file.write(str_csvt) 977 978 return file_path_res 979 980 981class geojson_encoder(json.JSONEncoder): 982 """ 983 Class Encoder to parser SDO_GEOM to GEOJSON 984 """ 985 __num_decs__ = 9 986 987 def default(self, obj_val): 988 """ 989 Redefine default para tratar las geometrias SDO_GEOM y convertirlas a geojson y las fechas a iso_format 990 Args: 991 obj_val: valor 992 993 Returns: 994 object encoded 995 """ 996 if isinstance(obj_val, m_sdo_geom.sdo_geom): 997 return obj_val.as_geojson() 998 elif isinstance(obj_val, (datetime.datetime, datetime.date)): 999 return obj_val.isoformat() 1000 elif isinstance(obj_val, cx_Oracle.LOB): 1001 return obj_val.read() 1002 else: 1003 return json.JSONEncoder.default(self, obj_val) 1004 1005 1006def print_to_log_exception(a_type_exc=Exception, lanzar_exc=False): 1007 """ 1008 Decorator para imprimir en el log una excepción capturada por un metodo de la clase 1009 1010 Returns: 1011 function 1012 """ 1013 1014 def decor_print_log_exception(func): 1015 @wraps(func) 1016 def meth_wrapper(cls, *args, **kwargs): 1017 try: 1018 return func(cls, *args, **kwargs) 1019 except a_type_exc: 1020 error_type, error_instance, traceback = sys.exc_info() 1021 1022 error_msg = "Error al executar funció '{clas}.{func}()' \n" \ 1023 "Arguments: {args}\n" \ 1024 "Fitxer: {file}".format( 1025 file=inspect.getmodule(func).__file__, 1026 clas=cls.__class__.__name__, 1027 func=func.__name__, 1028 args=", ".join(["'{}'".format(arg) for arg in args] + 1029 ["'{}={}'".format(a, b) for a, b in kwargs.items()])) 1030 1031 if hasattr(error_instance, "output"): 1032 error_msg += "\n" \ 1033 "Output: {}".format(error_instance.output) 1034 1035 cls.print_log_exception(error_msg) 1036 if lanzar_exc: 1037 raise error_instance 1038 1039 return meth_wrapper 1040 1041 return decor_print_log_exception 1042 1043 1044class gestor_oracle(object): 1045 """ 1046 Clase que gestionará distintas conexiones a Oracle y facilitará operaciones sobre la BBDD 1047 """ 1048 tip_number = cx_Oracle.NUMBER 1049 tip_string = cx_Oracle.STRING 1050 tip_clob = cx_Oracle.CLOB 1051 tip_blob = cx_Oracle.BLOB 1052 tip_date = cx_Oracle.DATETIME 1053 tip_fix_char = cx_Oracle.FIXED_CHAR 1054 1055 __slots__ = 'nom_con_db', '__con_db__', '__user_con_db__', \ 1056 '__psw_con_db__', '__dsn_ora__', '__call_timeout__', '__schema_con_db__', 'logger' 1057 1058 def __init__(self, user_ora, psw_ora, dsn_ora, a_logger=None, call_timeout: int = None, schema_ora=None): 1059 """ 1060 Inicializa gestor de Oracle para una conexion cx_Oracle a Oracle 1061 Se puede pasar por parametro un logger o inicializar por defecto 1062 1063 Args: 1064 user_ora {str}: Usuario/schema Oracle 1065 psw_ora {str}: Password usuario 1066 dsn_ora {str}: DSN Oracle (Nombre instancia/datasource de Oracle 1067 según TSN o string tal cual devuelve cx_Oracle.makedsn()) 1068 call_timeout (int=None): miliseconds espera per transaccio 1069 a_logger: 1070 schema_ora(str=None): indicate scheme when it is different from the user, default schema = user 1071 """ 1072 self.__con_db__ = None 1073 self.__call_timeout__ = call_timeout 1074 self.logger = a_logger 1075 self.__set_logger() 1076 self.__set_conexion(user_ora, psw_ora, dsn_ora, schema_ora=schema_ora) 1077 1078 def __del__(self): 1079 """ 1080 Cierra la conexion al matar la instancia 1081 """ 1082 try: 1083 if hasattr(self, "__con_db__"): 1084 self.__con_db__.close() 1085 except: 1086 pass 1087 1088 def __repr__(self): 1089 """ 1090 built_in que actua cuando se representa clase como STRING 1091 1092 Returns: 1093 str 1094 """ 1095 repr_txt = "{}".format(self.nom_con_db) 1096 1097 return repr_txt 1098 1099 @staticmethod 1100 def log_dir(): 1101 """ 1102 Devuelve el directorio donde irán los logs indicado en la funcion apb_logging.logs_dir() 1103 1104 Returns: 1105 {str} - path del directorio de logs 1106 """ 1107 return utils_logging.logs_dir(True) 1108 1109 def log_name(self): 1110 """ 1111 Devuelve el nombre del fichero de log por defecto 1112 1113 Returns: 1114 {str} - Nombre fichero log por defecto 1115 """ 1116 return self.__class__.__name__ 1117 1118 def log_file_name(self): 1119 return "{}.(LOG_LEVEL).log".format(os.path.join(self.log_dir(), self.log_name())) 1120 1121 def __set_logger(self): 1122 """ 1123 Asigna el LOGGER po defecto si este no se ha informado al inicializar el gestor 1124 1125 Returns: 1126 """ 1127 if self.logger is None: 1128 self.logger = utils_logging.get_file_logger(self.log_name(), dir_log=self.log_dir()) 1129 1130 def path_logs(self, if_exist=True): 1131 """ 1132 Devuelve lista paths base de los logs vinculados al gestor 1133 1134 Args: 1135 if_exist (bool): Devuelve los paths si el fichero existe 1136 1137 Returns: 1138 list: 1139 """ 1140 return logger_path_logs(self.logger) 1141 1142 def print_log(self, msg): 1143 """ 1144 Sobre el logger escribe mensaje de info 1145 1146 Args: 1147 msg {str}: String con el mensaje 1148 """ 1149 self.logger.info(msg) 1150 1151 def print_log_error(self, msg): 1152 """ 1153 Sobre el logger escribe mensaje de error 1154 1155 Args: 1156 msg {str}: String con el mensaje 1157 """ 1158 self.logger.error(msg) 1159 1160 def print_log_exception(self, msg): 1161 """ 1162 Sobre el logger escribe excepcion 1163 1164 Args: 1165 msg {str}: String con el mensaje 1166 """ 1167 self.logger.exception(msg) 1168 1169 @print_to_log_exception(lanzar_exc=True) 1170 def __set_conexion(self, user_ora, psw_ora, dsn_ora, schema_ora=None): 1171 """ 1172 Añade conexion Oracle al gestor a partir de nombre de usuario/schema (user_ora), contraseña (psw_ora) y 1173 nombre datasource de la bbdd según tns_names (ds_ora). 1174 1175 La conexión quedará registrada como 'user_ora@ds_ora' 1176 1177 Args: 1178 user_ora {str}: Usuario/schema Oracle 1179 psw_ora {str}: Password usuario 1180 dsn_ora {str}: DSN Oracle (Nombre instancia/datasource de Oracle según TSN o string tal cual devuelve cx_Oracle.makedsn()) 1181 schema_ora(str=None): indicate scheme when it is different from the user, default schema = user 1182 1183 """ 1184 nom_con = "@".join((user_ora.upper(), dsn_ora.upper())) 1185 self.nom_con_db = nom_con 1186 self.__user_con_db__ = user_ora 1187 self.__psw_con_db__ = psw_ora 1188 self.__schema_con_db__ = schema_ora 1189 self.__dsn_ora__ = dsn_ora 1190 self.__con_db__ = get_oracle_connection(user_ora, psw_ora, dsn_ora, self.__call_timeout__, schema_ora=schema_ora) 1191 1192 @property 1193 @print_to_log_exception(cx_Oracle.Error, lanzar_exc=True) 1194 def con_db(self): 1195 """ 1196 Return a cx_Oracle Conection live 1197 1198 Returns: 1199 cx_Oracle.Connection 1200 """ 1201 reconnect = False 1202 if (con_ora := self.__con_db__) is not None: 1203 try: 1204 con_ora.ping() 1205 except cx_Oracle.Error as exc: 1206 # Borramos las entradas de cache asociadas a la conexión que no responde 1207 del_cache_rel_con_db(get_nom_conexion(con_ora)) 1208 try: 1209 con_ora.close() 1210 except cx_Oracle.Error: 1211 pass 1212 self.__con_db__ = con_ora = None 1213 1214 if con_ora is None: 1215 self.__set_conexion( 1216 self.__user_con_db__, 1217 self.__psw_con_db__, 1218 self.__dsn_ora__, schema_ora=self.__schema_con_db__) 1219 1220 con_ora = self.__con_db__ 1221 1222 return con_ora 1223 1224 @print_to_log_exception(cx_Oracle.DatabaseError) 1225 def exec_trans_db(self, sql_str, *args_sql, **types_sql_args): 1226 """ 1227 Ejecuta transaccion SQL 1228 1229 Args: 1230 sql_str (str): sql transaction (update, insert, delete} 1231 *args_sql: Lista argumentos a pasar 1232 **types_sql_args (OPCIONAL): Lista tipos cx_Oracle para cada argumento 1233 1234 Returns: 1235 ok {bool}: Si ha ido bien True si no False 1236 """ 1237 curs_db = None 1238 try: 1239 curs_db = new_cursor(self.con_db, 1240 input_handler=m_sdo_geom.get_sdo_input_handler()) 1241 1242 curs_db.setinputsizes(*types_sql_args.values()) 1243 1244 curs_db.execute(sql_str, 1245 args_sql) 1246 finally: 1247 if curs_db: 1248 curs_db.close() 1249 1250 return True 1251 1252 @print_to_log_exception(cx_Oracle.DatabaseError) 1253 def exec_script_plsql(self, sql_str): 1254 """ 1255 Ejecuta script SQL 1256 1257 Args: 1258 sql_str {str}: sql script 1259 1260 Returns: 1261 ok {bool}: Si ha ido bien True si no False 1262 """ 1263 curs_db = None 1264 try: 1265 curs_db = new_cursor(self.con_db) 1266 curs_db.execute(sql_str) 1267 finally: 1268 if curs_db: 1269 curs_db.close() 1270 1271 return True 1272 1273 @print_to_log_exception(cx_Oracle.DatabaseError) 1274 def callfunc_sql(self, nom_func, ret_cx_ora_tipo, *args_func): 1275 """ 1276 Ejecuta funcion PL/SQL y retorna el valor 1277 1278 Args: 1279 nom_func (str): Nombre de la funcion PL/SQL 1280 ret_cx_ora_tipo (cx_Oracle TIPO): El retorno de la función en cx_Oracle (cx_Oracle.NUMBER, 1281 cx_Oracle.STRING,...) 1282 *args_func: Argumentos de la funcion PL/SQL 1283 1284 Returns: 1285 Valor retornado por la función PL/SQL 1286 """ 1287 curs = None 1288 try: 1289 curs = new_cursor(self.con_db) 1290 ret = curs.callfunc(nom_func, 1291 ret_cx_ora_tipo, 1292 args_func) 1293 finally: 1294 if curs: 1295 curs.close() 1296 1297 return ret 1298 1299 @print_to_log_exception(cx_Oracle.DatabaseError) 1300 def callproc_sql(self, nom_proc, *args_proc): 1301 """ 1302 Ejecuta procedimiento PL/SQL 1303 1304 Args: 1305 nom_proc (str): Nombre del procedimiento PL/SQL 1306 *args_proc: Argumentos del procedimiento 1307 1308 Returns: 1309 ok {bool}: Si ha ido bien True si no False 1310 """ 1311 curs = None 1312 try: 1313 curs = new_cursor(self.con_db) 1314 curs.callproc(nom_proc, 1315 args_proc) 1316 finally: 1317 if curs: 1318 curs.close() 1319 1320 return True 1321 1322 @print_to_log_exception(cx_Oracle.DatabaseError) 1323 def row_sql(self, sql_str, *args_sql, **extra_params): 1324 """ 1325 Retorna la fila resultante de la query sql SQL_STR con los parámetros *ARGS_SQL. 1326 Se puede informar **EXTRA_PARAMS con algunos de los siguientes parámetros: 1327 INPUT_HANDLER funcion que tratará los bindings de manera específica 1328 OUTPUT_HANLER funcion que tratará los valores de las columnas de modo específico 1329 ROW_CLASS define que con que clase se devolverá cada fila. Por defecto la que devuleve la funcion 1330 'apb_cx_oracle_spatial.get_row_class_cursor()' 1331 AS_FORMAT (as_xml, as_json, as_geojson) devuelve fila en el formato especificado. La row_class deberá 1332 responder a esas funciones 1333 1334 Args: 1335 sql_str: 1336 *args_sql: 1337 **extra_params: 1338 1339 Returns: 1340 object (instancia que debería ser o heredar de las clases row_cursor o row_table) 1341 """ 1342 return execute_fetch_sql(self.con_db, 1343 sql_str, 1344 *args_sql, 1345 input_handler=extra_params.pop("input_handler", None), 1346 output_handler=extra_params.pop( 1347 "output_handler", 1348 m_sdo_geom.get_output_handler(self.con_db, 1349 extra_params.pop("geom_format", None))), 1350 row_class=extra_params.pop("row_class", None), 1351 **extra_params) 1352 1353 @print_to_log_exception(cx_Oracle.DatabaseError) 1354 def generator_rows_sql(self, sql_str, *args_sql, **extra_params): 1355 """ 1356 Ejecuta consulta SQL de forma iterativa retornando cada fila como un objeto row_class (por defecto row_cursor) 1357 1358 Se puede informar **EXTRA_PARAMS con algunos de los siguientes parámetros: 1359 INPUT_HANDLER funcion que tratará los bindings de manera específica 1360 OUTPUT_HANLER funcion que tratará los valores de las columnas de modo específico 1361 ROW_CLASS define que con que clase se devolverá cada fila. Por defecto la que devuleve la funcion 1362 'apb_cx_oracle_spatial.get_row_class_cursor()' 1363 AS_FORMAT (as_xml, as_json, as_geojson) devuelve fila en el formato especificado. La row_class deberá 1364 responder a esas funciones 1365 1366 Args: 1367 sql_str: 1368 *args_sql: 1369 **extra_params: 1370 1371 Returns: 1372 object (instancia que debería ser o heredar de las clases row_cursor o row_table) 1373 """ 1374 for reg in iter_execute_fetch_sql(self.con_db, 1375 sql_str, 1376 *args_sql, 1377 input_handler=extra_params.pop("input_handler", None), 1378 output_handler=extra_params.pop( 1379 "output_handler", 1380 m_sdo_geom.get_output_handler(self.con_db, 1381 extra_params.pop("geom_format", None))), 1382 row_class=extra_params.pop("row_class", None), 1383 **extra_params): 1384 yield reg 1385 1386 def rows_sql(self, sql_str, *args_sql): 1387 """ 1388 Vease funcion 'generator_rows_sql()' 1389 Args: 1390 sql_str: 1391 *args_sql: 1392 1393 Returns: 1394 list 1395 """ 1396 return list(self.generator_rows_sql(sql_str, *args_sql)) 1397 1398 def get_primary_key_table(self, nom_tab_or_view): 1399 """ 1400 Retorna lista con las columnas que conforman la primary key de una tabla/vista 1401 Args: 1402 nom_tab_or_view: 1403 1404 Returns: 1405 list con campos clave 1406 """ 1407 return get_pk_tab(self.con_db, nom_tab_or_view) 1408 1409 @print_to_log_exception(lanzar_exc=True) 1410 def get_dd_table(self, nom_tab_or_view): 1411 """ 1412 Retorna instancia row_table con los tipos para cada columna 1413 Args: 1414 nom_tab_or_view: 1415 1416 Returns: 1417 object de clase row_table con los tipos de cada columna como valores 1418 """ 1419 return get_row_desc_tab(self.con_db, nom_tab_or_view) 1420 1421 def generator_rows_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql, **extra_params): 1422 """ 1423 Retorna los registros que cumplan con el FILTER_SQL sobre la tabla o vista indicada. 1424 1425 La tabla se puede referenciar en el filtro con el alias 'TAB' 1426 1427 Si el FILTER_SQL utiliza valores por binding (:1, :2,...) estos se indicaran por orden en ARGS_FILTER_SQL 1428 1429 Args: 1430 nom_tab_or_view: Nombre de la tabla o vista sobre la que se hará la consulta 1431 filter_sql: Filtre sobre la taula 1432 args_filter_sql: Valors en ordre de binding per passar 1433 extra_params: Vease generator_rows_sql() 1434 1435 Yields: 1436 row_table: regs. clase row_table (mirar get_row_class_tab()) 1437 """ 1438 for reg in self.generator_rows_sql(sql_tab(nom_tab_or_view, 1439 filter_sql), 1440 *args_filter_sql, 1441 row_class=extra_params.pop( 1442 "row_class", 1443 get_row_class_tab(self.con_db, nom_tab_or_view)), 1444 **extra_params): 1445 yield reg 1446 1447 def generator_rows_interact_geom(self, nom_tab, a_sdo_geom, cols_geom=None, geom_format=None): 1448 """ 1449 Retorna las filas de una tabla que interactuan con una geometria 1450 1451 Args: 1452 nom_tab: nombre de la tabla 1453 a_sdo_geom: geometria clase sdo_geom 1454 cols_geom (default=None): Lista con nombre de columnas geométricas sobre las que se quiere aplicar filtro 1455 geom_format: 1456 Yields: 1457 row_table: regs. clase row_table (mirar get_row_class_tab()) 1458 """ 1459 # Uso de " <>'FALSE'" por fallo de Oracle usando "= 'TRUE'" 1460 filter_interact_base = "SDO_ANYINTERACT({camp_geom}, :1) <> 'FALSE'" 1461 1462 if not cols_geom: 1463 cols_geom = get_tips_geom_tab(self.con_db, nom_tab).keys() 1464 1465 if cols_geom: 1466 filter_sql = " OR ".join([filter_interact_base.format(camp_geom=ng) for ng in cols_geom]) 1467 for reg in self.generator_rows_table(nom_tab, filter_sql, a_sdo_geom.as_ora_sdo_geometry(), 1468 geom_format=geom_format): 1469 yield reg 1470 1471 def rows_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql): 1472 """ 1473 Vease funcion 'generator_rows_table()' 1474 1475 Args: 1476 nom_tab_or_view: 1477 filter_sql: 1478 *args_filter_sql: 1479 1480 Returns: 1481 dict 1482 """ 1483 gen_tab = self.generator_rows_table(nom_tab_or_view, 1484 filter_sql, 1485 *args_filter_sql) 1486 pk_tab = self.get_primary_key_table(nom_tab_or_view) 1487 l_pk = len(pk_tab) 1488 if l_pk == 0: 1489 return [r for r in gen_tab] 1490 else: 1491 def f_key(r): 1492 return getattr(r, pk_tab[0]) 1493 1494 if l_pk > 1: 1495 def f_key(r): return tuple(map(lambda nf: getattr(r, nf), pk_tab)) 1496 return {f_key(r): r for r in gen_tab} 1497 1498 def row_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql, **extra_params): 1499 """ 1500 Retorna primer registro para el FILTER_SQL sobre la tabla o vista indicada 1501 Si el FILTER_SQL utiliza valores por binding (:1, :2,...) estos se indicaran por orden en ARGS_FILTER_SQL 1502 1503 Args: 1504 nom_tab_or_view: 1505 filter_sql: 1506 *args_filter_sql: 1507 **extra_params: 1508 1509 Returns: 1510 object de la clase row_table o especificada en **extra_params['row_class'] 1511 """ 1512 gen = self.generator_rows_table(nom_tab_or_view, 1513 filter_sql, 1514 *args_filter_sql, 1515 **extra_params) 1516 return next(gen, None) 1517 1518 def row_table_at(self, nom_tab_or_view, *vals_key): 1519 """ 1520 Devuelve row_tabla_class para el registro que de la tabla_vista que cumpla con la clave 1521 1522 Args: 1523 nom_tab_or_view: 1524 *vals_key: 1525 1526 Returns: 1527 object de la clase row_table o especificada en **extra_params['row_class'] 1528 """ 1529 return self.exist_row_tab(nom_tab_or_view, 1530 {nc: val for nc, val in zip(self.get_primary_key_table(nom_tab_or_view), 1531 vals_key)}) 1532 1533 def test_row_table(self, row_tab, a_sql, *args_sql): 1534 """ 1535 Testea un registro de tabla (clase rwo_table) cumpla con sql indicado 1536 1537 Args: 1538 row_tab: registro de tabla en forma de clase row_table 1539 a_sql: string con sql a testear 1540 1541 Returns: 1542 bool: True o False según cumpla con el SQL indicado 1543 """ 1544 sql_pk = dict_as_sql_bind_and_params(row_tab.pk_vals()) 1545 query_sql = "{} AND ({})".format(sql_pk[0], a_sql) 1546 1547 ret = False 1548 if self.row_table(row_tab.nom_tab, query_sql, *(tuple(sql_pk[1]) + tuple(args_sql))): 1549 ret = True 1550 1551 return ret 1552 1553 def insert_row_tab(self, nom_tab, dict_vals_param=None, dict_vals_str=None, pasar_nulls=False): 1554 """ 1555 Inserta registro en la tabla indicada. Los valores para cada columna se pasarán a través de dict_vals_param 1556 como bindings o a través de dict_vals_str directemente en el string del sql ejecutado 1557 Args: 1558 nom_tab: nombre de la tabla 1559 dict_vals_param: diccionario indexado por columnas-valores. Los valores se pasarán como bindings 1560 dict_vals_str: diccionario indexado por columnas-valores a asignar como strings. El valor se pasa 1561 directamente como asignacion en la senetencia sql 1562 pasar_nulls: (opcional) por defecto False. Indica si los valores NULL se asignarán o no 1563 1564 Returns: 1565 row_table (si genera el registro) o False si va mal la operación 1566 """ 1567 if not dict_vals_param: 1568 dict_vals_param = {} 1569 if not dict_vals_str: 1570 dict_vals_str = {} 1571 1572 ora_dict_vals_param = self.get_vals_tab_for_transdb(nom_tab, dict_vals_param, pasar_nulls=pasar_nulls) 1573 1574 if pasar_nulls: 1575 # Se revisa que en un campo geometrico se asigne None y por lo tanto se asigne valor via STR 1576 geoms_null = [ng for ng, val in ora_dict_vals_param.items() 1577 if not val and self.get_tip_camp_geom(nom_tab, ng)] 1578 if geoms_null: 1579 keys_str = [nc.upper() for nc in dict_vals_str.keys()] 1580 for gn in geoms_null: 1581 ora_dict_vals_param.pop(gn) 1582 if gn.upper() not in keys_str: 1583 dict_vals_str[gn.upper()] = "NULL" 1584 1585 params = [] 1586 nom_camps = [] 1587 vals_camps = [] 1588 for nom_camp, val_camp in ora_dict_vals_param.items(): 1589 if val_camp is None: 1590 continue 1591 nom_camps.append(nom_camp) 1592 vals_camps.append(":" + nom_camp) 1593 params.append(val_camp) 1594 1595 for nom_camp, val_camp_str in dict_vals_str.items(): 1596 if val_camp_str is None: 1597 continue 1598 nom_camps.append(nom_camp) 1599 vals_camps.append(str(val_camp_str)) 1600 1601 row_desc_tab = get_row_desc_tab(self.con_db, nom_tab) 1602 pk_binds = {k: new_cursor(self.con_db).var(ora_tip_camp) for k, ora_tip_camp in row_desc_tab.pk_vals().items()} 1603 if not pk_binds: 1604 pk_binds = {'ROWID': new_cursor(self.con_db).var(cx_Oracle.ROWID)} 1605 str_pk_camps = ",".join(pk_binds.keys()) 1606 str_pk_binds = ",".join(list(map(lambda x: ":ret_" + str(x), pk_binds.keys()))) 1607 params += list(pk_binds.values()) 1608 1609 a_sql_res = f"insert into {nom_tab}({','.join(nom_camps)}) values({','.join(vals_camps)}) " \ 1610 f"returning {str_pk_camps} into {str_pk_binds}" 1611 1612 ok = self.exec_trans_db(a_sql_res, *params) 1613 if ok: 1614 pk_vals = {k: curs_var.getvalue(0)[0] for k, curs_var in pk_binds.items()} 1615 return self.exist_row_tab(nom_tab, pk_vals) 1616 1617 return ok 1618 1619 def update_row_tab(self, nom_tab, dict_clau_reg, dict_vals_param=None, dict_vals_str=None, pasar_nulls=None): 1620 """ 1621 Actualiza registro en la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor} 1622 Los valores para cada columna se pasarán a través de dict_vals_param como bindings o a través de 1623 dict_vals_str directemente en el string del sql ejecutado 1624 1625 Args: 1626 nom_tab: nombre de la tabla 1627 dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar 1628 dict_vals_param: diccionario indexado por columnas-valores. Los valores se pasarán como bindings 1629 dict_vals_str: diccionario indexado por columnas-valores a asignar como strings. El valor se pasa 1630 directamente como asignacion en la senetencia sql 1631 pasar_nulls: (opcional) por defecto False. Indica si los valores NULL se asignarán o no 1632 1633 Returns: 1634 row_table (si genera el registro) o False si va mal la operación 1635 """ 1636 if not dict_vals_param: 1637 dict_vals_param = {} 1638 if not dict_vals_str: 1639 dict_vals_str = {} 1640 1641 ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg) 1642 ora_dict_vals_param = self.get_vals_tab_for_transdb(nom_tab, dict_vals_param, pasar_nulls=pasar_nulls) 1643 1644 if pasar_nulls: 1645 # Se revisa que en un campo geometrico se asigne None y por lo tanto se asigne valor via STR 1646 geoms_null = [ng for ng, val in ora_dict_vals_param.items() 1647 if not val and self.get_tip_camp_geom(nom_tab, ng)] 1648 if geoms_null: 1649 keys_str = [nc.upper() for nc in dict_vals_str.keys()] 1650 for gn in geoms_null: 1651 ora_dict_vals_param.pop(gn) 1652 if gn.upper() not in keys_str: 1653 dict_vals_str[gn.upper()] = "NULL" 1654 1655 (sql_set_camps, params_set_camps) = dict_as_sql_bind_and_params(ora_dict_vals_param, 1656 ",", "=") 1657 1658 (query_clau, params_filter) = dict_as_sql_bind_and_params(ora_dict_clau_reg) 1659 1660 params = params_set_camps + params_filter 1661 1662 for nom_camp, val_camp_str in dict_vals_str.items(): 1663 if sql_set_camps: 1664 sql_set_camps += " , " 1665 sql_set_camps += nom_camp + "=" + val_camp_str 1666 1667 ok = None 1668 if sql_set_camps: 1669 a_sql_res = f"update {nom_tab} set {sql_set_camps} where {query_clau}" 1670 1671 ok = self.exec_trans_db(a_sql_res, *params) 1672 1673 if ok: 1674 return self.exist_row_tab(nom_tab, dict_clau_reg) 1675 1676 return ok 1677 1678 def remove_row_tab(self, nom_tab, dict_clau_reg): 1679 """ 1680 Borra registro en la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor} 1681 1682 Args: 1683 nom_tab: nombre de la tabla 1684 dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar 1685 1686 Returns: 1687 bool según vaya la operación 1688 """ 1689 ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg) 1690 1691 a_sql_tmpl = "delete {nom_tab} where {query_clau}" 1692 1693 (sql_filter, params) = dict_as_sql_bind_and_params(ora_dict_clau_reg) 1694 1695 a_sql_res = a_sql_tmpl.format(nom_tab=nom_tab, 1696 query_clau=sql_filter) 1697 1698 return self.exec_trans_db(a_sql_res, *params) 1699 1700 def exist_row_tab(self, nom_tab, dict_clau_reg, **extra_params): 1701 """ 1702 Devuelve registro de la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor} 1703 1704 Args: 1705 nom_tab: nombre de la tabla 1706 dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar 1707 **extra_params: 1708 1709 Returns: 1710 object de la clase row_table o especificada en **extra_params['row_class'] 1711 """ 1712 ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg) 1713 1714 (filter_sql, params) = dict_as_sql_bind_and_params(ora_dict_clau_reg, "and", "=") 1715 1716 return self.row_table(nom_tab, filter_sql, *params, **extra_params) 1717 1718 def get_vals_tab_for_transdb(self, nom_tab, dict_camps_vals, pasar_nulls=True): 1719 """ 1720 Para un tabla y diccionario columnas-valores devuelve diccionario indexado por las columnas con los valores 1721 convertidos a formato cx_Oracle según tipo de cada columna en Oracle 1722 Args: 1723 nom_tab: nombre de la tabla 1724 dict_camps_vals: diccionario indexado por columnas-valor a convertir a formato cx_Oracle 1725 pasar_nulls: (opcional) por defecto convertirá los None a NULL de Oracle 1726 1727 Returns: 1728 dict indexado por columnas con los valores convertidos a tipo cx_Oracle 1729 """ 1730 dd_tab = get_row_desc_tab(self.con_db, nom_tab) 1731 1732 # Retorna dict con los campos a pasar por parametro 1733 d_params = {} 1734 1735 # Los nombres de campo siempre se buscarán en mayúsculas 1736 dict_camps_vals = {k.upper(): v for k, v in dict_camps_vals.items()} 1737 1738 for camp, tip_camp in dd_tab.vals().items(): 1739 if camp not in dict_camps_vals: 1740 continue 1741 1742 val_camp = dict_camps_vals.get(camp) 1743 if not pasar_nulls and val_camp is None: 1744 continue 1745 1746 if isinstance(val_camp, m_sdo_geom.sdo_geom): 1747 var = val_camp.as_ora_sdo_geometry() 1748 else: 1749 try: 1750 var = new_cursor(self.con_db).var(tip_camp) 1751 var.setvalue(0, val_camp) 1752 except: 1753 var = val_camp 1754 1755 d_params[camp] = var 1756 1757 return d_params 1758 1759 @print_to_log_exception() 1760 def run_sql_script(self, filename): 1761 """ 1762 Ejecuta slq script (filename) sobre SQLPLUS 1763 1764 Args: 1765 filename: path del sql script 1766 1767 Returns: 1768 1769 """ 1770 user_ora = self.con_db.username 1771 ds_ora = self.con_db.dsn 1772 nom_con = self.nom_con_db 1773 psw_ora = self.__psw_con_db__ 1774 if psw_ora is None: 1775 print("ERROR - Conexión '" + nom_con + "' no está añadida al gestor!!") 1776 return 1777 1778 with open(filename, 'rb') as a_file: 1779 a_sql_command = a_file.read() 1780 1781 con_db_str = user_ora + "/" + psw_ora + "@" + ds_ora 1782 sqlplus = Popen(['sqlplus', '-S', con_db_str], stdin=PIPE, stdout=PIPE, stderr=PIPE) 1783 # sqlplus.stdin.write(a_sql_command) 1784 1785 (stdout, stderr) = sqlplus.communicate(a_sql_command) 1786 1787 self.print_log("Resultado lanzar script '{}': \n" 1788 "{}".format(filename, 1789 stdout.decode("utf-8"))) 1790 1791 if sqlplus is not None: 1792 sqlplus.terminate() 1793 1794 @staticmethod 1795 def get_nom_obj_sql(nom_base, prefix="", sufix=""): 1796 """ 1797 Retorna nombre propuesto con prefijo/sufijos formateado para que cumpla longitud máxima de 32 caracteres en 1798 objetos sql Oracle 1799 1800 Args: 1801 nom_base: nombre propuesto 1802 prefix: (opc) prefijo 1803 sufix: (opc) sufijo 1804 1805 Returns: 1806 str formateado 1807 """ 1808 return x_sql_parser.get_nom_obj_sql(nom_base, prefix, sufix) 1809 1810 def iter_sdo_gtypes_vals_camp_tab(self, nom_taula, nom_camp): 1811 """ 1812 Retorna los distintos tipos de Geometria (codigo entero que define el tipo SDO_GTYPE) 1813 que se encuentran dentro de la columna sdo_geometry de una tabla 1814 1815 Args: 1816 nom_taula: nombre de la tabla 1817 nom_camp: nombre campo geometrico 1818 1819 Returns: 1820 int definiendo tipo de geometría 1821 """ 1822 sql_tip_geoms = f"select distinct(tab.{nom_camp}.Get_GType()) as tip_geom from {nom_taula} tab " \ 1823 f"where {nom_camp} is not null" 1824 1825 for reg in self.generator_rows_sql(sql_tip_geoms): 1826 yield reg.TIP_GEOM 1827 1828 def iter_distinct_vals_camp_tab(self, nom_taula, nom_camp, filter_sql=None): 1829 """ 1830 Retorna los distintos valores de la columna de una tabla 1831 1832 Args: 1833 nom_taula (str): Nombre de tabla 1834 nom_camp (str): Nombre de campo 1835 filter_sql(str): Filtro SQL sobre la tabla indicada 1836 1837 Returns: 1838 {str}: Itera los distintos valores encontrados en el campo indicado 1839 """ 1840 sql_distinct_vals = f"select distinct(tab.{nom_camp}) as VAL from {nom_taula} tab" 1841 if filter_sql: 1842 sql_distinct_vals += " where " + filter_sql 1843 1844 for reg in self.generator_rows_sql(sql_distinct_vals): 1845 yield reg.VAL 1846 1847 def get_tip_camp_geom(self, nom_tab_or_view, nom_camp_geom): 1848 """ 1849 Retorna el tipo de campo geométrico (class_tip_geom) registrados en la global __class_tips_geom_ora 1850 para el campo indicado 1851 1852 Args: 1853 nom_tab_or_view: nombre tabla/vista 1854 nom_camp_geom: nombre campo geom 1855 1856 Returns: 1857 namedtuple: con atributos ['TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID']) 1858 """ 1859 tips_geom_tab = get_tips_geom_tab(self.con_db, nom_tab_or_view) 1860 if tips_geom_tab: 1861 return tips_geom_tab.get(nom_camp_geom.upper()) 1862 1863 def get_epsg_for_srid(self, srid): 1864 """ 1865 Rertorna WKT con la definicion del SRID dado 1866 """ 1867 return self.callfunc_sql('SDO_CS.MAP_ORACLE_SRID_TO_EPSG', cx_Oracle.NUMBER, srid) 1868 1869 def get_gtype_camp_geom(self, nom_tab_or_view, nom_camp_geom): 1870 """ 1871 Retorna el tipo GTYPE (int) de la geometria 1872 1873 Args: 1874 nom_tab_or_view: nombre tabla/vista 1875 nom_camp_geom: nombre campo geom 1876 1877 Returns: 1878 int 1879 """ 1880 gtype = 0 1881 g_tip_ora = self.get_tip_camp_geom(nom_tab_or_view, nom_camp_geom) 1882 if g_tip_ora: 1883 gtype = GTYPES_ORA.index(g_tip_ora.GTYPE) 1884 1885 return gtype 1886 1887 @staticmethod 1888 def verificar_path_vector_file(nom_tab_or_view, dir, file_name, ext, zipped): 1889 """ 1890 Compone el/los path para el/los vector_file de una tabla y determina si exists 1891 Args: 1892 nom_tab_or_view: 1893 dir: 1894 file_name: 1895 ext: 1896 zipped: 1897 1898 Returns: 1899 file_path (str), file_path_zip (str), exists (bool) 1900 """ 1901 if file_name and not file_name.endswith(ext): 1902 file_name = ".".join((file_name, ext)) 1903 elif not file_name: 1904 file_name = ".".join((nom_tab_or_view, ext)).lower() 1905 1906 file_path = os.path.join(dir, file_name) 1907 file_path_zip = None 1908 if zipped: 1909 file_path_zip = "{}.zip".format(os.path.splitext(file_path)[0]) 1910 1911 exists = (os.path.exists(file_path) and not file_path_zip) or (file_path_zip and os.path.exists(file_path_zip)) 1912 1913 return file_path, file_path_zip, exists 1914 1915 @print_to_log_exception() 1916 def create_json_tab_or_view(self, nom_tab_or_view, dir='.', file_name=None, overwrite=True, cols=None, zipped=True, 1917 filter_sql=None, *args_sql): 1918 """ 1919 1920 Args: 1921 nom_tab_or_view (str): Nombre tabla o vista 1922 dir (str): 1923 file_name (str): 1924 overwrite (bool): 1925 cols (list): columnas 1926 zipped (bool): 1927 filter_sql (str): 1928 *args_sql: lista de argumentos a pasar al filtro sql 1929 Returns: 1930 file_path (str) 1931 """ 1932 file_path, file_path_zip, exists = self.verificar_path_vector_file( 1933 nom_tab_or_view, dir, file_name, "json", zipped) 1934 1935 if overwrite or not exists: 1936 # Se calculan las columnas para hacer get de la fila con el orden en las columnas de la tabla 1937 if not cols: 1938 dd_tab = self.get_dd_table(nom_tab_or_view) 1939 cols = dd_tab.cols 1940 sql = sql_tab(nom_tab_or_view, 1941 filter_sql=filter_sql, 1942 columns=cols) 1943 1944 file_path_res = vector_file_from_gen_ora_sql(file_path, "json", self.generator_rows_sql(sql, *args_sql), 1945 zipped=zipped) 1946 else: 1947 file_path_res = file_path if not zipped else file_path_zip 1948 1949 return file_path_res 1950 1951 @print_to_log_exception() 1952 def create_csv_tab_or_view(self, nom_tab_or_view, dir='.', file_name=None, overwrite=True, cols=None, zipped=True, 1953 filter_sql=None, *args_sql): 1954 """ 1955 1956 Args: 1957 nom_tab_or_view (str): Nombre tabla o vista 1958 dir (str): 1959 file_name (str): 1960 overwrite (bool): 1961 cols (list): columnas 1962 zipped (bool): 1963 filter_sql (str): 1964 *args_sql: lista de argumentos a pasar al filtro sql 1965 1966 Returns: 1967 file_path_res (str) 1968 """ 1969 file_path, file_path_zip, exists = self.verificar_path_vector_file( 1970 nom_tab_or_view, dir, file_name, "csv", zipped) 1971 1972 if overwrite or not exists: 1973 if not cols: 1974 dd_tab = self.get_dd_table(nom_tab_or_view) 1975 cols = dd_tab.cols 1976 sql = sql_tab(nom_tab_or_view, 1977 filter_sql=filter_sql, 1978 columns=cols) 1979 1980 # Para el formato geocsv que acepta GDAL se añade fichero con los tipos de columna 1981 tip_cols_csv = [] 1982 for col in cols: 1983 r_tip_col = self.row_table("user_tab_columns", 1984 "table_name = :1 and column_name = :2", 1985 nom_tab_or_view.upper(), col.upper()) 1986 dtype = r_tip_col.DATA_TYPE 1987 dlength = r_tip_col.DATA_LENGTH 1988 dprecision = r_tip_col.DATA_PRECISION 1989 dscale = r_tip_col.DATA_SCALE 1990 1991 if dtype == "DATE": 1992 tip_cols_csv.append('"DateTime"') 1993 elif dtype == "FLOAT": 1994 tip_cols_csv.append('"Real({}.{})"'.format(dlength, dprecision)) 1995 elif dtype == "NUMBER": 1996 if dscale and dscale != 0: 1997 tip_cols_csv.append('"Real({}.{})"'.format(dprecision, dscale)) 1998 elif dprecision: 1999 tip_cols_csv.append('"Integer({})"'.format(dprecision)) 2000 else: 2001 tip_cols_csv.append('"Real(10.8)"') 2002 elif dtype == "SDO_GEOMETRY": 2003 tip_cols_csv.append('"WKT"') 2004 else: 2005 tip_cols_csv.append('"String({})"'.format(round(dlength * 1.25))) 2006 2007 file_path_res = vector_file_from_gen_ora_sql(file_path, "csv", 2008 self.generator_rows_sql(sql, *args_sql, geom_format="as_wkt"), 2009 zipped=zipped, cols_csv=cols, tip_cols_csv=tip_cols_csv) 2010 else: 2011 file_path_res = file_path if not zipped else file_path_zip 2012 2013 return file_path_res 2014 2015 @print_to_log_exception() 2016 def create_geojsons_tab_or_view(self, nom_tab_or_view, dir='.', file_name_prefix=None, by_geom=False, 2017 dir_topojson=None, overwrite=True, cols=None, 2018 filter_sql=None, *args_sql): 2019 """ 2020 2021 Args: 2022 nom_tab_or_view (str): Nombre tabla (vigente o versionada) para entidad GIS 2023 dir (str="."): 2024 file_name_prefix (str=None): (opcional) prefijo del fichero 2025 by_geom (bool=False): (Opcional) si se querrán los geojsons por geometria. Si no se saca un unico geojson con 2026 la columna geometry como una GeometryCollection si la tabla es multigeom 2027 dir_topojson (str=None): path donde irán las conversiones 2028 overwrite (bool=True): 2029 cols (list=None): 2030 filter_sql (str=None): 2031 *args_sql: lista de argumentos a pasar al filtro sql 2032 2033 Returns: 2034 ok (bool) 2035 """ 2036 ext = "geo.json" 2037 sqls = {} 2038 dd_tab = self.get_dd_table(nom_tab_or_view) 2039 if not cols: 2040 cols = dd_tab.cols 2041 2042 if by_geom: 2043 c_alfas = [cn for cn in dd_tab.alfas() if cn in cols] 2044 c_geoms = [cn for cn in dd_tab.geoms() if cn in cols] 2045 for c_geom in c_geoms: 2046 sqls[c_geom] = sql_tab(nom_tab_or_view, filter_sql, c_alfas + [c_geom]) 2047 else: 2048 sqls[None] = sql_tab(nom_tab_or_view, filter_sql, cols) 2049 2050 if not file_name_prefix: 2051 file_name_prefix = nom_tab_or_view 2052 2053 for ng, sql in sqls.items(): 2054 file_name = file_name_prefix 2055 if ng: 2056 file_name = "-".join((file_name, ng)) 2057 2058 file_name = ".".join((file_name, ext)).lower() 2059 file_path = os.path.join(dir, file_name) 2060 2061 if overwrite or not os.path.exists(file_path): 2062 file_path = vector_file_from_gen_ora_sql(file_path, "geojson", 2063 self.generator_rows_sql(sql, *args_sql)) 2064 2065 if by_geom and dir_topojson and file_path: 2066 tip_geom = getattr(dd_tab, ng).GTYPE 2067 simplify = True 2068 if tip_geom.endswith("POINT"): 2069 simplify = False 2070 2071 topojson_utils.geojson_to_topojson(file_path, dir_topojson, 2072 simplify=simplify, 2073 overwrite=overwrite) 2074 2075 return True 2076 2077 2078if __name__ == '__main__': 2079 import fire 2080 2081 sys.exit(fire.Fire())
50def class_tip_geom(gtype_ora="DEFAULT"): 51 """ 52 Retorna NAMEDTUPLE 'cursor_desc_tip_geom_GTYPE' con columnas 53 'TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID' 54 55 Args: 56 gtype_ora: tipo geometrias como claves en __class_tips_geom_ora 57 58 Returns: 59 namedtuple('TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID') 60 61 """ 62 gtype_ora = gtype_ora.upper() 63 64 if gtype_ora not in __class_tips_geom_ora: 65 gtype_ora = "DEFAULT" 66 67 return __class_tips_geom_ora.get(gtype_ora)
Retorna NAMEDTUPLE 'cursor_desc_tip_geom_GTYPE' con columnas 'TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID'
Arguments:
- gtype_ora: tipo geometrias como claves en __class_tips_geom_ora
Returns:
namedtuple('TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID')
77def del_cache_rel_con_db(con_db_name: str): 78 """ 79 Borra las Caches para atributos de tablas_o_vistas de Oracle 80 Args: 81 con_db_name: nom de la connexió 82 83 Returns: 84 85 """ 86 all_cache = [__cache_pks_tab, __cache_row_desc_tab, __cache_tips_geom_tab, __cache_row_class_tab] 87 for cache_dic in all_cache: 88 # alamcenamos las claves porque no se puede eliminar del diccionario mientras se itera 89 keys_remove = [] 90 for key in cache_dic: 91 if key.startswith(con_db_name): 92 keys_remove.append(key) 93 for key_r in keys_remove: 94 del cache_dic[key_r]
Borra las Caches para atributos de tablas_o_vistas de Oracle
Arguments:
- con_db_name: nom de la connexió
Returns:
97def get_oracle_connection(user_ora, psw_ora, dsn_ora=None, call_timeout=None, schema_ora=None): 98 """ 99 Return cx_Oracle Connection 100 Args: 101 user_ora (str): 102 psw_ora (str): 103 dsn_ora (str=None): 104 call_timeout (int=None): miliseconds 105 schema_ora(str=None): indicate scheme when it is different from the user, default schema = user 106 107 Returns: 108 cx_Oracle.Connection 109 """ 110 connection = cx_Oracle.Connection(user_ora, psw_ora, dsn_ora, encoding="UTF-8", nencoding="UTF-8") 111 if call_timeout: 112 connection.call_timeout = call_timeout 113 114 if schema_ora: 115 connection.current_schema = schema_ora 116 117 return connection
Return cx_Oracle Connection
Arguments:
- user_ora (str):
- psw_ora (str):
- dsn_ora (str=None):
- call_timeout (int=None): miliseconds
- schema_ora(str=None): indicate scheme when it is different from the user, default schema = user
Returns:
cx_Oracle.Connection
120def get_nom_conexion(con_db): 121 """ 122 Devuelve el nombre de la conexion a Oracle 123 124 Args: 125 con_db: 126 127 Returns: 128 129 """ 130 return "@".join((con_db.username.upper(), con_db.dsn.upper()))
Devuelve el nombre de la conexion a Oracle
Arguments:
- con_db:
Returns:
133def new_cursor(con_db, input_handler=None, output_handler=None): 134 """ 135 Retorna cx_Oracle.Cursor con los handlers pasados por parámetro 136 137 Args: 138 con_db: 139 input_handler: 140 output_handler: 141 142 Returns: 143 cx_Oracle.cursor 144 """ 145 try: 146 curs = con_db.cursor() 147 148 if input_handler: 149 curs.inputtypehandler = input_handler 150 151 if output_handler: 152 curs.outputtypehandler = output_handler 153 154 return curs 155 156 except cx_Oracle.Error as exc: 157 print("!!ERROR!! - Error al instanciar Cursor para la conexion Oracle {}\n" 158 " Error: {}".format(get_nom_conexion(con_db), exc)) 159 raise
Retorna cx_Oracle.Cursor con los handlers pasados por parámetro
Arguments:
- con_db:
- input_handler:
- output_handler:
Returns:
cx_Oracle.cursor
162def get_row_descriptor(curs, nom_base_desc=None): 163 """ 164 Retorna instancia namedtuple indexada por las columnas de la query ejecutada en el cursor con los 165 tipos de columna por valores. 166 167 Si se pasa nom_base_desc se hará heredar el namedtuple de esa clase 168 169 Args: 170 curs: 171 nom_base_desc: 172 173 Returns: 174 175 """ 176 ora_desc = None 177 dd_reg = curs.description 178 dict_camps = OrderedDict.fromkeys([def_col[0].replace(" ", "_") for def_col in dd_reg]) 179 if nom_base_desc: 180 # Por si viene nombre de tabla con esquema delante nos quedamos solo con la ultima parte 181 nom_base_desc = nom_base_desc.split(".")[-1] 182 nom_base_desc += "_cursor" 183 else: 184 nom_base_desc = "cursor_" + str(id(curs)) 185 186 try: 187 nt_class = namedtuple(nom_base_desc, 188 list(dict_camps)) 189 ora_desc = nt_class(*[def_cols[1] for def_cols in dd_reg]) 190 except ValueError: 191 raise Exception("!ERROR! - No se puede crear descriptor de fila para el SQL especificado. " 192 "El nombre de la columna {} no es válido".format(str(sys.exc_info()[1]).split(":")[1]), 193 sys.exc_info()) 194 195 return ora_desc
Retorna instancia namedtuple indexada por las columnas de la query ejecutada en el cursor con los tipos de columna por valores.
Si se pasa nom_base_desc se hará heredar el namedtuple de esa clase
Arguments:
- curs:
- nom_base_desc:
Returns:
198def get_row_class_cursor(cursor_desc, con_db): 199 """ 200 Retorna clase de fila por defecto (class row_cursor) para un cursor 201 202 Args: 203 cursor_desc: (opcional) clase de la que se quiera heredar 204 con_db: conexión cx_Oracle 205 206 Returns: 207 class (default = row_cursor) 208 """ 209 cls_curs_dsc = type(cursor_desc) 210 211 class row_cursor(cls_curs_dsc): 212 """ 213 Clase para fila devuelta por query SQL (para queries que NO sean sobre una tabla/vista) 214 """ 215 ora_descriptor = cursor_desc 216 con_ora = con_db 217 218 def vals(self): 219 """ 220 Valores de la fila 221 222 Returns: 223 OrderedDict 224 """ 225 return self._asdict() 226 227 def as_xml(self, 228 nom_root_xml="root_reg_sql", 229 atts_root_xml=None, 230 excluded_cols=None, 231 as_etree_elem=False): 232 """ 233 Devuelve fila en formato XML 234 235 Args: 236 nom_root_xml: define el nombre del TAG root del XML. Por defecto 'root_reg_sql' 237 atts_root_xml: lista que define las columnas que se asignarán 238 como atributos del TAG root 239 excluded_cols: lista con nombres de columnas que no se quieran incluir 240 as_etree_elem (default=False): Si True devuelve registro como objeto etree.Element. 241 Si False (por defecto) como str en formato XML 242 243 Returns: 244 str (formato XML) OR etree.Element 245 """ 246 if not excluded_cols: 247 excluded_cols = list() 248 249 atts_xml = None 250 d_xml_elems = self.xml_elems() 251 if atts_root_xml: 252 atts_xml = OrderedDict({att.lower(): d_xml_elems[att.lower()] 253 for att in atts_root_xml}) 254 255 tab_elem = etree.Element(nom_root_xml.lower(), atts_xml) 256 excluded_cols = [c.lower() for c in excluded_cols] 257 for tag, xml_val in d_xml_elems.items(): 258 if tag in excluded_cols: 259 continue 260 261 try: 262 camp_elem = etree.SubElement(tab_elem, tag.lower()) 263 camp_elem.text = xml_val 264 except: 265 print("!ERROR! - No se ha podido añadir el campo '", 266 tag, "' al XML", sep="") 267 268 ret = tab_elem 269 270 if not as_etree_elem: 271 ret = etree.tostring(tab_elem, encoding="unicode") 272 273 return ret 274 275 def xml_elems(self): 276 """ 277 Itera los campos del registro y devuelve su nombre y su valor parseado para XML 278 279 Yields: 280 n_camp, val_camp 281 """ 282 d_xml_elems = dict() 283 for camp, val in self.vals().items(): 284 ret_val = "" 285 if isinstance(val, datetime.datetime): 286 ret_val = val.strftime("%Y-%m-%dT%H:%M:%S") 287 elif isinstance(val, m_sdo_geom.sdo_geom): 288 ret_val = val.as_wkt() 289 elif val: 290 ret_val = str(val) 291 292 d_xml_elems[camp.lower()] = ret_val 293 294 return d_xml_elems 295 296 def as_json(self): 297 """ 298 Retorna la fila como JSON con las geometrias en formato GeoJson 299 300 Returns: 301 str con la fila en formato json 302 """ 303 return json.dumps(self.vals(), 304 ensure_ascii=False, 305 cls=geojson_encoder) 306 307 def as_geojson(self): 308 """ 309 Retorna la fila como GEOJSON 310 311 Returns: 312 str con la fila en formato geojson 313 """ 314 return json.dumps(self.__geo_interface__, 315 ensure_ascii=False, 316 cls=geojson_encoder) 317 318 @property 319 def __geo_interface__(self): 320 """ 321 GeoJSON-like protocol for geo-spatial para toda una fila devuelta por query SQL 322 323 Si la fila tiene varias geometrias devolverá geojson como "GeometryCollection" 324 325 Returns: 326 dict con las geometrias y campos alfanuméricos (properties de Feature) en formato geojson 327 """ 328 geos = dict(**self.sdo_geoms) 329 num_geos = len(geos) 330 geom = None 331 if num_geos == 1: 332 geom = next(iter(geos.values())) 333 elif num_geos > 1: 334 geom = {"type": "GeometryCollection", 335 "geometries": list(geos.values())} 336 337 if geom: 338 return dict({"type": "Feature", 339 "geometry": geom, 340 "properties": {nc: val for nc, val in self.vals().items() if nc not in geos}}) 341 342 @property 343 def sdo_geoms(self): 344 """ 345 Devuelve diccionario indexado por los nombres de columnas que tienen valor geometrico de la clase sdo_geom 346 347 Returns: 348 dict {nom_column:sdo_geom} 349 """ 350 return {nc: g.__geo_interface__ 351 for nc, g in self.vals().items() if isinstance(g, m_sdo_geom.sdo_geom)} 352 353 def geometries(self, as_format=None): 354 """ 355 Itera por los campos geométricos informados 356 357 Args: 358 as_format (default=None): Se puede informar con nombre funcion 'as_XXXX' a la que responda SDO_GEOM 359 360 Yields: 361 nom_camp, geom 362 """ 363 for ng, g in self.vals().items(): 364 if isinstance(g, m_sdo_geom.sdo_geom): 365 g_val = g 366 if as_format: 367 g_val = getattr(g, as_format)() 368 369 yield ng, g_val 370 371 @property 372 def cols_pts_angles(self): 373 """ 374 Devuelve diccionario de campos angulo para geometrias puntuales con la clave el nombre del campo angulo y 375 el valor el nombre del campo puntual al que hace referencia 376 377 Returns: 378 dict 379 """ 380 sufix_ang = "_ANG" 381 cols = self.cols 382 cols_ang = {} 383 for c in (c for c, gd in self.geoms_vals().items() if gd.GTYPE.endswith('POINT')): 384 col_ang = x_sql_parser.get_nom_obj_sql(c, sufix=sufix_ang) 385 if col_ang in cols: 386 cols_ang[col_ang] = c 387 388 return cols_ang 389 390 @property 391 def cols(self): 392 """ 393 Devuelve lista con el nombre de la columnas de la fila 394 395 Returns: 396 list con nombres columnas 397 """ 398 return self._fields 399 400 return row_cursor
Retorna clase de fila por defecto (class row_cursor) para un cursor
Arguments:
- cursor_desc: (opcional) clase de la que se quiera heredar
- con_db: conexión cx_Oracle
Returns:
class (default = row_cursor)
403def get_pk_tab(con_db, nom_tab_or_view): 404 """ 405 Retorna la PRIMARY KEY de una tabla o vista de Oracle 406 407 Args: 408 con_db: 409 nom_tab_or_view: 410 411 Returns: 412 lista nombre columnas clave 413 """ 414 nom_pk_cache = get_nom_conexion(con_db) + ":" + nom_tab_or_view.upper() 415 noms_camps_pk = __cache_pks_tab.get(nom_pk_cache) 416 if not noms_camps_pk: 417 sql_keys = "SELECT cols.column_name " \ 418 "FROM user_constraints cons, user_cons_columns cols " \ 419 "WHERE cols.table_name = :1 " \ 420 "AND cons.constraint_type = 'P' " \ 421 "AND cons.constraint_name = cols.constraint_name " \ 422 "AND cons.owner = cols.owner " \ 423 "ORDER BY cols.table_name, cols.position" 424 425 noms_camps_pk = [fila_val.COLUMN_NAME for fila_val in 426 iter_execute_fetch_sql(con_db, sql_keys, nom_tab_or_view.upper())] 427 __cache_pks_tab[nom_pk_cache] = noms_camps_pk 428 429 return noms_camps_pk
Retorna la PRIMARY KEY de una tabla o vista de Oracle
Arguments:
- con_db:
- nom_tab_or_view:
Returns:
lista nombre columnas clave
432def get_tips_geom_tab(con_db, nom_tab_or_view): 433 """ 434 Retorna diccionario con nombre del campo como indice y los tipos de campos geométricos 435 (class_tip_geom) registrados en la global __class_tips_geom_ora como clases 'c_desc_tip_geom_?' siendo ? 436 el LAYER_GTYPE del indice de Oracle 437 438 Args: 439 con_db: 440 nom_tab_or_view: 441 442 Returns: 443 dict indexado por nombre columnas geometria y la clase de geometria (__class_tips_geom_ora) 444 """ 445 nom_con = get_nom_conexion(con_db) 446 447 tips_geom_con = __cache_tips_geom_tab.get(nom_con) 448 if not tips_geom_con: 449 tips_geom_con = __cache_tips_geom_tab[nom_con] = {} 450 sql_tip_geom = "select /*+ result_cache */ " \ 451 " T_COLS.TABLE_NAME, " \ 452 " T_COLS.COLUMN_NAME, " \ 453 " V_IDX_META.SDO_LAYER_GTYPE GTYPE, " \ 454 " V_G_META.SRID " \ 455 "from user_tab_columns t_cols," \ 456 " user_sdo_geom_metadata v_g_meta," \ 457 " user_sdo_index_info v_idx_info," \ 458 " user_sdo_index_metadata v_idx_meta " \ 459 "where v_g_meta.table_name = t_cols.table_name and " \ 460 "v_g_meta.column_name = t_cols.column_name and" \ 461 " v_idx_info.table_name = t_cols.table_name and " \ 462 "v_idx_info.column_name = t_cols.column_name and" \ 463 " v_idx_meta.sdo_index_name = v_idx_info.index_name" 464 465 for reg_tip_geom in iter_execute_fetch_sql(con_db, sql_tip_geom): 466 nom_cache = reg_tip_geom.TABLE_NAME.upper() 467 cache_tips_geom = tips_geom_con.get(nom_cache) 468 if not cache_tips_geom: 469 cache_tips_geom = {} 470 tips_geom_con[nom_cache] = cache_tips_geom 471 472 tip_geom = class_tip_geom(reg_tip_geom.GTYPE)(*reg_tip_geom.vals().values()) 473 474 nom_camp_geom = reg_tip_geom.COLUMN_NAME.upper() 475 cache_tips_geom[nom_camp_geom] = tip_geom 476 477 tips_geom = tips_geom_con.get(nom_tab_or_view.upper()) 478 479 return tips_geom if tips_geom else {}
Retorna diccionario con nombre del campo como indice y los tipos de campos geométricos (class_tip_geom) registrados en la global __class_tips_geom_ora como clases 'c_desc_tip_geom_?' siendo ? el LAYER_GTYPE del indice de Oracle
Arguments:
- con_db:
- nom_tab_or_view:
Returns:
dict indexado por nombre columnas geometria y la clase de geometria (__class_tips_geom_ora)
482def get_row_desc_tab(con_db, nom_tab_or_view): 483 """ 484 Retorna el descriptor (cursor con los tipos de campo para cada columna) de una tabla o vista de Oracle 485 486 Args: 487 con_db: 488 nom_tab_or_view: 489 490 Returns: 491 namedtuple ó clase fila con las columnas y sus tipos 492 """ 493 nom_dd_cache = get_nom_conexion(con_db) + ":" + nom_tab_or_view.upper() 494 tab_desc = __cache_row_desc_tab.get(nom_dd_cache) 495 if not tab_desc: 496 row_class_tab = get_row_class_tab(con_db, nom_tab_or_view) 497 498 camps_dd = row_class_tab.ora_descriptor 499 dict_camps = camps_dd._asdict() 500 dict_camps_geom = row_class_tab.geoms() 501 if dict_camps_geom: 502 for nom_camp, tip_camp_geom in dict_camps_geom.items(): 503 dict_camps[nom_camp] = tip_camp_geom 504 505 tab_desc = row_class_tab(*dict_camps.values()) 506 __cache_row_desc_tab[nom_dd_cache] = tab_desc 507 508 return tab_desc
Retorna el descriptor (cursor con los tipos de campo para cada columna) de una tabla o vista de Oracle
Arguments:
- con_db:
- nom_tab_or_view:
Returns:
namedtuple ó clase fila con las columnas y sus tipos
511def get_tip_camp(con_db, nom_tab_or_view, nom_camp): 512 """ 513 Devuelve el tipo de campo para la tabla_vista y campo especificado. En el caso de campos alfanuméricos 514 devuelve los tipos de cx_Oracle relacionados (cx_Oracle.NUMBER, cx_Oracle.STRING, ...) y para las 515 geométricas del tipo CLASS_TIP_GEOM 516 517 Args: 518 con_db: 519 nom_tab_or_view: 520 nom_camp: 521 522 Returns: 523 object: (cx_Oracle.NUMBER, cx_Oracle.STRING, ...) o si geometria tipo en __class_tips_geom_ora 524 """ 525 nom_tab_or_view = nom_tab_or_view.upper() 526 nom_camp = nom_camp.upper() 527 528 dd_tab = get_row_desc_tab(con_db, nom_tab_or_view) 529 if dd_tab: 530 return getattr(dd_tab, nom_camp, None)
Devuelve el tipo de campo para la tabla_vista y campo especificado. En el caso de campos alfanuméricos devuelve los tipos de cx_Oracle relacionados (cx_Oracle.NUMBER, cx_Oracle.STRING, ...) y para las geométricas del tipo CLASS_TIP_GEOM
Arguments:
- con_db:
- nom_tab_or_view:
- nom_camp:
Returns:
object: (cx_Oracle.NUMBER, cx_Oracle.STRING, ...) o si geometria tipo en __class_tips_geom_ora
533def sql_tab(nom_tab_or_view, filter_sql=None, columns=None): 534 """ 535 Devuelve sql para tabla o vista 536 537 Args: 538 nom_tab_or_view: 539 filter_sql: 540 columns (list): Lista nombre de columnas a mostrar. Default '*' (todas) 541 542 Returns: 543 str: con select sql para tabla o vista 544 """ 545 cols = "*" 546 if columns: 547 cols = ",".join(columns) 548 549 sql_tab = "select {cols} from {taula} TAB".format( 550 taula=nom_tab_or_view, 551 cols=cols) 552 553 if filter_sql: 554 sql_tab += " where " + filter_sql 555 556 return sql_tab
Devuelve sql para tabla o vista
Arguments:
- nom_tab_or_view:
- filter_sql:
- columns (list): Lista nombre de columnas a mostrar. Default '*' (todas)
Returns:
str: con select sql para tabla o vista
559def get_row_class_tab(con_db, nom_tab_or_view): 560 """ 561 Retorna clase que hereda de namedtuple para instanciar con los valores de un nuevo registro 562 563 Args: 564 con_db: 565 nom_tab_or_view: 566 567 Returns: 568 object: clase que hereda del namedtuple para una fila 569 """ 570 nom_tab_or_view = nom_tab_or_view.upper() 571 nom_row_class_tab_cache = get_nom_conexion(con_db) + ":" + nom_tab_or_view 572 row_class_tab = __cache_row_class_tab.get(nom_row_class_tab_cache) 573 574 if not row_class_tab: 575 curs = con_db.cursor() 576 curs.execute(sql_tab(nom_tab_or_view)) 577 ora_desc = get_row_descriptor(curs, nom_tab_or_view) 578 579 row_cursor_cls = get_row_class_cursor(ora_desc, con_db) 580 581 class row_table(row_cursor_cls): 582 """ 583 Clase para fila devuelta por SQL sobre tabla 584 """ 585 nom_tab = nom_tab_or_view 586 587 def __repr__(self): 588 """ 589 built_in que actua cuando se representa clase como STRING 590 591 Returns: 592 str 593 """ 594 repr_txt = nom_tab_or_view + "({pk_vals})" 595 pk_txt = [] 596 for i, v in self.pk_vals().items(): 597 pk_txt.append(str(i) + "=" + str(v)) 598 599 return repr_txt.format(pk_vals=",".join(pk_txt)) 600 601 @staticmethod 602 def pk(): 603 """ 604 Retorna lista con los nombres de campos clave 605 606 Returns: 607 list con nombres campos clave 608 """ 609 return get_pk_tab(con_db, nom_tab_or_view) 610 611 def pk_vals(self): 612 """ 613 Retorna OrderedDict con los pares nombres:valor para los campos clave 614 615 Returns: 616 OrderedDict 617 """ 618 pk_vals = OrderedDict.fromkeys(self.pk()) 619 for k in self.pk(): 620 pk_vals[k] = self.vals().get(k) 621 622 return pk_vals 623 624 @staticmethod 625 def geoms(): 626 """ 627 Devuelve diccionario {nom_camp_geom:tip_geom} para todas las columnas de la tabla que son de 628 tipo geométrico. 629 Lo deduce de la definición de la tabla independientemente de si la columna está informada o no 630 631 Returns: 632 dict {nom_geom:tip_geom} (vease funcion clas_tip_geom()) 633 """ 634 return get_tips_geom_tab(con_db, nom_tab_or_view) 635 636 def geoms_vals(self): 637 """ 638 Valores de los campos geometricos 639 640 Returns: 641 dict 642 """ 643 vals = self.vals() 644 return {nc: vals[nc] for nc in self.geoms()} 645 646 def as_xml(self, excluded_cols=[], 647 as_etree_elem=False): 648 """ 649 Devuelve row como XML 650 651 Args: 652 excluded_cols: lista de nombre de columnas que se quieren excluir 653 as_etree_elem {bool} (default=False): Si True devuelve registro como objeto etree.Element. Si False (por defecto) como str en formato XML 654 655 Returns: 656 str (formato XML) OR etree.Element 657 """ 658 return super(row_table, self).as_xml(nom_tab_or_view, 659 self.pk_vals(), 660 excluded_cols, 661 as_etree_elem) 662 663 @staticmethod 664 def get_row_desc(): 665 return get_row_desc_tab(con_db, nom_tab_or_view) 666 667 @staticmethod 668 def alfas(include_pk=True): 669 """ 670 Devuelve diccionario {nom_camp:tip_camp} para las columnas alfanuméricas (NO son geométricas) 671 Lo deduce de la definición de la tabla independientemente de si la columna está informada o no 672 673 Args: 674 include_pk: incluir primary key 675 676 Returns: 677 dict {nom_camp:tip_camp} 678 """ 679 return {nc: tc 680 for nc, tc in get_row_desc_tab(con_db, nom_tab_or_view).vals().items() 681 if nc not in get_tips_geom_tab(con_db, nom_tab_or_view) and 682 (include_pk or nc not in get_pk_tab(con_db, nom_tab_or_view))} 683 684 def alfas_vals(self): 685 """ 686 Devuelve diccionario con valores campos alfanuméricos 687 688 Returns: 689 dict {nom_camp:val_camp} 690 """ 691 vals = self.vals() 692 return {nc: vals[nc] for nc in self.alfas()} 693 694 row_class_tab = row_table 695 __cache_row_class_tab[nom_row_class_tab_cache] = row_class_tab 696 697 return row_class_tab
Retorna clase que hereda de namedtuple para instanciar con los valores de un nuevo registro
Arguments:
- con_db:
- nom_tab_or_view:
Returns:
object: clase que hereda del namedtuple para una fila
700def get_row_factory(curs, a_row_class=None): 701 """ 702 Retorna funcion para crear instancia clase a partir de los valores de una fila 703 704 Args: 705 curs: 706 a_row_class: 707 708 Returns: 709 function 710 """ 711 if not a_row_class: 712 cursor_desc = get_row_descriptor(curs) 713 con_db = curs.connection 714 a_row_class = get_row_class_cursor(cursor_desc, con_db) 715 716 def row_factory_func(*vals_camps): 717 return a_row_class(*vals_camps) 718 719 return row_factory_func
Retorna funcion para crear instancia clase a partir de los valores de una fila
Arguments:
- curs:
- a_row_class:
Returns:
function
722def get_row_cursor(curs, rowfactory=None): 723 """ 724 Retorna fila como clase que construye el rowfactory. Por defecto si no se informa esta clase heredará 725 de 'namedtuple' con los campos como items más funcionalidad relacionada con la conexion y el descriptor de la fila 726 con los tipos de campo para cada columna 727 728 Args: 729 curs: 730 rowfactory: 731 732 Returns: 733 734 """ 735 if not rowfactory and not curs.rowfactory: 736 rowfactory = get_row_factory(curs) 737 738 if rowfactory: 739 curs.rowfactory = rowfactory 740 741 row = curs.fetchone() 742 743 return row
Retorna fila como clase que construye el rowfactory. Por defecto si no se informa esta clase heredará de 'namedtuple' con los campos como items más funcionalidad relacionada con la conexion y el descriptor de la fila con los tipos de campo para cada columna
Arguments:
- curs:
- rowfactory:
Returns:
746def iter_execute_fetch_sql(con_db, sql_str, *args_sql, **extra_params): 747 """ 748 Itera y devuelve cada fila devuelta para la consulta sql_str 749 750 Args: 751 con_db: 752 sql_str: 753 *args_sql: 754 **extra_params: { "row_class": clase que se utilizará para cada fila. Vease get_row_factory() 755 "as_format": formato en el que se devuelve cada fila. Las clases base row_cursor y row_table 756 (vease get_row_class_cursor() y get_row_class_tab()) responden por defecto a 757 "as_xml()" y "as_json()" } 758 759 Returns: 760 row_class o string en formato especificado en **extra_params["as_format"] 761 """ 762 curs = None 763 764 try: 765 curs = new_cursor(con_db, 766 input_handler=extra_params.pop("input_handler", 767 m_sdo_geom.get_sdo_input_handler()), 768 output_handler=extra_params.pop("output_handler", 769 m_sdo_geom.get_output_handler( 770 con_db, 771 extra_params.pop("geom_format", None)))) 772 773 curs.execute(sql_str, 774 args_sql) 775 776 row_class = extra_params.pop("row_class", None) 777 row_factory = None 778 if row_class: 779 row_factory = get_row_factory(curs, row_class) 780 781 reg = get_row_cursor(curs, rowfactory=row_factory) 782 while reg is not None: 783 if "as_format" in extra_params: 784 f_format = extra_params["as_format"] 785 if f_format: 786 reg = getattr(reg, f_format)() 787 788 yield reg 789 790 reg = get_row_cursor(curs) 791 792 except: 793 if curs is not None: 794 curs.close() 795 raise 796 797 if curs is not None: 798 curs.close()
Itera y devuelve cada fila devuelta para la consulta sql_str
Arguments:
- con_db:
- sql_str:
- *args_sql:
- **extra_params: { "row_class": clase que se utilizará para cada fila. Vease get_row_factory() "as_format": formato en el que se devuelve cada fila. Las clases base row_cursor y row_table (vease get_row_class_cursor() y get_row_class_tab()) responden por defecto a "as_xml()" y "as_json()" }
Returns:
row_class o string en formato especificado en **extra_params["as_format"]
801def execute_fetch_sql(con_db, sql_str, *args_sql, **extra_params): 802 """ 803 Devuelve la primera iteración sobre la consulta sql_str 804 805 Args: 806 con_db: 807 sql_str: 808 *args_sql: 809 **extra_params: { "row_class": clase que se utilizará para cada fila. Vease get_row_factory() 810 "as_format": formato en el que se devuelve cada fila. Las clases base row_cursor y row_table 811 (vease get_row_class_cursor() y get_row_class_tab()) responden por defecto a 812 "as_xml()" y "as_json()" } 813 814 Returns: 815 row_class o string en formato especificado en **extra_params["as_format"] 816 """ 817 reg = None 818 for row in iter_execute_fetch_sql(con_db, 819 sql_str, 820 *args_sql, 821 input_handler=extra_params.pop("input_handler", None), 822 output_handler=extra_params.pop("output_handler", None), 823 row_class=extra_params.pop("row_class", None), 824 **extra_params): 825 reg = row 826 break 827 828 return reg
Devuelve la primera iteración sobre la consulta sql_str
Arguments:
- con_db:
- sql_str:
- *args_sql:
- **extra_params: { "row_class": clase que se utilizará para cada fila. Vease get_row_factory() "as_format": formato en el que se devuelve cada fila. Las clases base row_cursor y row_table (vease get_row_class_cursor() y get_row_class_tab()) responden por defecto a "as_xml()" y "as_json()" }
Returns:
row_class o string en formato especificado en **extra_params["as_format"]
831def dict_as_sql_bind_and_params(dict_vals, bool_rel="and", filter_oper="="): 832 """ 833 A partir de un dict de {nom_camps:value_camps} devuelve un string en forma 834 de SQL FILTER bindind (camp_a = :1 and camp_b = :2) y la lista de params que se asignarán via binding 835 El BOOL_REL podrá ser 'AND', 'OR' o ',' para el SET de los updates 836 El FILTER_OPER podrá ser '=', '!=' o ':=' para el SET de los updates 837 838 Args: 839 dict_vals: 840 bool_rel: 841 filter_oper: 842 843 Returns: 844 str, [params*] 845 """ 846 query_elems = {} 847 for nom_camp, val_clau in dict_vals.items(): 848 sql_str = str(nom_camp) + " " + filter_oper + " :" + str(nom_camp) 849 query_elems[sql_str] = val_clau 850 851 sql = (" " + bool_rel.strip().upper() + " ").join(query_elems.keys()) 852 853 return sql, list(query_elems.values())
A partir de un dict de {nom_camps:value_camps} devuelve un string en forma de SQL FILTER bindind (camp_a = :1 and camp_b = :2) y la lista de params que se asignarán via binding El BOOL_REL podrá ser 'AND', 'OR' o ',' para el SET de los updates El FILTER_OPER podrá ser '=', '!=' o ':=' para el SET de los updates
Arguments:
- dict_vals:
- bool_rel:
- filter_oper:
Returns:
str, [params*]
856class SerializableGenerator(list): 857 """Generator that is serializable by JSON 858 859 It is useful for serializing huge data by JSON 860 It can be used in a generator of json chunks used e.g. for a stream 861 ('[1', ']') 862 # >>> for chunk in iter_json: 863 # ... stream.write(chunk) 864 # >>> SerializableGenerator((x for x in range(3))) 865 # [<generator object <genexpr> at 0x7f858b5180f8>] 866 """ 867 868 def __init__(self, iterable): 869 super().__init__() 870 tmp_body = iter(iterable) 871 try: 872 self._head = iter([next(tmp_body)]) 873 self.append(tmp_body) 874 except StopIteration: 875 self._head = [] 876 877 def __iter__(self): 878 return itertools.chain(self._head, *self[:1])
Generator that is serializable by JSON
It is useful for serializing huge data by JSON It can be used in a generator of json chunks used e.g. for a stream ('[1', ']')
>>> for chunk in iter_json:
... stream.write(chunk)
>>> SerializableGenerator((x for x in range(3)))
[ at 0x7f858b5180f8>]
881def geojson_from_gen_ora_sql(generator_sql, as_string=False): 882 """ 883 Devuelve diccionario geojson para un generator de rows de Oracle 884 885 Args: 886 generator_sql (function generator): 887 as_string (bool): (opcional) indica si se querrá el geojson como un string 888 889 Returns: 890 geojson (dict ó str) 891 """ 892 vals = {"type": "FeatureCollection", 893 "features": [getattr(r, "__geo_interface__") 894 for r in generator_sql 895 if getattr(r, "__geo_interface__")]} 896 897 ret = vals 898 if as_string: 899 ret = json.dumps(vals, 900 ensure_ascii=False) 901 902 return ret
Devuelve diccionario geojson para un generator de rows de Oracle
Arguments:
- generator_sql (function generator):
- as_string (bool): (opcional) indica si se querrá el geojson como un string
Returns:
geojson (dict ó str)
905def vector_file_from_gen_ora_sql(file_path, vector_format, func_gen, zipped=False, indent_json=None, cols_csv=None, 906 tip_cols_csv=None): 907 """ 908 A partir del resultado de una query SQL devuelve un file_object formateado segun formato 909 910 Args: 911 file_path (str): path del fichero a grabar 912 vector_format (str): tipo formato (CSV, JSON, GEOJSON) 913 func_gen (generator function): funcion que devuelva filas sql en forma de row_cursor o row_table 914 (vease funciones generator_rows_sql() o generator_rows_table()) 915 zipped (bool=False): Devuelve fichero en un fichero comprimido (.zip) 916 indent_json (int): 917 cols_csv (list): Lista de columnes del CSV 918 tip_cols_csv (list): Lista de tipos de columnas para CSV. 919 Revisar especificacion en https://giswiki.hsr.ch/GeoCSV 920 Returns: 921 str: pathfile del fichero generado 922 """ 923 vector_format = vector_format.lower() 924 newline = None if vector_format != "csv" else "" 925 file_path_csvt = None 926 str_csvt = None 927 dir_base = os.path.dirname(file_path) 928 if dir_base: 929 os.makedirs(dir_base, exist_ok=True) 930 931 # Se hace en memoria o recursos locales (SpooledTemporaryFile) para evitar trabajar lo minimo en la red 932 # si se da el caso en el path fichero del fichero indicado. Cuando se quiere grabar en recurso local el tiempo 933 # perdido por utilizar un fichero temporal debería ser despreciable comparado con la complejidad añadida al código 934 # para decidir si usar o no el SpooledTemporaryFile 935 with SpooledTemporaryFile(mode="w+", encoding="utf-8", newline=newline) as temp_file: 936 if vector_format == "geojson": 937 json.dump(geojson_from_gen_ora_sql(func_gen), 938 temp_file, 939 ensure_ascii=False, 940 indent=indent_json, 941 cls=geojson_encoder) 942 elif vector_format == "json": 943 json.dump(SerializableGenerator((r.vals() for r in func_gen)), 944 temp_file, 945 ensure_ascii=False, 946 indent=indent_json, 947 cls=geojson_encoder) 948 elif vector_format == "csv": 949 writer = csv.DictWriter(temp_file, fieldnames=cols_csv) 950 writer.writeheader() 951 952 for r in func_gen: 953 writer.writerow(r.vals()) 954 955 temp_file.seek(0) 956 if tip_cols_csv: 957 file_path_csvt = ".".join((os.path.splitext(file_path)[0], "csvt")) 958 str_csvt = ",".join(tip_cols_csv) 959 960 if zipped: 961 file_path_res = "{}.zip".format(os.path.splitext(file_path)[0]) 962 with SpooledTemporaryFile() as zip_temp_file: 963 with ZipFile(zip_temp_file, "w", compression=ZIP_DEFLATED, allowZip64=True) as my_temp_zip: 964 my_temp_zip.writestr(zinfo_or_arcname=os.path.basename(file_path), data=temp_file.read()) 965 if str_csvt: 966 my_temp_zip.writestr(zinfo_or_arcname=os.path.basename(file_path_csvt), data=str_csvt) 967 968 zip_temp_file.seek(0) 969 with open(file_path_res, mode="wb") as file_res: 970 shutil.copyfileobj(zip_temp_file, file_res) 971 else: 972 file_path_res = file_path 973 with open(file_path_res, mode="w", encoding="utf-8") as file_res: 974 shutil.copyfileobj(temp_file, file_res) 975 if str_csvt: 976 with open(file_path_csvt, mode="w") as csvt_file: 977 csvt_file.write(str_csvt) 978 979 return file_path_res
A partir del resultado de una query SQL devuelve un file_object formateado segun formato
Arguments:
- file_path (str): path del fichero a grabar
- vector_format (str): tipo formato (CSV, JSON, GEOJSON)
- func_gen (generator function): funcion que devuelva filas sql en forma de row_cursor o row_table (vease funciones generator_rows_sql() o generator_rows_table())
- zipped (bool=False): Devuelve fichero en un fichero comprimido (.zip)
- indent_json (int):
- cols_csv (list): Lista de columnes del CSV
- tip_cols_csv (list): Lista de tipos de columnas para CSV. Revisar especificacion en https://giswiki.hsr.ch/GeoCSV
Returns:
str: pathfile del fichero generado
982class geojson_encoder(json.JSONEncoder): 983 """ 984 Class Encoder to parser SDO_GEOM to GEOJSON 985 """ 986 __num_decs__ = 9 987 988 def default(self, obj_val): 989 """ 990 Redefine default para tratar las geometrias SDO_GEOM y convertirlas a geojson y las fechas a iso_format 991 Args: 992 obj_val: valor 993 994 Returns: 995 object encoded 996 """ 997 if isinstance(obj_val, m_sdo_geom.sdo_geom): 998 return obj_val.as_geojson() 999 elif isinstance(obj_val, (datetime.datetime, datetime.date)): 1000 return obj_val.isoformat() 1001 elif isinstance(obj_val, cx_Oracle.LOB): 1002 return obj_val.read() 1003 else: 1004 return json.JSONEncoder.default(self, obj_val)
Class Encoder to parser SDO_GEOM to GEOJSON
988 def default(self, obj_val): 989 """ 990 Redefine default para tratar las geometrias SDO_GEOM y convertirlas a geojson y las fechas a iso_format 991 Args: 992 obj_val: valor 993 994 Returns: 995 object encoded 996 """ 997 if isinstance(obj_val, m_sdo_geom.sdo_geom): 998 return obj_val.as_geojson() 999 elif isinstance(obj_val, (datetime.datetime, datetime.date)): 1000 return obj_val.isoformat() 1001 elif isinstance(obj_val, cx_Oracle.LOB): 1002 return obj_val.read() 1003 else: 1004 return json.JSONEncoder.default(self, obj_val)
Redefine default para tratar las geometrias SDO_GEOM y convertirlas a geojson y las fechas a iso_format
Arguments:
- obj_val: valor
Returns:
object encoded
1007def print_to_log_exception(a_type_exc=Exception, lanzar_exc=False): 1008 """ 1009 Decorator para imprimir en el log una excepción capturada por un metodo de la clase 1010 1011 Returns: 1012 function 1013 """ 1014 1015 def decor_print_log_exception(func): 1016 @wraps(func) 1017 def meth_wrapper(cls, *args, **kwargs): 1018 try: 1019 return func(cls, *args, **kwargs) 1020 except a_type_exc: 1021 error_type, error_instance, traceback = sys.exc_info() 1022 1023 error_msg = "Error al executar funció '{clas}.{func}()' \n" \ 1024 "Arguments: {args}\n" \ 1025 "Fitxer: {file}".format( 1026 file=inspect.getmodule(func).__file__, 1027 clas=cls.__class__.__name__, 1028 func=func.__name__, 1029 args=", ".join(["'{}'".format(arg) for arg in args] + 1030 ["'{}={}'".format(a, b) for a, b in kwargs.items()])) 1031 1032 if hasattr(error_instance, "output"): 1033 error_msg += "\n" \ 1034 "Output: {}".format(error_instance.output) 1035 1036 cls.print_log_exception(error_msg) 1037 if lanzar_exc: 1038 raise error_instance 1039 1040 return meth_wrapper 1041 1042 return decor_print_log_exception
Decorator para imprimir en el log una excepción capturada por un metodo de la clase
Returns:
function
1045class gestor_oracle(object): 1046 """ 1047 Clase que gestionará distintas conexiones a Oracle y facilitará operaciones sobre la BBDD 1048 """ 1049 tip_number = cx_Oracle.NUMBER 1050 tip_string = cx_Oracle.STRING 1051 tip_clob = cx_Oracle.CLOB 1052 tip_blob = cx_Oracle.BLOB 1053 tip_date = cx_Oracle.DATETIME 1054 tip_fix_char = cx_Oracle.FIXED_CHAR 1055 1056 __slots__ = 'nom_con_db', '__con_db__', '__user_con_db__', \ 1057 '__psw_con_db__', '__dsn_ora__', '__call_timeout__', '__schema_con_db__', 'logger' 1058 1059 def __init__(self, user_ora, psw_ora, dsn_ora, a_logger=None, call_timeout: int = None, schema_ora=None): 1060 """ 1061 Inicializa gestor de Oracle para una conexion cx_Oracle a Oracle 1062 Se puede pasar por parametro un logger o inicializar por defecto 1063 1064 Args: 1065 user_ora {str}: Usuario/schema Oracle 1066 psw_ora {str}: Password usuario 1067 dsn_ora {str}: DSN Oracle (Nombre instancia/datasource de Oracle 1068 según TSN o string tal cual devuelve cx_Oracle.makedsn()) 1069 call_timeout (int=None): miliseconds espera per transaccio 1070 a_logger: 1071 schema_ora(str=None): indicate scheme when it is different from the user, default schema = user 1072 """ 1073 self.__con_db__ = None 1074 self.__call_timeout__ = call_timeout 1075 self.logger = a_logger 1076 self.__set_logger() 1077 self.__set_conexion(user_ora, psw_ora, dsn_ora, schema_ora=schema_ora) 1078 1079 def __del__(self): 1080 """ 1081 Cierra la conexion al matar la instancia 1082 """ 1083 try: 1084 if hasattr(self, "__con_db__"): 1085 self.__con_db__.close() 1086 except: 1087 pass 1088 1089 def __repr__(self): 1090 """ 1091 built_in que actua cuando se representa clase como STRING 1092 1093 Returns: 1094 str 1095 """ 1096 repr_txt = "{}".format(self.nom_con_db) 1097 1098 return repr_txt 1099 1100 @staticmethod 1101 def log_dir(): 1102 """ 1103 Devuelve el directorio donde irán los logs indicado en la funcion apb_logging.logs_dir() 1104 1105 Returns: 1106 {str} - path del directorio de logs 1107 """ 1108 return utils_logging.logs_dir(True) 1109 1110 def log_name(self): 1111 """ 1112 Devuelve el nombre del fichero de log por defecto 1113 1114 Returns: 1115 {str} - Nombre fichero log por defecto 1116 """ 1117 return self.__class__.__name__ 1118 1119 def log_file_name(self): 1120 return "{}.(LOG_LEVEL).log".format(os.path.join(self.log_dir(), self.log_name())) 1121 1122 def __set_logger(self): 1123 """ 1124 Asigna el LOGGER po defecto si este no se ha informado al inicializar el gestor 1125 1126 Returns: 1127 """ 1128 if self.logger is None: 1129 self.logger = utils_logging.get_file_logger(self.log_name(), dir_log=self.log_dir()) 1130 1131 def path_logs(self, if_exist=True): 1132 """ 1133 Devuelve lista paths base de los logs vinculados al gestor 1134 1135 Args: 1136 if_exist (bool): Devuelve los paths si el fichero existe 1137 1138 Returns: 1139 list: 1140 """ 1141 return logger_path_logs(self.logger) 1142 1143 def print_log(self, msg): 1144 """ 1145 Sobre el logger escribe mensaje de info 1146 1147 Args: 1148 msg {str}: String con el mensaje 1149 """ 1150 self.logger.info(msg) 1151 1152 def print_log_error(self, msg): 1153 """ 1154 Sobre el logger escribe mensaje de error 1155 1156 Args: 1157 msg {str}: String con el mensaje 1158 """ 1159 self.logger.error(msg) 1160 1161 def print_log_exception(self, msg): 1162 """ 1163 Sobre el logger escribe excepcion 1164 1165 Args: 1166 msg {str}: String con el mensaje 1167 """ 1168 self.logger.exception(msg) 1169 1170 @print_to_log_exception(lanzar_exc=True) 1171 def __set_conexion(self, user_ora, psw_ora, dsn_ora, schema_ora=None): 1172 """ 1173 Añade conexion Oracle al gestor a partir de nombre de usuario/schema (user_ora), contraseña (psw_ora) y 1174 nombre datasource de la bbdd según tns_names (ds_ora). 1175 1176 La conexión quedará registrada como 'user_ora@ds_ora' 1177 1178 Args: 1179 user_ora {str}: Usuario/schema Oracle 1180 psw_ora {str}: Password usuario 1181 dsn_ora {str}: DSN Oracle (Nombre instancia/datasource de Oracle según TSN o string tal cual devuelve cx_Oracle.makedsn()) 1182 schema_ora(str=None): indicate scheme when it is different from the user, default schema = user 1183 1184 """ 1185 nom_con = "@".join((user_ora.upper(), dsn_ora.upper())) 1186 self.nom_con_db = nom_con 1187 self.__user_con_db__ = user_ora 1188 self.__psw_con_db__ = psw_ora 1189 self.__schema_con_db__ = schema_ora 1190 self.__dsn_ora__ = dsn_ora 1191 self.__con_db__ = get_oracle_connection(user_ora, psw_ora, dsn_ora, self.__call_timeout__, schema_ora=schema_ora) 1192 1193 @property 1194 @print_to_log_exception(cx_Oracle.Error, lanzar_exc=True) 1195 def con_db(self): 1196 """ 1197 Return a cx_Oracle Conection live 1198 1199 Returns: 1200 cx_Oracle.Connection 1201 """ 1202 reconnect = False 1203 if (con_ora := self.__con_db__) is not None: 1204 try: 1205 con_ora.ping() 1206 except cx_Oracle.Error as exc: 1207 # Borramos las entradas de cache asociadas a la conexión que no responde 1208 del_cache_rel_con_db(get_nom_conexion(con_ora)) 1209 try: 1210 con_ora.close() 1211 except cx_Oracle.Error: 1212 pass 1213 self.__con_db__ = con_ora = None 1214 1215 if con_ora is None: 1216 self.__set_conexion( 1217 self.__user_con_db__, 1218 self.__psw_con_db__, 1219 self.__dsn_ora__, schema_ora=self.__schema_con_db__) 1220 1221 con_ora = self.__con_db__ 1222 1223 return con_ora 1224 1225 @print_to_log_exception(cx_Oracle.DatabaseError) 1226 def exec_trans_db(self, sql_str, *args_sql, **types_sql_args): 1227 """ 1228 Ejecuta transaccion SQL 1229 1230 Args: 1231 sql_str (str): sql transaction (update, insert, delete} 1232 *args_sql: Lista argumentos a pasar 1233 **types_sql_args (OPCIONAL): Lista tipos cx_Oracle para cada argumento 1234 1235 Returns: 1236 ok {bool}: Si ha ido bien True si no False 1237 """ 1238 curs_db = None 1239 try: 1240 curs_db = new_cursor(self.con_db, 1241 input_handler=m_sdo_geom.get_sdo_input_handler()) 1242 1243 curs_db.setinputsizes(*types_sql_args.values()) 1244 1245 curs_db.execute(sql_str, 1246 args_sql) 1247 finally: 1248 if curs_db: 1249 curs_db.close() 1250 1251 return True 1252 1253 @print_to_log_exception(cx_Oracle.DatabaseError) 1254 def exec_script_plsql(self, sql_str): 1255 """ 1256 Ejecuta script SQL 1257 1258 Args: 1259 sql_str {str}: sql script 1260 1261 Returns: 1262 ok {bool}: Si ha ido bien True si no False 1263 """ 1264 curs_db = None 1265 try: 1266 curs_db = new_cursor(self.con_db) 1267 curs_db.execute(sql_str) 1268 finally: 1269 if curs_db: 1270 curs_db.close() 1271 1272 return True 1273 1274 @print_to_log_exception(cx_Oracle.DatabaseError) 1275 def callfunc_sql(self, nom_func, ret_cx_ora_tipo, *args_func): 1276 """ 1277 Ejecuta funcion PL/SQL y retorna el valor 1278 1279 Args: 1280 nom_func (str): Nombre de la funcion PL/SQL 1281 ret_cx_ora_tipo (cx_Oracle TIPO): El retorno de la función en cx_Oracle (cx_Oracle.NUMBER, 1282 cx_Oracle.STRING,...) 1283 *args_func: Argumentos de la funcion PL/SQL 1284 1285 Returns: 1286 Valor retornado por la función PL/SQL 1287 """ 1288 curs = None 1289 try: 1290 curs = new_cursor(self.con_db) 1291 ret = curs.callfunc(nom_func, 1292 ret_cx_ora_tipo, 1293 args_func) 1294 finally: 1295 if curs: 1296 curs.close() 1297 1298 return ret 1299 1300 @print_to_log_exception(cx_Oracle.DatabaseError) 1301 def callproc_sql(self, nom_proc, *args_proc): 1302 """ 1303 Ejecuta procedimiento PL/SQL 1304 1305 Args: 1306 nom_proc (str): Nombre del procedimiento PL/SQL 1307 *args_proc: Argumentos del procedimiento 1308 1309 Returns: 1310 ok {bool}: Si ha ido bien True si no False 1311 """ 1312 curs = None 1313 try: 1314 curs = new_cursor(self.con_db) 1315 curs.callproc(nom_proc, 1316 args_proc) 1317 finally: 1318 if curs: 1319 curs.close() 1320 1321 return True 1322 1323 @print_to_log_exception(cx_Oracle.DatabaseError) 1324 def row_sql(self, sql_str, *args_sql, **extra_params): 1325 """ 1326 Retorna la fila resultante de la query sql SQL_STR con los parámetros *ARGS_SQL. 1327 Se puede informar **EXTRA_PARAMS con algunos de los siguientes parámetros: 1328 INPUT_HANDLER funcion que tratará los bindings de manera específica 1329 OUTPUT_HANLER funcion que tratará los valores de las columnas de modo específico 1330 ROW_CLASS define que con que clase se devolverá cada fila. Por defecto la que devuleve la funcion 1331 'apb_cx_oracle_spatial.get_row_class_cursor()' 1332 AS_FORMAT (as_xml, as_json, as_geojson) devuelve fila en el formato especificado. La row_class deberá 1333 responder a esas funciones 1334 1335 Args: 1336 sql_str: 1337 *args_sql: 1338 **extra_params: 1339 1340 Returns: 1341 object (instancia que debería ser o heredar de las clases row_cursor o row_table) 1342 """ 1343 return execute_fetch_sql(self.con_db, 1344 sql_str, 1345 *args_sql, 1346 input_handler=extra_params.pop("input_handler", None), 1347 output_handler=extra_params.pop( 1348 "output_handler", 1349 m_sdo_geom.get_output_handler(self.con_db, 1350 extra_params.pop("geom_format", None))), 1351 row_class=extra_params.pop("row_class", None), 1352 **extra_params) 1353 1354 @print_to_log_exception(cx_Oracle.DatabaseError) 1355 def generator_rows_sql(self, sql_str, *args_sql, **extra_params): 1356 """ 1357 Ejecuta consulta SQL de forma iterativa retornando cada fila como un objeto row_class (por defecto row_cursor) 1358 1359 Se puede informar **EXTRA_PARAMS con algunos de los siguientes parámetros: 1360 INPUT_HANDLER funcion que tratará los bindings de manera específica 1361 OUTPUT_HANLER funcion que tratará los valores de las columnas de modo específico 1362 ROW_CLASS define que con que clase se devolverá cada fila. Por defecto la que devuleve la funcion 1363 'apb_cx_oracle_spatial.get_row_class_cursor()' 1364 AS_FORMAT (as_xml, as_json, as_geojson) devuelve fila en el formato especificado. La row_class deberá 1365 responder a esas funciones 1366 1367 Args: 1368 sql_str: 1369 *args_sql: 1370 **extra_params: 1371 1372 Returns: 1373 object (instancia que debería ser o heredar de las clases row_cursor o row_table) 1374 """ 1375 for reg in iter_execute_fetch_sql(self.con_db, 1376 sql_str, 1377 *args_sql, 1378 input_handler=extra_params.pop("input_handler", None), 1379 output_handler=extra_params.pop( 1380 "output_handler", 1381 m_sdo_geom.get_output_handler(self.con_db, 1382 extra_params.pop("geom_format", None))), 1383 row_class=extra_params.pop("row_class", None), 1384 **extra_params): 1385 yield reg 1386 1387 def rows_sql(self, sql_str, *args_sql): 1388 """ 1389 Vease funcion 'generator_rows_sql()' 1390 Args: 1391 sql_str: 1392 *args_sql: 1393 1394 Returns: 1395 list 1396 """ 1397 return list(self.generator_rows_sql(sql_str, *args_sql)) 1398 1399 def get_primary_key_table(self, nom_tab_or_view): 1400 """ 1401 Retorna lista con las columnas que conforman la primary key de una tabla/vista 1402 Args: 1403 nom_tab_or_view: 1404 1405 Returns: 1406 list con campos clave 1407 """ 1408 return get_pk_tab(self.con_db, nom_tab_or_view) 1409 1410 @print_to_log_exception(lanzar_exc=True) 1411 def get_dd_table(self, nom_tab_or_view): 1412 """ 1413 Retorna instancia row_table con los tipos para cada columna 1414 Args: 1415 nom_tab_or_view: 1416 1417 Returns: 1418 object de clase row_table con los tipos de cada columna como valores 1419 """ 1420 return get_row_desc_tab(self.con_db, nom_tab_or_view) 1421 1422 def generator_rows_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql, **extra_params): 1423 """ 1424 Retorna los registros que cumplan con el FILTER_SQL sobre la tabla o vista indicada. 1425 1426 La tabla se puede referenciar en el filtro con el alias 'TAB' 1427 1428 Si el FILTER_SQL utiliza valores por binding (:1, :2,...) estos se indicaran por orden en ARGS_FILTER_SQL 1429 1430 Args: 1431 nom_tab_or_view: Nombre de la tabla o vista sobre la que se hará la consulta 1432 filter_sql: Filtre sobre la taula 1433 args_filter_sql: Valors en ordre de binding per passar 1434 extra_params: Vease generator_rows_sql() 1435 1436 Yields: 1437 row_table: regs. clase row_table (mirar get_row_class_tab()) 1438 """ 1439 for reg in self.generator_rows_sql(sql_tab(nom_tab_or_view, 1440 filter_sql), 1441 *args_filter_sql, 1442 row_class=extra_params.pop( 1443 "row_class", 1444 get_row_class_tab(self.con_db, nom_tab_or_view)), 1445 **extra_params): 1446 yield reg 1447 1448 def generator_rows_interact_geom(self, nom_tab, a_sdo_geom, cols_geom=None, geom_format=None): 1449 """ 1450 Retorna las filas de una tabla que interactuan con una geometria 1451 1452 Args: 1453 nom_tab: nombre de la tabla 1454 a_sdo_geom: geometria clase sdo_geom 1455 cols_geom (default=None): Lista con nombre de columnas geométricas sobre las que se quiere aplicar filtro 1456 geom_format: 1457 Yields: 1458 row_table: regs. clase row_table (mirar get_row_class_tab()) 1459 """ 1460 # Uso de " <>'FALSE'" por fallo de Oracle usando "= 'TRUE'" 1461 filter_interact_base = "SDO_ANYINTERACT({camp_geom}, :1) <> 'FALSE'" 1462 1463 if not cols_geom: 1464 cols_geom = get_tips_geom_tab(self.con_db, nom_tab).keys() 1465 1466 if cols_geom: 1467 filter_sql = " OR ".join([filter_interact_base.format(camp_geom=ng) for ng in cols_geom]) 1468 for reg in self.generator_rows_table(nom_tab, filter_sql, a_sdo_geom.as_ora_sdo_geometry(), 1469 geom_format=geom_format): 1470 yield reg 1471 1472 def rows_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql): 1473 """ 1474 Vease funcion 'generator_rows_table()' 1475 1476 Args: 1477 nom_tab_or_view: 1478 filter_sql: 1479 *args_filter_sql: 1480 1481 Returns: 1482 dict 1483 """ 1484 gen_tab = self.generator_rows_table(nom_tab_or_view, 1485 filter_sql, 1486 *args_filter_sql) 1487 pk_tab = self.get_primary_key_table(nom_tab_or_view) 1488 l_pk = len(pk_tab) 1489 if l_pk == 0: 1490 return [r for r in gen_tab] 1491 else: 1492 def f_key(r): 1493 return getattr(r, pk_tab[0]) 1494 1495 if l_pk > 1: 1496 def f_key(r): return tuple(map(lambda nf: getattr(r, nf), pk_tab)) 1497 return {f_key(r): r for r in gen_tab} 1498 1499 def row_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql, **extra_params): 1500 """ 1501 Retorna primer registro para el FILTER_SQL sobre la tabla o vista indicada 1502 Si el FILTER_SQL utiliza valores por binding (:1, :2,...) estos se indicaran por orden en ARGS_FILTER_SQL 1503 1504 Args: 1505 nom_tab_or_view: 1506 filter_sql: 1507 *args_filter_sql: 1508 **extra_params: 1509 1510 Returns: 1511 object de la clase row_table o especificada en **extra_params['row_class'] 1512 """ 1513 gen = self.generator_rows_table(nom_tab_or_view, 1514 filter_sql, 1515 *args_filter_sql, 1516 **extra_params) 1517 return next(gen, None) 1518 1519 def row_table_at(self, nom_tab_or_view, *vals_key): 1520 """ 1521 Devuelve row_tabla_class para el registro que de la tabla_vista que cumpla con la clave 1522 1523 Args: 1524 nom_tab_or_view: 1525 *vals_key: 1526 1527 Returns: 1528 object de la clase row_table o especificada en **extra_params['row_class'] 1529 """ 1530 return self.exist_row_tab(nom_tab_or_view, 1531 {nc: val for nc, val in zip(self.get_primary_key_table(nom_tab_or_view), 1532 vals_key)}) 1533 1534 def test_row_table(self, row_tab, a_sql, *args_sql): 1535 """ 1536 Testea un registro de tabla (clase rwo_table) cumpla con sql indicado 1537 1538 Args: 1539 row_tab: registro de tabla en forma de clase row_table 1540 a_sql: string con sql a testear 1541 1542 Returns: 1543 bool: True o False según cumpla con el SQL indicado 1544 """ 1545 sql_pk = dict_as_sql_bind_and_params(row_tab.pk_vals()) 1546 query_sql = "{} AND ({})".format(sql_pk[0], a_sql) 1547 1548 ret = False 1549 if self.row_table(row_tab.nom_tab, query_sql, *(tuple(sql_pk[1]) + tuple(args_sql))): 1550 ret = True 1551 1552 return ret 1553 1554 def insert_row_tab(self, nom_tab, dict_vals_param=None, dict_vals_str=None, pasar_nulls=False): 1555 """ 1556 Inserta registro en la tabla indicada. Los valores para cada columna se pasarán a través de dict_vals_param 1557 como bindings o a través de dict_vals_str directemente en el string del sql ejecutado 1558 Args: 1559 nom_tab: nombre de la tabla 1560 dict_vals_param: diccionario indexado por columnas-valores. Los valores se pasarán como bindings 1561 dict_vals_str: diccionario indexado por columnas-valores a asignar como strings. El valor se pasa 1562 directamente como asignacion en la senetencia sql 1563 pasar_nulls: (opcional) por defecto False. Indica si los valores NULL se asignarán o no 1564 1565 Returns: 1566 row_table (si genera el registro) o False si va mal la operación 1567 """ 1568 if not dict_vals_param: 1569 dict_vals_param = {} 1570 if not dict_vals_str: 1571 dict_vals_str = {} 1572 1573 ora_dict_vals_param = self.get_vals_tab_for_transdb(nom_tab, dict_vals_param, pasar_nulls=pasar_nulls) 1574 1575 if pasar_nulls: 1576 # Se revisa que en un campo geometrico se asigne None y por lo tanto se asigne valor via STR 1577 geoms_null = [ng for ng, val in ora_dict_vals_param.items() 1578 if not val and self.get_tip_camp_geom(nom_tab, ng)] 1579 if geoms_null: 1580 keys_str = [nc.upper() for nc in dict_vals_str.keys()] 1581 for gn in geoms_null: 1582 ora_dict_vals_param.pop(gn) 1583 if gn.upper() not in keys_str: 1584 dict_vals_str[gn.upper()] = "NULL" 1585 1586 params = [] 1587 nom_camps = [] 1588 vals_camps = [] 1589 for nom_camp, val_camp in ora_dict_vals_param.items(): 1590 if val_camp is None: 1591 continue 1592 nom_camps.append(nom_camp) 1593 vals_camps.append(":" + nom_camp) 1594 params.append(val_camp) 1595 1596 for nom_camp, val_camp_str in dict_vals_str.items(): 1597 if val_camp_str is None: 1598 continue 1599 nom_camps.append(nom_camp) 1600 vals_camps.append(str(val_camp_str)) 1601 1602 row_desc_tab = get_row_desc_tab(self.con_db, nom_tab) 1603 pk_binds = {k: new_cursor(self.con_db).var(ora_tip_camp) for k, ora_tip_camp in row_desc_tab.pk_vals().items()} 1604 if not pk_binds: 1605 pk_binds = {'ROWID': new_cursor(self.con_db).var(cx_Oracle.ROWID)} 1606 str_pk_camps = ",".join(pk_binds.keys()) 1607 str_pk_binds = ",".join(list(map(lambda x: ":ret_" + str(x), pk_binds.keys()))) 1608 params += list(pk_binds.values()) 1609 1610 a_sql_res = f"insert into {nom_tab}({','.join(nom_camps)}) values({','.join(vals_camps)}) " \ 1611 f"returning {str_pk_camps} into {str_pk_binds}" 1612 1613 ok = self.exec_trans_db(a_sql_res, *params) 1614 if ok: 1615 pk_vals = {k: curs_var.getvalue(0)[0] for k, curs_var in pk_binds.items()} 1616 return self.exist_row_tab(nom_tab, pk_vals) 1617 1618 return ok 1619 1620 def update_row_tab(self, nom_tab, dict_clau_reg, dict_vals_param=None, dict_vals_str=None, pasar_nulls=None): 1621 """ 1622 Actualiza registro en la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor} 1623 Los valores para cada columna se pasarán a través de dict_vals_param como bindings o a través de 1624 dict_vals_str directemente en el string del sql ejecutado 1625 1626 Args: 1627 nom_tab: nombre de la tabla 1628 dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar 1629 dict_vals_param: diccionario indexado por columnas-valores. Los valores se pasarán como bindings 1630 dict_vals_str: diccionario indexado por columnas-valores a asignar como strings. El valor se pasa 1631 directamente como asignacion en la senetencia sql 1632 pasar_nulls: (opcional) por defecto False. Indica si los valores NULL se asignarán o no 1633 1634 Returns: 1635 row_table (si genera el registro) o False si va mal la operación 1636 """ 1637 if not dict_vals_param: 1638 dict_vals_param = {} 1639 if not dict_vals_str: 1640 dict_vals_str = {} 1641 1642 ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg) 1643 ora_dict_vals_param = self.get_vals_tab_for_transdb(nom_tab, dict_vals_param, pasar_nulls=pasar_nulls) 1644 1645 if pasar_nulls: 1646 # Se revisa que en un campo geometrico se asigne None y por lo tanto se asigne valor via STR 1647 geoms_null = [ng for ng, val in ora_dict_vals_param.items() 1648 if not val and self.get_tip_camp_geom(nom_tab, ng)] 1649 if geoms_null: 1650 keys_str = [nc.upper() for nc in dict_vals_str.keys()] 1651 for gn in geoms_null: 1652 ora_dict_vals_param.pop(gn) 1653 if gn.upper() not in keys_str: 1654 dict_vals_str[gn.upper()] = "NULL" 1655 1656 (sql_set_camps, params_set_camps) = dict_as_sql_bind_and_params(ora_dict_vals_param, 1657 ",", "=") 1658 1659 (query_clau, params_filter) = dict_as_sql_bind_and_params(ora_dict_clau_reg) 1660 1661 params = params_set_camps + params_filter 1662 1663 for nom_camp, val_camp_str in dict_vals_str.items(): 1664 if sql_set_camps: 1665 sql_set_camps += " , " 1666 sql_set_camps += nom_camp + "=" + val_camp_str 1667 1668 ok = None 1669 if sql_set_camps: 1670 a_sql_res = f"update {nom_tab} set {sql_set_camps} where {query_clau}" 1671 1672 ok = self.exec_trans_db(a_sql_res, *params) 1673 1674 if ok: 1675 return self.exist_row_tab(nom_tab, dict_clau_reg) 1676 1677 return ok 1678 1679 def remove_row_tab(self, nom_tab, dict_clau_reg): 1680 """ 1681 Borra registro en la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor} 1682 1683 Args: 1684 nom_tab: nombre de la tabla 1685 dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar 1686 1687 Returns: 1688 bool según vaya la operación 1689 """ 1690 ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg) 1691 1692 a_sql_tmpl = "delete {nom_tab} where {query_clau}" 1693 1694 (sql_filter, params) = dict_as_sql_bind_and_params(ora_dict_clau_reg) 1695 1696 a_sql_res = a_sql_tmpl.format(nom_tab=nom_tab, 1697 query_clau=sql_filter) 1698 1699 return self.exec_trans_db(a_sql_res, *params) 1700 1701 def exist_row_tab(self, nom_tab, dict_clau_reg, **extra_params): 1702 """ 1703 Devuelve registro de la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor} 1704 1705 Args: 1706 nom_tab: nombre de la tabla 1707 dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar 1708 **extra_params: 1709 1710 Returns: 1711 object de la clase row_table o especificada en **extra_params['row_class'] 1712 """ 1713 ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg) 1714 1715 (filter_sql, params) = dict_as_sql_bind_and_params(ora_dict_clau_reg, "and", "=") 1716 1717 return self.row_table(nom_tab, filter_sql, *params, **extra_params) 1718 1719 def get_vals_tab_for_transdb(self, nom_tab, dict_camps_vals, pasar_nulls=True): 1720 """ 1721 Para un tabla y diccionario columnas-valores devuelve diccionario indexado por las columnas con los valores 1722 convertidos a formato cx_Oracle según tipo de cada columna en Oracle 1723 Args: 1724 nom_tab: nombre de la tabla 1725 dict_camps_vals: diccionario indexado por columnas-valor a convertir a formato cx_Oracle 1726 pasar_nulls: (opcional) por defecto convertirá los None a NULL de Oracle 1727 1728 Returns: 1729 dict indexado por columnas con los valores convertidos a tipo cx_Oracle 1730 """ 1731 dd_tab = get_row_desc_tab(self.con_db, nom_tab) 1732 1733 # Retorna dict con los campos a pasar por parametro 1734 d_params = {} 1735 1736 # Los nombres de campo siempre se buscarán en mayúsculas 1737 dict_camps_vals = {k.upper(): v for k, v in dict_camps_vals.items()} 1738 1739 for camp, tip_camp in dd_tab.vals().items(): 1740 if camp not in dict_camps_vals: 1741 continue 1742 1743 val_camp = dict_camps_vals.get(camp) 1744 if not pasar_nulls and val_camp is None: 1745 continue 1746 1747 if isinstance(val_camp, m_sdo_geom.sdo_geom): 1748 var = val_camp.as_ora_sdo_geometry() 1749 else: 1750 try: 1751 var = new_cursor(self.con_db).var(tip_camp) 1752 var.setvalue(0, val_camp) 1753 except: 1754 var = val_camp 1755 1756 d_params[camp] = var 1757 1758 return d_params 1759 1760 @print_to_log_exception() 1761 def run_sql_script(self, filename): 1762 """ 1763 Ejecuta slq script (filename) sobre SQLPLUS 1764 1765 Args: 1766 filename: path del sql script 1767 1768 Returns: 1769 1770 """ 1771 user_ora = self.con_db.username 1772 ds_ora = self.con_db.dsn 1773 nom_con = self.nom_con_db 1774 psw_ora = self.__psw_con_db__ 1775 if psw_ora is None: 1776 print("ERROR - Conexión '" + nom_con + "' no está añadida al gestor!!") 1777 return 1778 1779 with open(filename, 'rb') as a_file: 1780 a_sql_command = a_file.read() 1781 1782 con_db_str = user_ora + "/" + psw_ora + "@" + ds_ora 1783 sqlplus = Popen(['sqlplus', '-S', con_db_str], stdin=PIPE, stdout=PIPE, stderr=PIPE) 1784 # sqlplus.stdin.write(a_sql_command) 1785 1786 (stdout, stderr) = sqlplus.communicate(a_sql_command) 1787 1788 self.print_log("Resultado lanzar script '{}': \n" 1789 "{}".format(filename, 1790 stdout.decode("utf-8"))) 1791 1792 if sqlplus is not None: 1793 sqlplus.terminate() 1794 1795 @staticmethod 1796 def get_nom_obj_sql(nom_base, prefix="", sufix=""): 1797 """ 1798 Retorna nombre propuesto con prefijo/sufijos formateado para que cumpla longitud máxima de 32 caracteres en 1799 objetos sql Oracle 1800 1801 Args: 1802 nom_base: nombre propuesto 1803 prefix: (opc) prefijo 1804 sufix: (opc) sufijo 1805 1806 Returns: 1807 str formateado 1808 """ 1809 return x_sql_parser.get_nom_obj_sql(nom_base, prefix, sufix) 1810 1811 def iter_sdo_gtypes_vals_camp_tab(self, nom_taula, nom_camp): 1812 """ 1813 Retorna los distintos tipos de Geometria (codigo entero que define el tipo SDO_GTYPE) 1814 que se encuentran dentro de la columna sdo_geometry de una tabla 1815 1816 Args: 1817 nom_taula: nombre de la tabla 1818 nom_camp: nombre campo geometrico 1819 1820 Returns: 1821 int definiendo tipo de geometría 1822 """ 1823 sql_tip_geoms = f"select distinct(tab.{nom_camp}.Get_GType()) as tip_geom from {nom_taula} tab " \ 1824 f"where {nom_camp} is not null" 1825 1826 for reg in self.generator_rows_sql(sql_tip_geoms): 1827 yield reg.TIP_GEOM 1828 1829 def iter_distinct_vals_camp_tab(self, nom_taula, nom_camp, filter_sql=None): 1830 """ 1831 Retorna los distintos valores de la columna de una tabla 1832 1833 Args: 1834 nom_taula (str): Nombre de tabla 1835 nom_camp (str): Nombre de campo 1836 filter_sql(str): Filtro SQL sobre la tabla indicada 1837 1838 Returns: 1839 {str}: Itera los distintos valores encontrados en el campo indicado 1840 """ 1841 sql_distinct_vals = f"select distinct(tab.{nom_camp}) as VAL from {nom_taula} tab" 1842 if filter_sql: 1843 sql_distinct_vals += " where " + filter_sql 1844 1845 for reg in self.generator_rows_sql(sql_distinct_vals): 1846 yield reg.VAL 1847 1848 def get_tip_camp_geom(self, nom_tab_or_view, nom_camp_geom): 1849 """ 1850 Retorna el tipo de campo geométrico (class_tip_geom) registrados en la global __class_tips_geom_ora 1851 para el campo indicado 1852 1853 Args: 1854 nom_tab_or_view: nombre tabla/vista 1855 nom_camp_geom: nombre campo geom 1856 1857 Returns: 1858 namedtuple: con atributos ['TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID']) 1859 """ 1860 tips_geom_tab = get_tips_geom_tab(self.con_db, nom_tab_or_view) 1861 if tips_geom_tab: 1862 return tips_geom_tab.get(nom_camp_geom.upper()) 1863 1864 def get_epsg_for_srid(self, srid): 1865 """ 1866 Rertorna WKT con la definicion del SRID dado 1867 """ 1868 return self.callfunc_sql('SDO_CS.MAP_ORACLE_SRID_TO_EPSG', cx_Oracle.NUMBER, srid) 1869 1870 def get_gtype_camp_geom(self, nom_tab_or_view, nom_camp_geom): 1871 """ 1872 Retorna el tipo GTYPE (int) de la geometria 1873 1874 Args: 1875 nom_tab_or_view: nombre tabla/vista 1876 nom_camp_geom: nombre campo geom 1877 1878 Returns: 1879 int 1880 """ 1881 gtype = 0 1882 g_tip_ora = self.get_tip_camp_geom(nom_tab_or_view, nom_camp_geom) 1883 if g_tip_ora: 1884 gtype = GTYPES_ORA.index(g_tip_ora.GTYPE) 1885 1886 return gtype 1887 1888 @staticmethod 1889 def verificar_path_vector_file(nom_tab_or_view, dir, file_name, ext, zipped): 1890 """ 1891 Compone el/los path para el/los vector_file de una tabla y determina si exists 1892 Args: 1893 nom_tab_or_view: 1894 dir: 1895 file_name: 1896 ext: 1897 zipped: 1898 1899 Returns: 1900 file_path (str), file_path_zip (str), exists (bool) 1901 """ 1902 if file_name and not file_name.endswith(ext): 1903 file_name = ".".join((file_name, ext)) 1904 elif not file_name: 1905 file_name = ".".join((nom_tab_or_view, ext)).lower() 1906 1907 file_path = os.path.join(dir, file_name) 1908 file_path_zip = None 1909 if zipped: 1910 file_path_zip = "{}.zip".format(os.path.splitext(file_path)[0]) 1911 1912 exists = (os.path.exists(file_path) and not file_path_zip) or (file_path_zip and os.path.exists(file_path_zip)) 1913 1914 return file_path, file_path_zip, exists 1915 1916 @print_to_log_exception() 1917 def create_json_tab_or_view(self, nom_tab_or_view, dir='.', file_name=None, overwrite=True, cols=None, zipped=True, 1918 filter_sql=None, *args_sql): 1919 """ 1920 1921 Args: 1922 nom_tab_or_view (str): Nombre tabla o vista 1923 dir (str): 1924 file_name (str): 1925 overwrite (bool): 1926 cols (list): columnas 1927 zipped (bool): 1928 filter_sql (str): 1929 *args_sql: lista de argumentos a pasar al filtro sql 1930 Returns: 1931 file_path (str) 1932 """ 1933 file_path, file_path_zip, exists = self.verificar_path_vector_file( 1934 nom_tab_or_view, dir, file_name, "json", zipped) 1935 1936 if overwrite or not exists: 1937 # Se calculan las columnas para hacer get de la fila con el orden en las columnas de la tabla 1938 if not cols: 1939 dd_tab = self.get_dd_table(nom_tab_or_view) 1940 cols = dd_tab.cols 1941 sql = sql_tab(nom_tab_or_view, 1942 filter_sql=filter_sql, 1943 columns=cols) 1944 1945 file_path_res = vector_file_from_gen_ora_sql(file_path, "json", self.generator_rows_sql(sql, *args_sql), 1946 zipped=zipped) 1947 else: 1948 file_path_res = file_path if not zipped else file_path_zip 1949 1950 return file_path_res 1951 1952 @print_to_log_exception() 1953 def create_csv_tab_or_view(self, nom_tab_or_view, dir='.', file_name=None, overwrite=True, cols=None, zipped=True, 1954 filter_sql=None, *args_sql): 1955 """ 1956 1957 Args: 1958 nom_tab_or_view (str): Nombre tabla o vista 1959 dir (str): 1960 file_name (str): 1961 overwrite (bool): 1962 cols (list): columnas 1963 zipped (bool): 1964 filter_sql (str): 1965 *args_sql: lista de argumentos a pasar al filtro sql 1966 1967 Returns: 1968 file_path_res (str) 1969 """ 1970 file_path, file_path_zip, exists = self.verificar_path_vector_file( 1971 nom_tab_or_view, dir, file_name, "csv", zipped) 1972 1973 if overwrite or not exists: 1974 if not cols: 1975 dd_tab = self.get_dd_table(nom_tab_or_view) 1976 cols = dd_tab.cols 1977 sql = sql_tab(nom_tab_or_view, 1978 filter_sql=filter_sql, 1979 columns=cols) 1980 1981 # Para el formato geocsv que acepta GDAL se añade fichero con los tipos de columna 1982 tip_cols_csv = [] 1983 for col in cols: 1984 r_tip_col = self.row_table("user_tab_columns", 1985 "table_name = :1 and column_name = :2", 1986 nom_tab_or_view.upper(), col.upper()) 1987 dtype = r_tip_col.DATA_TYPE 1988 dlength = r_tip_col.DATA_LENGTH 1989 dprecision = r_tip_col.DATA_PRECISION 1990 dscale = r_tip_col.DATA_SCALE 1991 1992 if dtype == "DATE": 1993 tip_cols_csv.append('"DateTime"') 1994 elif dtype == "FLOAT": 1995 tip_cols_csv.append('"Real({}.{})"'.format(dlength, dprecision)) 1996 elif dtype == "NUMBER": 1997 if dscale and dscale != 0: 1998 tip_cols_csv.append('"Real({}.{})"'.format(dprecision, dscale)) 1999 elif dprecision: 2000 tip_cols_csv.append('"Integer({})"'.format(dprecision)) 2001 else: 2002 tip_cols_csv.append('"Real(10.8)"') 2003 elif dtype == "SDO_GEOMETRY": 2004 tip_cols_csv.append('"WKT"') 2005 else: 2006 tip_cols_csv.append('"String({})"'.format(round(dlength * 1.25))) 2007 2008 file_path_res = vector_file_from_gen_ora_sql(file_path, "csv", 2009 self.generator_rows_sql(sql, *args_sql, geom_format="as_wkt"), 2010 zipped=zipped, cols_csv=cols, tip_cols_csv=tip_cols_csv) 2011 else: 2012 file_path_res = file_path if not zipped else file_path_zip 2013 2014 return file_path_res 2015 2016 @print_to_log_exception() 2017 def create_geojsons_tab_or_view(self, nom_tab_or_view, dir='.', file_name_prefix=None, by_geom=False, 2018 dir_topojson=None, overwrite=True, cols=None, 2019 filter_sql=None, *args_sql): 2020 """ 2021 2022 Args: 2023 nom_tab_or_view (str): Nombre tabla (vigente o versionada) para entidad GIS 2024 dir (str="."): 2025 file_name_prefix (str=None): (opcional) prefijo del fichero 2026 by_geom (bool=False): (Opcional) si se querrán los geojsons por geometria. Si no se saca un unico geojson con 2027 la columna geometry como una GeometryCollection si la tabla es multigeom 2028 dir_topojson (str=None): path donde irán las conversiones 2029 overwrite (bool=True): 2030 cols (list=None): 2031 filter_sql (str=None): 2032 *args_sql: lista de argumentos a pasar al filtro sql 2033 2034 Returns: 2035 ok (bool) 2036 """ 2037 ext = "geo.json" 2038 sqls = {} 2039 dd_tab = self.get_dd_table(nom_tab_or_view) 2040 if not cols: 2041 cols = dd_tab.cols 2042 2043 if by_geom: 2044 c_alfas = [cn for cn in dd_tab.alfas() if cn in cols] 2045 c_geoms = [cn for cn in dd_tab.geoms() if cn in cols] 2046 for c_geom in c_geoms: 2047 sqls[c_geom] = sql_tab(nom_tab_or_view, filter_sql, c_alfas + [c_geom]) 2048 else: 2049 sqls[None] = sql_tab(nom_tab_or_view, filter_sql, cols) 2050 2051 if not file_name_prefix: 2052 file_name_prefix = nom_tab_or_view 2053 2054 for ng, sql in sqls.items(): 2055 file_name = file_name_prefix 2056 if ng: 2057 file_name = "-".join((file_name, ng)) 2058 2059 file_name = ".".join((file_name, ext)).lower() 2060 file_path = os.path.join(dir, file_name) 2061 2062 if overwrite or not os.path.exists(file_path): 2063 file_path = vector_file_from_gen_ora_sql(file_path, "geojson", 2064 self.generator_rows_sql(sql, *args_sql)) 2065 2066 if by_geom and dir_topojson and file_path: 2067 tip_geom = getattr(dd_tab, ng).GTYPE 2068 simplify = True 2069 if tip_geom.endswith("POINT"): 2070 simplify = False 2071 2072 topojson_utils.geojson_to_topojson(file_path, dir_topojson, 2073 simplify=simplify, 2074 overwrite=overwrite) 2075 2076 return True
Clase que gestionará distintas conexiones a Oracle y facilitará operaciones sobre la BBDD
1059 def __init__(self, user_ora, psw_ora, dsn_ora, a_logger=None, call_timeout: int = None, schema_ora=None): 1060 """ 1061 Inicializa gestor de Oracle para una conexion cx_Oracle a Oracle 1062 Se puede pasar por parametro un logger o inicializar por defecto 1063 1064 Args: 1065 user_ora {str}: Usuario/schema Oracle 1066 psw_ora {str}: Password usuario 1067 dsn_ora {str}: DSN Oracle (Nombre instancia/datasource de Oracle 1068 según TSN o string tal cual devuelve cx_Oracle.makedsn()) 1069 call_timeout (int=None): miliseconds espera per transaccio 1070 a_logger: 1071 schema_ora(str=None): indicate scheme when it is different from the user, default schema = user 1072 """ 1073 self.__con_db__ = None 1074 self.__call_timeout__ = call_timeout 1075 self.logger = a_logger 1076 self.__set_logger() 1077 self.__set_conexion(user_ora, psw_ora, dsn_ora, schema_ora=schema_ora)
Inicializa gestor de Oracle para una conexion cx_Oracle a Oracle Se puede pasar por parametro un logger o inicializar por defecto
Arguments:
- user_ora {str}: Usuario/schema Oracle
- psw_ora {str}: Password usuario
- dsn_ora {str}: DSN Oracle (Nombre instancia/datasource de Oracle según TSN o string tal cual devuelve cx_Oracle.makedsn())
- call_timeout (int=None): miliseconds espera per transaccio
- a_logger:
- schema_ora(str=None): indicate scheme when it is different from the user, default schema = user
1100 @staticmethod 1101 def log_dir(): 1102 """ 1103 Devuelve el directorio donde irán los logs indicado en la funcion apb_logging.logs_dir() 1104 1105 Returns: 1106 {str} - path del directorio de logs 1107 """ 1108 return utils_logging.logs_dir(True)
Devuelve el directorio donde irán los logs indicado en la funcion apb_logging.logs_dir()
Returns:
{str} - path del directorio de logs
1110 def log_name(self): 1111 """ 1112 Devuelve el nombre del fichero de log por defecto 1113 1114 Returns: 1115 {str} - Nombre fichero log por defecto 1116 """ 1117 return self.__class__.__name__
Devuelve el nombre del fichero de log por defecto
Returns:
{str} - Nombre fichero log por defecto
1131 def path_logs(self, if_exist=True): 1132 """ 1133 Devuelve lista paths base de los logs vinculados al gestor 1134 1135 Args: 1136 if_exist (bool): Devuelve los paths si el fichero existe 1137 1138 Returns: 1139 list: 1140 """ 1141 return logger_path_logs(self.logger)
Devuelve lista paths base de los logs vinculados al gestor
Arguments:
- if_exist (bool): Devuelve los paths si el fichero existe
Returns:
list:
1143 def print_log(self, msg): 1144 """ 1145 Sobre el logger escribe mensaje de info 1146 1147 Args: 1148 msg {str}: String con el mensaje 1149 """ 1150 self.logger.info(msg)
Sobre el logger escribe mensaje de info
Arguments:
- msg {str}: String con el mensaje
1152 def print_log_error(self, msg): 1153 """ 1154 Sobre el logger escribe mensaje de error 1155 1156 Args: 1157 msg {str}: String con el mensaje 1158 """ 1159 self.logger.error(msg)
Sobre el logger escribe mensaje de error
Arguments:
- msg {str}: String con el mensaje
1161 def print_log_exception(self, msg): 1162 """ 1163 Sobre el logger escribe excepcion 1164 1165 Args: 1166 msg {str}: String con el mensaje 1167 """ 1168 self.logger.exception(msg)
Sobre el logger escribe excepcion
Arguments:
- msg {str}: String con el mensaje
1193 @property 1194 @print_to_log_exception(cx_Oracle.Error, lanzar_exc=True) 1195 def con_db(self): 1196 """ 1197 Return a cx_Oracle Conection live 1198 1199 Returns: 1200 cx_Oracle.Connection 1201 """ 1202 reconnect = False 1203 if (con_ora := self.__con_db__) is not None: 1204 try: 1205 con_ora.ping() 1206 except cx_Oracle.Error as exc: 1207 # Borramos las entradas de cache asociadas a la conexión que no responde 1208 del_cache_rel_con_db(get_nom_conexion(con_ora)) 1209 try: 1210 con_ora.close() 1211 except cx_Oracle.Error: 1212 pass 1213 self.__con_db__ = con_ora = None 1214 1215 if con_ora is None: 1216 self.__set_conexion( 1217 self.__user_con_db__, 1218 self.__psw_con_db__, 1219 self.__dsn_ora__, schema_ora=self.__schema_con_db__) 1220 1221 con_ora = self.__con_db__ 1222 1223 return con_ora
Return a cx_Oracle Conection live
Returns:
cx_Oracle.Connection
1225 @print_to_log_exception(cx_Oracle.DatabaseError) 1226 def exec_trans_db(self, sql_str, *args_sql, **types_sql_args): 1227 """ 1228 Ejecuta transaccion SQL 1229 1230 Args: 1231 sql_str (str): sql transaction (update, insert, delete} 1232 *args_sql: Lista argumentos a pasar 1233 **types_sql_args (OPCIONAL): Lista tipos cx_Oracle para cada argumento 1234 1235 Returns: 1236 ok {bool}: Si ha ido bien True si no False 1237 """ 1238 curs_db = None 1239 try: 1240 curs_db = new_cursor(self.con_db, 1241 input_handler=m_sdo_geom.get_sdo_input_handler()) 1242 1243 curs_db.setinputsizes(*types_sql_args.values()) 1244 1245 curs_db.execute(sql_str, 1246 args_sql) 1247 finally: 1248 if curs_db: 1249 curs_db.close() 1250 1251 return True
Ejecuta transaccion SQL
Arguments:
- sql_str (str): sql transaction (update, insert, delete}
- *args_sql: Lista argumentos a pasar
- **types_sql_args (OPCIONAL): Lista tipos cx_Oracle para cada argumento
Returns:
ok {bool}: Si ha ido bien True si no False
1253 @print_to_log_exception(cx_Oracle.DatabaseError) 1254 def exec_script_plsql(self, sql_str): 1255 """ 1256 Ejecuta script SQL 1257 1258 Args: 1259 sql_str {str}: sql script 1260 1261 Returns: 1262 ok {bool}: Si ha ido bien True si no False 1263 """ 1264 curs_db = None 1265 try: 1266 curs_db = new_cursor(self.con_db) 1267 curs_db.execute(sql_str) 1268 finally: 1269 if curs_db: 1270 curs_db.close() 1271 1272 return True
Ejecuta script SQL
Arguments:
- sql_str {str}: sql script
Returns:
ok {bool}: Si ha ido bien True si no False
1274 @print_to_log_exception(cx_Oracle.DatabaseError) 1275 def callfunc_sql(self, nom_func, ret_cx_ora_tipo, *args_func): 1276 """ 1277 Ejecuta funcion PL/SQL y retorna el valor 1278 1279 Args: 1280 nom_func (str): Nombre de la funcion PL/SQL 1281 ret_cx_ora_tipo (cx_Oracle TIPO): El retorno de la función en cx_Oracle (cx_Oracle.NUMBER, 1282 cx_Oracle.STRING,...) 1283 *args_func: Argumentos de la funcion PL/SQL 1284 1285 Returns: 1286 Valor retornado por la función PL/SQL 1287 """ 1288 curs = None 1289 try: 1290 curs = new_cursor(self.con_db) 1291 ret = curs.callfunc(nom_func, 1292 ret_cx_ora_tipo, 1293 args_func) 1294 finally: 1295 if curs: 1296 curs.close() 1297 1298 return ret
Ejecuta funcion PL/SQL y retorna el valor
Arguments:
- nom_func (str): Nombre de la funcion PL/SQL
- ret_cx_ora_tipo (cx_Oracle TIPO): El retorno de la función en cx_Oracle (cx_Oracle.NUMBER, cx_Oracle.STRING,...)
- *args_func: Argumentos de la funcion PL/SQL
Returns:
Valor retornado por la función PL/SQL
1300 @print_to_log_exception(cx_Oracle.DatabaseError) 1301 def callproc_sql(self, nom_proc, *args_proc): 1302 """ 1303 Ejecuta procedimiento PL/SQL 1304 1305 Args: 1306 nom_proc (str): Nombre del procedimiento PL/SQL 1307 *args_proc: Argumentos del procedimiento 1308 1309 Returns: 1310 ok {bool}: Si ha ido bien True si no False 1311 """ 1312 curs = None 1313 try: 1314 curs = new_cursor(self.con_db) 1315 curs.callproc(nom_proc, 1316 args_proc) 1317 finally: 1318 if curs: 1319 curs.close() 1320 1321 return True
Ejecuta procedimiento PL/SQL
Arguments:
- nom_proc (str): Nombre del procedimiento PL/SQL
- *args_proc: Argumentos del procedimiento
Returns:
ok {bool}: Si ha ido bien True si no False
1323 @print_to_log_exception(cx_Oracle.DatabaseError) 1324 def row_sql(self, sql_str, *args_sql, **extra_params): 1325 """ 1326 Retorna la fila resultante de la query sql SQL_STR con los parámetros *ARGS_SQL. 1327 Se puede informar **EXTRA_PARAMS con algunos de los siguientes parámetros: 1328 INPUT_HANDLER funcion que tratará los bindings de manera específica 1329 OUTPUT_HANLER funcion que tratará los valores de las columnas de modo específico 1330 ROW_CLASS define que con que clase se devolverá cada fila. Por defecto la que devuleve la funcion 1331 'apb_cx_oracle_spatial.get_row_class_cursor()' 1332 AS_FORMAT (as_xml, as_json, as_geojson) devuelve fila en el formato especificado. La row_class deberá 1333 responder a esas funciones 1334 1335 Args: 1336 sql_str: 1337 *args_sql: 1338 **extra_params: 1339 1340 Returns: 1341 object (instancia que debería ser o heredar de las clases row_cursor o row_table) 1342 """ 1343 return execute_fetch_sql(self.con_db, 1344 sql_str, 1345 *args_sql, 1346 input_handler=extra_params.pop("input_handler", None), 1347 output_handler=extra_params.pop( 1348 "output_handler", 1349 m_sdo_geom.get_output_handler(self.con_db, 1350 extra_params.pop("geom_format", None))), 1351 row_class=extra_params.pop("row_class", None), 1352 **extra_params)
Retorna la fila resultante de la query sql SQL_STR con los parámetros *ARGS_SQL. Se puede informar **EXTRA_PARAMS con algunos de los siguientes parámetros: INPUT_HANDLER funcion que tratará los bindings de manera específica OUTPUT_HANLER funcion que tratará los valores de las columnas de modo específico ROW_CLASS define que con que clase se devolverá cada fila. Por defecto la que devuleve la funcion 'apb_cx_oracle_spatial.get_row_class_cursor()' AS_FORMAT (as_xml, as_json, as_geojson) devuelve fila en el formato especificado. La row_class deberá responder a esas funciones
Arguments:
- sql_str:
- *args_sql:
- **extra_params:
Returns:
object (instancia que debería ser o heredar de las clases row_cursor o row_table)
1354 @print_to_log_exception(cx_Oracle.DatabaseError) 1355 def generator_rows_sql(self, sql_str, *args_sql, **extra_params): 1356 """ 1357 Ejecuta consulta SQL de forma iterativa retornando cada fila como un objeto row_class (por defecto row_cursor) 1358 1359 Se puede informar **EXTRA_PARAMS con algunos de los siguientes parámetros: 1360 INPUT_HANDLER funcion que tratará los bindings de manera específica 1361 OUTPUT_HANLER funcion que tratará los valores de las columnas de modo específico 1362 ROW_CLASS define que con que clase se devolverá cada fila. Por defecto la que devuleve la funcion 1363 'apb_cx_oracle_spatial.get_row_class_cursor()' 1364 AS_FORMAT (as_xml, as_json, as_geojson) devuelve fila en el formato especificado. La row_class deberá 1365 responder a esas funciones 1366 1367 Args: 1368 sql_str: 1369 *args_sql: 1370 **extra_params: 1371 1372 Returns: 1373 object (instancia que debería ser o heredar de las clases row_cursor o row_table) 1374 """ 1375 for reg in iter_execute_fetch_sql(self.con_db, 1376 sql_str, 1377 *args_sql, 1378 input_handler=extra_params.pop("input_handler", None), 1379 output_handler=extra_params.pop( 1380 "output_handler", 1381 m_sdo_geom.get_output_handler(self.con_db, 1382 extra_params.pop("geom_format", None))), 1383 row_class=extra_params.pop("row_class", None), 1384 **extra_params): 1385 yield reg
Ejecuta consulta SQL de forma iterativa retornando cada fila como un objeto row_class (por defecto row_cursor)
Se puede informar **EXTRA_PARAMS con algunos de los siguientes parámetros: INPUT_HANDLER funcion que tratará los bindings de manera específica OUTPUT_HANLER funcion que tratará los valores de las columnas de modo específico ROW_CLASS define que con que clase se devolverá cada fila. Por defecto la que devuleve la funcion 'apb_cx_oracle_spatial.get_row_class_cursor()' AS_FORMAT (as_xml, as_json, as_geojson) devuelve fila en el formato especificado. La row_class deberá responder a esas funciones
Arguments:
- sql_str:
- *args_sql:
- **extra_params:
Returns:
object (instancia que debería ser o heredar de las clases row_cursor o row_table)
1387 def rows_sql(self, sql_str, *args_sql): 1388 """ 1389 Vease funcion 'generator_rows_sql()' 1390 Args: 1391 sql_str: 1392 *args_sql: 1393 1394 Returns: 1395 list 1396 """ 1397 return list(self.generator_rows_sql(sql_str, *args_sql))
Vease funcion 'generator_rows_sql()'
Arguments:
- sql_str:
- *args_sql:
Returns:
list
1399 def get_primary_key_table(self, nom_tab_or_view): 1400 """ 1401 Retorna lista con las columnas que conforman la primary key de una tabla/vista 1402 Args: 1403 nom_tab_or_view: 1404 1405 Returns: 1406 list con campos clave 1407 """ 1408 return get_pk_tab(self.con_db, nom_tab_or_view)
Retorna lista con las columnas que conforman la primary key de una tabla/vista
Arguments:
- nom_tab_or_view:
Returns:
list con campos clave
1410 @print_to_log_exception(lanzar_exc=True) 1411 def get_dd_table(self, nom_tab_or_view): 1412 """ 1413 Retorna instancia row_table con los tipos para cada columna 1414 Args: 1415 nom_tab_or_view: 1416 1417 Returns: 1418 object de clase row_table con los tipos de cada columna como valores 1419 """ 1420 return get_row_desc_tab(self.con_db, nom_tab_or_view)
Retorna instancia row_table con los tipos para cada columna
Arguments:
- nom_tab_or_view:
Returns:
object de clase row_table con los tipos de cada columna como valores
1422 def generator_rows_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql, **extra_params): 1423 """ 1424 Retorna los registros que cumplan con el FILTER_SQL sobre la tabla o vista indicada. 1425 1426 La tabla se puede referenciar en el filtro con el alias 'TAB' 1427 1428 Si el FILTER_SQL utiliza valores por binding (:1, :2,...) estos se indicaran por orden en ARGS_FILTER_SQL 1429 1430 Args: 1431 nom_tab_or_view: Nombre de la tabla o vista sobre la que se hará la consulta 1432 filter_sql: Filtre sobre la taula 1433 args_filter_sql: Valors en ordre de binding per passar 1434 extra_params: Vease generator_rows_sql() 1435 1436 Yields: 1437 row_table: regs. clase row_table (mirar get_row_class_tab()) 1438 """ 1439 for reg in self.generator_rows_sql(sql_tab(nom_tab_or_view, 1440 filter_sql), 1441 *args_filter_sql, 1442 row_class=extra_params.pop( 1443 "row_class", 1444 get_row_class_tab(self.con_db, nom_tab_or_view)), 1445 **extra_params): 1446 yield reg
Retorna los registros que cumplan con el FILTER_SQL sobre la tabla o vista indicada.
La tabla se puede referenciar en el filtro con el alias 'TAB'
Si el FILTER_SQL utiliza valores por binding (:1, :2,...) estos se indicaran por orden en ARGS_FILTER_SQL
Arguments:
- nom_tab_or_view: Nombre de la tabla o vista sobre la que se hará la consulta
- filter_sql: Filtre sobre la taula
- args_filter_sql: Valors en ordre de binding per passar
- extra_params: Vease generator_rows_sql()
Yields:
row_table: regs. clase row_table (mirar get_row_class_tab())
1448 def generator_rows_interact_geom(self, nom_tab, a_sdo_geom, cols_geom=None, geom_format=None): 1449 """ 1450 Retorna las filas de una tabla que interactuan con una geometria 1451 1452 Args: 1453 nom_tab: nombre de la tabla 1454 a_sdo_geom: geometria clase sdo_geom 1455 cols_geom (default=None): Lista con nombre de columnas geométricas sobre las que se quiere aplicar filtro 1456 geom_format: 1457 Yields: 1458 row_table: regs. clase row_table (mirar get_row_class_tab()) 1459 """ 1460 # Uso de " <>'FALSE'" por fallo de Oracle usando "= 'TRUE'" 1461 filter_interact_base = "SDO_ANYINTERACT({camp_geom}, :1) <> 'FALSE'" 1462 1463 if not cols_geom: 1464 cols_geom = get_tips_geom_tab(self.con_db, nom_tab).keys() 1465 1466 if cols_geom: 1467 filter_sql = " OR ".join([filter_interact_base.format(camp_geom=ng) for ng in cols_geom]) 1468 for reg in self.generator_rows_table(nom_tab, filter_sql, a_sdo_geom.as_ora_sdo_geometry(), 1469 geom_format=geom_format): 1470 yield reg
Retorna las filas de una tabla que interactuan con una geometria
Arguments:
- nom_tab: nombre de la tabla
- a_sdo_geom: geometria clase sdo_geom
- cols_geom (default=None): Lista con nombre de columnas geométricas sobre las que se quiere aplicar filtro
- geom_format:
Yields:
row_table: regs. clase row_table (mirar get_row_class_tab())
1472 def rows_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql): 1473 """ 1474 Vease funcion 'generator_rows_table()' 1475 1476 Args: 1477 nom_tab_or_view: 1478 filter_sql: 1479 *args_filter_sql: 1480 1481 Returns: 1482 dict 1483 """ 1484 gen_tab = self.generator_rows_table(nom_tab_or_view, 1485 filter_sql, 1486 *args_filter_sql) 1487 pk_tab = self.get_primary_key_table(nom_tab_or_view) 1488 l_pk = len(pk_tab) 1489 if l_pk == 0: 1490 return [r for r in gen_tab] 1491 else: 1492 def f_key(r): 1493 return getattr(r, pk_tab[0]) 1494 1495 if l_pk > 1: 1496 def f_key(r): return tuple(map(lambda nf: getattr(r, nf), pk_tab)) 1497 return {f_key(r): r for r in gen_tab}
Vease funcion 'generator_rows_table()'
Arguments:
- nom_tab_or_view:
- filter_sql:
- *args_filter_sql:
Returns:
dict
1499 def row_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql, **extra_params): 1500 """ 1501 Retorna primer registro para el FILTER_SQL sobre la tabla o vista indicada 1502 Si el FILTER_SQL utiliza valores por binding (:1, :2,...) estos se indicaran por orden en ARGS_FILTER_SQL 1503 1504 Args: 1505 nom_tab_or_view: 1506 filter_sql: 1507 *args_filter_sql: 1508 **extra_params: 1509 1510 Returns: 1511 object de la clase row_table o especificada en **extra_params['row_class'] 1512 """ 1513 gen = self.generator_rows_table(nom_tab_or_view, 1514 filter_sql, 1515 *args_filter_sql, 1516 **extra_params) 1517 return next(gen, None)
Retorna primer registro para el FILTER_SQL sobre la tabla o vista indicada Si el FILTER_SQL utiliza valores por binding (:1, :2,...) estos se indicaran por orden en ARGS_FILTER_SQL
Arguments:
- nom_tab_or_view:
- filter_sql:
- *args_filter_sql:
- **extra_params:
Returns:
object de la clase row_table o especificada en **extra_params['row_class']
1519 def row_table_at(self, nom_tab_or_view, *vals_key): 1520 """ 1521 Devuelve row_tabla_class para el registro que de la tabla_vista que cumpla con la clave 1522 1523 Args: 1524 nom_tab_or_view: 1525 *vals_key: 1526 1527 Returns: 1528 object de la clase row_table o especificada en **extra_params['row_class'] 1529 """ 1530 return self.exist_row_tab(nom_tab_or_view, 1531 {nc: val for nc, val in zip(self.get_primary_key_table(nom_tab_or_view), 1532 vals_key)})
Devuelve row_tabla_class para el registro que de la tabla_vista que cumpla con la clave
Arguments:
- nom_tab_or_view:
- *vals_key:
Returns:
object de la clase row_table o especificada en **extra_params['row_class']
1534 def test_row_table(self, row_tab, a_sql, *args_sql): 1535 """ 1536 Testea un registro de tabla (clase rwo_table) cumpla con sql indicado 1537 1538 Args: 1539 row_tab: registro de tabla en forma de clase row_table 1540 a_sql: string con sql a testear 1541 1542 Returns: 1543 bool: True o False según cumpla con el SQL indicado 1544 """ 1545 sql_pk = dict_as_sql_bind_and_params(row_tab.pk_vals()) 1546 query_sql = "{} AND ({})".format(sql_pk[0], a_sql) 1547 1548 ret = False 1549 if self.row_table(row_tab.nom_tab, query_sql, *(tuple(sql_pk[1]) + tuple(args_sql))): 1550 ret = True 1551 1552 return ret
Testea un registro de tabla (clase rwo_table) cumpla con sql indicado
Arguments:
- row_tab: registro de tabla en forma de clase row_table
- a_sql: string con sql a testear
Returns:
bool: True o False según cumpla con el SQL indicado
1554 def insert_row_tab(self, nom_tab, dict_vals_param=None, dict_vals_str=None, pasar_nulls=False): 1555 """ 1556 Inserta registro en la tabla indicada. Los valores para cada columna se pasarán a través de dict_vals_param 1557 como bindings o a través de dict_vals_str directemente en el string del sql ejecutado 1558 Args: 1559 nom_tab: nombre de la tabla 1560 dict_vals_param: diccionario indexado por columnas-valores. Los valores se pasarán como bindings 1561 dict_vals_str: diccionario indexado por columnas-valores a asignar como strings. El valor se pasa 1562 directamente como asignacion en la senetencia sql 1563 pasar_nulls: (opcional) por defecto False. Indica si los valores NULL se asignarán o no 1564 1565 Returns: 1566 row_table (si genera el registro) o False si va mal la operación 1567 """ 1568 if not dict_vals_param: 1569 dict_vals_param = {} 1570 if not dict_vals_str: 1571 dict_vals_str = {} 1572 1573 ora_dict_vals_param = self.get_vals_tab_for_transdb(nom_tab, dict_vals_param, pasar_nulls=pasar_nulls) 1574 1575 if pasar_nulls: 1576 # Se revisa que en un campo geometrico se asigne None y por lo tanto se asigne valor via STR 1577 geoms_null = [ng for ng, val in ora_dict_vals_param.items() 1578 if not val and self.get_tip_camp_geom(nom_tab, ng)] 1579 if geoms_null: 1580 keys_str = [nc.upper() for nc in dict_vals_str.keys()] 1581 for gn in geoms_null: 1582 ora_dict_vals_param.pop(gn) 1583 if gn.upper() not in keys_str: 1584 dict_vals_str[gn.upper()] = "NULL" 1585 1586 params = [] 1587 nom_camps = [] 1588 vals_camps = [] 1589 for nom_camp, val_camp in ora_dict_vals_param.items(): 1590 if val_camp is None: 1591 continue 1592 nom_camps.append(nom_camp) 1593 vals_camps.append(":" + nom_camp) 1594 params.append(val_camp) 1595 1596 for nom_camp, val_camp_str in dict_vals_str.items(): 1597 if val_camp_str is None: 1598 continue 1599 nom_camps.append(nom_camp) 1600 vals_camps.append(str(val_camp_str)) 1601 1602 row_desc_tab = get_row_desc_tab(self.con_db, nom_tab) 1603 pk_binds = {k: new_cursor(self.con_db).var(ora_tip_camp) for k, ora_tip_camp in row_desc_tab.pk_vals().items()} 1604 if not pk_binds: 1605 pk_binds = {'ROWID': new_cursor(self.con_db).var(cx_Oracle.ROWID)} 1606 str_pk_camps = ",".join(pk_binds.keys()) 1607 str_pk_binds = ",".join(list(map(lambda x: ":ret_" + str(x), pk_binds.keys()))) 1608 params += list(pk_binds.values()) 1609 1610 a_sql_res = f"insert into {nom_tab}({','.join(nom_camps)}) values({','.join(vals_camps)}) " \ 1611 f"returning {str_pk_camps} into {str_pk_binds}" 1612 1613 ok = self.exec_trans_db(a_sql_res, *params) 1614 if ok: 1615 pk_vals = {k: curs_var.getvalue(0)[0] for k, curs_var in pk_binds.items()} 1616 return self.exist_row_tab(nom_tab, pk_vals) 1617 1618 return ok
Inserta registro en la tabla indicada. Los valores para cada columna se pasarán a través de dict_vals_param como bindings o a través de dict_vals_str directemente en el string del sql ejecutado
Arguments:
- nom_tab: nombre de la tabla
- dict_vals_param: diccionario indexado por columnas-valores. Los valores se pasarán como bindings
- dict_vals_str: diccionario indexado por columnas-valores a asignar como strings. El valor se pasa directamente como asignacion en la senetencia sql
- pasar_nulls: (opcional) por defecto False. Indica si los valores NULL se asignarán o no
Returns:
row_table (si genera el registro) o False si va mal la operación
1620 def update_row_tab(self, nom_tab, dict_clau_reg, dict_vals_param=None, dict_vals_str=None, pasar_nulls=None): 1621 """ 1622 Actualiza registro en la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor} 1623 Los valores para cada columna se pasarán a través de dict_vals_param como bindings o a través de 1624 dict_vals_str directemente en el string del sql ejecutado 1625 1626 Args: 1627 nom_tab: nombre de la tabla 1628 dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar 1629 dict_vals_param: diccionario indexado por columnas-valores. Los valores se pasarán como bindings 1630 dict_vals_str: diccionario indexado por columnas-valores a asignar como strings. El valor se pasa 1631 directamente como asignacion en la senetencia sql 1632 pasar_nulls: (opcional) por defecto False. Indica si los valores NULL se asignarán o no 1633 1634 Returns: 1635 row_table (si genera el registro) o False si va mal la operación 1636 """ 1637 if not dict_vals_param: 1638 dict_vals_param = {} 1639 if not dict_vals_str: 1640 dict_vals_str = {} 1641 1642 ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg) 1643 ora_dict_vals_param = self.get_vals_tab_for_transdb(nom_tab, dict_vals_param, pasar_nulls=pasar_nulls) 1644 1645 if pasar_nulls: 1646 # Se revisa que en un campo geometrico se asigne None y por lo tanto se asigne valor via STR 1647 geoms_null = [ng for ng, val in ora_dict_vals_param.items() 1648 if not val and self.get_tip_camp_geom(nom_tab, ng)] 1649 if geoms_null: 1650 keys_str = [nc.upper() for nc in dict_vals_str.keys()] 1651 for gn in geoms_null: 1652 ora_dict_vals_param.pop(gn) 1653 if gn.upper() not in keys_str: 1654 dict_vals_str[gn.upper()] = "NULL" 1655 1656 (sql_set_camps, params_set_camps) = dict_as_sql_bind_and_params(ora_dict_vals_param, 1657 ",", "=") 1658 1659 (query_clau, params_filter) = dict_as_sql_bind_and_params(ora_dict_clau_reg) 1660 1661 params = params_set_camps + params_filter 1662 1663 for nom_camp, val_camp_str in dict_vals_str.items(): 1664 if sql_set_camps: 1665 sql_set_camps += " , " 1666 sql_set_camps += nom_camp + "=" + val_camp_str 1667 1668 ok = None 1669 if sql_set_camps: 1670 a_sql_res = f"update {nom_tab} set {sql_set_camps} where {query_clau}" 1671 1672 ok = self.exec_trans_db(a_sql_res, *params) 1673 1674 if ok: 1675 return self.exist_row_tab(nom_tab, dict_clau_reg) 1676 1677 return ok
Actualiza registro en la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor} Los valores para cada columna se pasarán a través de dict_vals_param como bindings o a través de dict_vals_str directemente en el string del sql ejecutado
Arguments:
- nom_tab: nombre de la tabla
- dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar
- dict_vals_param: diccionario indexado por columnas-valores. Los valores se pasarán como bindings
- dict_vals_str: diccionario indexado por columnas-valores a asignar como strings. El valor se pasa directamente como asignacion en la senetencia sql
- pasar_nulls: (opcional) por defecto False. Indica si los valores NULL se asignarán o no
Returns:
row_table (si genera el registro) o False si va mal la operación
1679 def remove_row_tab(self, nom_tab, dict_clau_reg): 1680 """ 1681 Borra registro en la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor} 1682 1683 Args: 1684 nom_tab: nombre de la tabla 1685 dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar 1686 1687 Returns: 1688 bool según vaya la operación 1689 """ 1690 ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg) 1691 1692 a_sql_tmpl = "delete {nom_tab} where {query_clau}" 1693 1694 (sql_filter, params) = dict_as_sql_bind_and_params(ora_dict_clau_reg) 1695 1696 a_sql_res = a_sql_tmpl.format(nom_tab=nom_tab, 1697 query_clau=sql_filter) 1698 1699 return self.exec_trans_db(a_sql_res, *params)
Borra registro en la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor}
Arguments:
- nom_tab: nombre de la tabla
- dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar
Returns:
bool según vaya la operación
1701 def exist_row_tab(self, nom_tab, dict_clau_reg, **extra_params): 1702 """ 1703 Devuelve registro de la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor} 1704 1705 Args: 1706 nom_tab: nombre de la tabla 1707 dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar 1708 **extra_params: 1709 1710 Returns: 1711 object de la clase row_table o especificada en **extra_params['row_class'] 1712 """ 1713 ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg) 1714 1715 (filter_sql, params) = dict_as_sql_bind_and_params(ora_dict_clau_reg, "and", "=") 1716 1717 return self.row_table(nom_tab, filter_sql, *params, **extra_params)
Devuelve registro de la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor}
Arguments:
- nom_tab: nombre de la tabla
- dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar
- **extra_params:
Returns:
object de la clase row_table o especificada en **extra_params['row_class']
1719 def get_vals_tab_for_transdb(self, nom_tab, dict_camps_vals, pasar_nulls=True): 1720 """ 1721 Para un tabla y diccionario columnas-valores devuelve diccionario indexado por las columnas con los valores 1722 convertidos a formato cx_Oracle según tipo de cada columna en Oracle 1723 Args: 1724 nom_tab: nombre de la tabla 1725 dict_camps_vals: diccionario indexado por columnas-valor a convertir a formato cx_Oracle 1726 pasar_nulls: (opcional) por defecto convertirá los None a NULL de Oracle 1727 1728 Returns: 1729 dict indexado por columnas con los valores convertidos a tipo cx_Oracle 1730 """ 1731 dd_tab = get_row_desc_tab(self.con_db, nom_tab) 1732 1733 # Retorna dict con los campos a pasar por parametro 1734 d_params = {} 1735 1736 # Los nombres de campo siempre se buscarán en mayúsculas 1737 dict_camps_vals = {k.upper(): v for k, v in dict_camps_vals.items()} 1738 1739 for camp, tip_camp in dd_tab.vals().items(): 1740 if camp not in dict_camps_vals: 1741 continue 1742 1743 val_camp = dict_camps_vals.get(camp) 1744 if not pasar_nulls and val_camp is None: 1745 continue 1746 1747 if isinstance(val_camp, m_sdo_geom.sdo_geom): 1748 var = val_camp.as_ora_sdo_geometry() 1749 else: 1750 try: 1751 var = new_cursor(self.con_db).var(tip_camp) 1752 var.setvalue(0, val_camp) 1753 except: 1754 var = val_camp 1755 1756 d_params[camp] = var 1757 1758 return d_params
Para un tabla y diccionario columnas-valores devuelve diccionario indexado por las columnas con los valores convertidos a formato cx_Oracle según tipo de cada columna en Oracle
Arguments:
- nom_tab: nombre de la tabla
- dict_camps_vals: diccionario indexado por columnas-valor a convertir a formato cx_Oracle
- pasar_nulls: (opcional) por defecto convertirá los None a NULL de Oracle
Returns:
dict indexado por columnas con los valores convertidos a tipo cx_Oracle
1760 @print_to_log_exception() 1761 def run_sql_script(self, filename): 1762 """ 1763 Ejecuta slq script (filename) sobre SQLPLUS 1764 1765 Args: 1766 filename: path del sql script 1767 1768 Returns: 1769 1770 """ 1771 user_ora = self.con_db.username 1772 ds_ora = self.con_db.dsn 1773 nom_con = self.nom_con_db 1774 psw_ora = self.__psw_con_db__ 1775 if psw_ora is None: 1776 print("ERROR - Conexión '" + nom_con + "' no está añadida al gestor!!") 1777 return 1778 1779 with open(filename, 'rb') as a_file: 1780 a_sql_command = a_file.read() 1781 1782 con_db_str = user_ora + "/" + psw_ora + "@" + ds_ora 1783 sqlplus = Popen(['sqlplus', '-S', con_db_str], stdin=PIPE, stdout=PIPE, stderr=PIPE) 1784 # sqlplus.stdin.write(a_sql_command) 1785 1786 (stdout, stderr) = sqlplus.communicate(a_sql_command) 1787 1788 self.print_log("Resultado lanzar script '{}': \n" 1789 "{}".format(filename, 1790 stdout.decode("utf-8"))) 1791 1792 if sqlplus is not None: 1793 sqlplus.terminate()
Ejecuta slq script (filename) sobre SQLPLUS
Arguments:
- filename: path del sql script
Returns:
1795 @staticmethod 1796 def get_nom_obj_sql(nom_base, prefix="", sufix=""): 1797 """ 1798 Retorna nombre propuesto con prefijo/sufijos formateado para que cumpla longitud máxima de 32 caracteres en 1799 objetos sql Oracle 1800 1801 Args: 1802 nom_base: nombre propuesto 1803 prefix: (opc) prefijo 1804 sufix: (opc) sufijo 1805 1806 Returns: 1807 str formateado 1808 """ 1809 return x_sql_parser.get_nom_obj_sql(nom_base, prefix, sufix)
Retorna nombre propuesto con prefijo/sufijos formateado para que cumpla longitud máxima de 32 caracteres en objetos sql Oracle
Arguments:
- nom_base: nombre propuesto
- prefix: (opc) prefijo
- sufix: (opc) sufijo
Returns:
str formateado
1811 def iter_sdo_gtypes_vals_camp_tab(self, nom_taula, nom_camp): 1812 """ 1813 Retorna los distintos tipos de Geometria (codigo entero que define el tipo SDO_GTYPE) 1814 que se encuentran dentro de la columna sdo_geometry de una tabla 1815 1816 Args: 1817 nom_taula: nombre de la tabla 1818 nom_camp: nombre campo geometrico 1819 1820 Returns: 1821 int definiendo tipo de geometría 1822 """ 1823 sql_tip_geoms = f"select distinct(tab.{nom_camp}.Get_GType()) as tip_geom from {nom_taula} tab " \ 1824 f"where {nom_camp} is not null" 1825 1826 for reg in self.generator_rows_sql(sql_tip_geoms): 1827 yield reg.TIP_GEOM
Retorna los distintos tipos de Geometria (codigo entero que define el tipo SDO_GTYPE) que se encuentran dentro de la columna sdo_geometry de una tabla
Arguments:
- nom_taula: nombre de la tabla
- nom_camp: nombre campo geometrico
Returns:
int definiendo tipo de geometría
1829 def iter_distinct_vals_camp_tab(self, nom_taula, nom_camp, filter_sql=None): 1830 """ 1831 Retorna los distintos valores de la columna de una tabla 1832 1833 Args: 1834 nom_taula (str): Nombre de tabla 1835 nom_camp (str): Nombre de campo 1836 filter_sql(str): Filtro SQL sobre la tabla indicada 1837 1838 Returns: 1839 {str}: Itera los distintos valores encontrados en el campo indicado 1840 """ 1841 sql_distinct_vals = f"select distinct(tab.{nom_camp}) as VAL from {nom_taula} tab" 1842 if filter_sql: 1843 sql_distinct_vals += " where " + filter_sql 1844 1845 for reg in self.generator_rows_sql(sql_distinct_vals): 1846 yield reg.VAL
Retorna los distintos valores de la columna de una tabla
Arguments:
- nom_taula (str): Nombre de tabla
- nom_camp (str): Nombre de campo
- filter_sql(str): Filtro SQL sobre la tabla indicada
Returns:
{str}: Itera los distintos valores encontrados en el campo indicado
1848 def get_tip_camp_geom(self, nom_tab_or_view, nom_camp_geom): 1849 """ 1850 Retorna el tipo de campo geométrico (class_tip_geom) registrados en la global __class_tips_geom_ora 1851 para el campo indicado 1852 1853 Args: 1854 nom_tab_or_view: nombre tabla/vista 1855 nom_camp_geom: nombre campo geom 1856 1857 Returns: 1858 namedtuple: con atributos ['TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID']) 1859 """ 1860 tips_geom_tab = get_tips_geom_tab(self.con_db, nom_tab_or_view) 1861 if tips_geom_tab: 1862 return tips_geom_tab.get(nom_camp_geom.upper())
Retorna el tipo de campo geométrico (class_tip_geom) registrados en la global __class_tips_geom_ora para el campo indicado
Arguments:
- nom_tab_or_view: nombre tabla/vista
- nom_camp_geom: nombre campo geom
Returns:
namedtuple: con atributos ['TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID'])
1864 def get_epsg_for_srid(self, srid): 1865 """ 1866 Rertorna WKT con la definicion del SRID dado 1867 """ 1868 return self.callfunc_sql('SDO_CS.MAP_ORACLE_SRID_TO_EPSG', cx_Oracle.NUMBER, srid)
Rertorna WKT con la definicion del SRID dado
1870 def get_gtype_camp_geom(self, nom_tab_or_view, nom_camp_geom): 1871 """ 1872 Retorna el tipo GTYPE (int) de la geometria 1873 1874 Args: 1875 nom_tab_or_view: nombre tabla/vista 1876 nom_camp_geom: nombre campo geom 1877 1878 Returns: 1879 int 1880 """ 1881 gtype = 0 1882 g_tip_ora = self.get_tip_camp_geom(nom_tab_or_view, nom_camp_geom) 1883 if g_tip_ora: 1884 gtype = GTYPES_ORA.index(g_tip_ora.GTYPE) 1885 1886 return gtype
Retorna el tipo GTYPE (int) de la geometria
Arguments:
- nom_tab_or_view: nombre tabla/vista
- nom_camp_geom: nombre campo geom
Returns:
int
1888 @staticmethod 1889 def verificar_path_vector_file(nom_tab_or_view, dir, file_name, ext, zipped): 1890 """ 1891 Compone el/los path para el/los vector_file de una tabla y determina si exists 1892 Args: 1893 nom_tab_or_view: 1894 dir: 1895 file_name: 1896 ext: 1897 zipped: 1898 1899 Returns: 1900 file_path (str), file_path_zip (str), exists (bool) 1901 """ 1902 if file_name and not file_name.endswith(ext): 1903 file_name = ".".join((file_name, ext)) 1904 elif not file_name: 1905 file_name = ".".join((nom_tab_or_view, ext)).lower() 1906 1907 file_path = os.path.join(dir, file_name) 1908 file_path_zip = None 1909 if zipped: 1910 file_path_zip = "{}.zip".format(os.path.splitext(file_path)[0]) 1911 1912 exists = (os.path.exists(file_path) and not file_path_zip) or (file_path_zip and os.path.exists(file_path_zip)) 1913 1914 return file_path, file_path_zip, exists
Compone el/los path para el/los vector_file de una tabla y determina si exists
Arguments:
- nom_tab_or_view:
- dir:
- file_name:
- ext:
- zipped:
Returns:
file_path (str), file_path_zip (str), exists (bool)
1916 @print_to_log_exception() 1917 def create_json_tab_or_view(self, nom_tab_or_view, dir='.', file_name=None, overwrite=True, cols=None, zipped=True, 1918 filter_sql=None, *args_sql): 1919 """ 1920 1921 Args: 1922 nom_tab_or_view (str): Nombre tabla o vista 1923 dir (str): 1924 file_name (str): 1925 overwrite (bool): 1926 cols (list): columnas 1927 zipped (bool): 1928 filter_sql (str): 1929 *args_sql: lista de argumentos a pasar al filtro sql 1930 Returns: 1931 file_path (str) 1932 """ 1933 file_path, file_path_zip, exists = self.verificar_path_vector_file( 1934 nom_tab_or_view, dir, file_name, "json", zipped) 1935 1936 if overwrite or not exists: 1937 # Se calculan las columnas para hacer get de la fila con el orden en las columnas de la tabla 1938 if not cols: 1939 dd_tab = self.get_dd_table(nom_tab_or_view) 1940 cols = dd_tab.cols 1941 sql = sql_tab(nom_tab_or_view, 1942 filter_sql=filter_sql, 1943 columns=cols) 1944 1945 file_path_res = vector_file_from_gen_ora_sql(file_path, "json", self.generator_rows_sql(sql, *args_sql), 1946 zipped=zipped) 1947 else: 1948 file_path_res = file_path if not zipped else file_path_zip 1949 1950 return file_path_res
Arguments:
- nom_tab_or_view (str): Nombre tabla o vista
- dir (str):
- file_name (str):
- overwrite (bool):
- cols (list): columnas
- zipped (bool):
- filter_sql (str):
- *args_sql: lista de argumentos a pasar al filtro sql
Returns:
file_path (str)
1952 @print_to_log_exception() 1953 def create_csv_tab_or_view(self, nom_tab_or_view, dir='.', file_name=None, overwrite=True, cols=None, zipped=True, 1954 filter_sql=None, *args_sql): 1955 """ 1956 1957 Args: 1958 nom_tab_or_view (str): Nombre tabla o vista 1959 dir (str): 1960 file_name (str): 1961 overwrite (bool): 1962 cols (list): columnas 1963 zipped (bool): 1964 filter_sql (str): 1965 *args_sql: lista de argumentos a pasar al filtro sql 1966 1967 Returns: 1968 file_path_res (str) 1969 """ 1970 file_path, file_path_zip, exists = self.verificar_path_vector_file( 1971 nom_tab_or_view, dir, file_name, "csv", zipped) 1972 1973 if overwrite or not exists: 1974 if not cols: 1975 dd_tab = self.get_dd_table(nom_tab_or_view) 1976 cols = dd_tab.cols 1977 sql = sql_tab(nom_tab_or_view, 1978 filter_sql=filter_sql, 1979 columns=cols) 1980 1981 # Para el formato geocsv que acepta GDAL se añade fichero con los tipos de columna 1982 tip_cols_csv = [] 1983 for col in cols: 1984 r_tip_col = self.row_table("user_tab_columns", 1985 "table_name = :1 and column_name = :2", 1986 nom_tab_or_view.upper(), col.upper()) 1987 dtype = r_tip_col.DATA_TYPE 1988 dlength = r_tip_col.DATA_LENGTH 1989 dprecision = r_tip_col.DATA_PRECISION 1990 dscale = r_tip_col.DATA_SCALE 1991 1992 if dtype == "DATE": 1993 tip_cols_csv.append('"DateTime"') 1994 elif dtype == "FLOAT": 1995 tip_cols_csv.append('"Real({}.{})"'.format(dlength, dprecision)) 1996 elif dtype == "NUMBER": 1997 if dscale and dscale != 0: 1998 tip_cols_csv.append('"Real({}.{})"'.format(dprecision, dscale)) 1999 elif dprecision: 2000 tip_cols_csv.append('"Integer({})"'.format(dprecision)) 2001 else: 2002 tip_cols_csv.append('"Real(10.8)"') 2003 elif dtype == "SDO_GEOMETRY": 2004 tip_cols_csv.append('"WKT"') 2005 else: 2006 tip_cols_csv.append('"String({})"'.format(round(dlength * 1.25))) 2007 2008 file_path_res = vector_file_from_gen_ora_sql(file_path, "csv", 2009 self.generator_rows_sql(sql, *args_sql, geom_format="as_wkt"), 2010 zipped=zipped, cols_csv=cols, tip_cols_csv=tip_cols_csv) 2011 else: 2012 file_path_res = file_path if not zipped else file_path_zip 2013 2014 return file_path_res
Arguments:
- nom_tab_or_view (str): Nombre tabla o vista
- dir (str):
- file_name (str):
- overwrite (bool):
- cols (list): columnas
- zipped (bool):
- filter_sql (str):
- *args_sql: lista de argumentos a pasar al filtro sql
Returns:
file_path_res (str)
2016 @print_to_log_exception() 2017 def create_geojsons_tab_or_view(self, nom_tab_or_view, dir='.', file_name_prefix=None, by_geom=False, 2018 dir_topojson=None, overwrite=True, cols=None, 2019 filter_sql=None, *args_sql): 2020 """ 2021 2022 Args: 2023 nom_tab_or_view (str): Nombre tabla (vigente o versionada) para entidad GIS 2024 dir (str="."): 2025 file_name_prefix (str=None): (opcional) prefijo del fichero 2026 by_geom (bool=False): (Opcional) si se querrán los geojsons por geometria. Si no se saca un unico geojson con 2027 la columna geometry como una GeometryCollection si la tabla es multigeom 2028 dir_topojson (str=None): path donde irán las conversiones 2029 overwrite (bool=True): 2030 cols (list=None): 2031 filter_sql (str=None): 2032 *args_sql: lista de argumentos a pasar al filtro sql 2033 2034 Returns: 2035 ok (bool) 2036 """ 2037 ext = "geo.json" 2038 sqls = {} 2039 dd_tab = self.get_dd_table(nom_tab_or_view) 2040 if not cols: 2041 cols = dd_tab.cols 2042 2043 if by_geom: 2044 c_alfas = [cn for cn in dd_tab.alfas() if cn in cols] 2045 c_geoms = [cn for cn in dd_tab.geoms() if cn in cols] 2046 for c_geom in c_geoms: 2047 sqls[c_geom] = sql_tab(nom_tab_or_view, filter_sql, c_alfas + [c_geom]) 2048 else: 2049 sqls[None] = sql_tab(nom_tab_or_view, filter_sql, cols) 2050 2051 if not file_name_prefix: 2052 file_name_prefix = nom_tab_or_view 2053 2054 for ng, sql in sqls.items(): 2055 file_name = file_name_prefix 2056 if ng: 2057 file_name = "-".join((file_name, ng)) 2058 2059 file_name = ".".join((file_name, ext)).lower() 2060 file_path = os.path.join(dir, file_name) 2061 2062 if overwrite or not os.path.exists(file_path): 2063 file_path = vector_file_from_gen_ora_sql(file_path, "geojson", 2064 self.generator_rows_sql(sql, *args_sql)) 2065 2066 if by_geom and dir_topojson and file_path: 2067 tip_geom = getattr(dd_tab, ng).GTYPE 2068 simplify = True 2069 if tip_geom.endswith("POINT"): 2070 simplify = False 2071 2072 topojson_utils.geojson_to_topojson(file_path, dir_topojson, 2073 simplify=simplify, 2074 overwrite=overwrite) 2075 2076 return True
Arguments:
- nom_tab_or_view (str): Nombre tabla (vigente o versionada) para entidad GIS
- dir (str="."):
- file_name_prefix (str=None): (opcional) prefijo del fichero
- by_geom (bool=False): (Opcional) si se querrán los geojsons por geometria. Si no se saca un unico geojson con la columna geometry como una GeometryCollection si la tabla es multigeom
- dir_topojson (str=None): path donde irán las conversiones
- overwrite (bool=True):
- cols (list=None):
- filter_sql (str=None):
- *args_sql: lista de argumentos a pasar al filtro sql
Returns:
ok (bool)