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
  11import inspect
  12import itertools
  13import json
  14import os
  15import shutil
  16import sys
  17from collections import namedtuple, OrderedDict
  18from functools import wraps
  19from logging import Logger
  20from subprocess import Popen, PIPE
  21from tempfile import SpooledTemporaryFile
  22from zipfile import ZipFile, ZIP_DEFLATED
  23
  24import lxml.etree as etree
  25import oracledb as cx_Oracle
  26
  27from apb_extra_utils import utils_logging
  28from apb_extra_utils.sql_parser import x_sql_parser
  29from apb_extra_utils.utils_logging import logger_path_logs
  30from apb_spatial_utils import topojson_utils
  31from . import sdo_geom as m_sdo_geom
  32
  33# Nombres tipo geometria GTYPE oracle por orden valor GTYPE
  34GTYPES_ORA = ["DEFAULT",
  35              "POINT",
  36              "LINE",
  37              "POLYGON",
  38              "COLLECTION",
  39              "MULTIPOINT",
  40              "MULTILINE",
  41              "MULTIPOLYGON"]
  42
  43# Se inicializan tipos de geometria Oracle
  44__class_tips_geom_ora = {}
  45for nom_tip in GTYPES_ORA:
  46    __class_tips_geom_ora[nom_tip] = namedtuple("gtype_" + nom_tip.upper(),
  47                                                ['TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID'])
  48
  49
  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)
  68
  69
  70# Caches para atributos de tablas_o_vistas de Oracle
  71__cache_pks_tab = {}
  72__cache_row_desc_tab = {}
  73__cache_tips_geom_tab = {}
  74__cache_row_class_tab = {}
  75
  76
  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]
  95
  96
  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.connect(dsn=dsn_ora, user=user_ora, password=psw_ora)
 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
 118
 119
 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()))
 131
 132
 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
 160
 161
 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
 196
 197
 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
 401
 402
 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
 430
 431
 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 {}
 480
 481
 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
 509
 510
 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)
 531
 532
 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
 557
 558
 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
 698
 699
 700def get_row_factory(curs, a_row_class=None, func_format_geom: str = None):
 701    """
 702    Retorna funcion para crear instancia clase a partir de los valores de una fila
 703
 704    Args:
 705        curs (cx_Oracle.Cursor): cursor de la consulta
 706        a_row_class (object=None): clase que se utilizará para crear la fila. Si no se informa se crea una clase
 707        func_format_geom (str=None): nombre función para formatear las geometrías SDO_GEOMETRY.
 708                (veanse las funciones sdo_geom as_[format])
 709
 710    Returns:
 711        function row_factory_func
 712    """
 713    con_db = curs.connection
 714    if not a_row_class:
 715        cursor_desc = get_row_descriptor(curs)
 716        a_row_class = get_row_class_cursor(cursor_desc, con_db)
 717
 718    f_sd_geom = m_sdo_geom.get_build_sdo_geom(con_db, func_format_geom=func_format_geom)
 719
 720    def row_factory_func(*vals_camps):
 721        has_sdo_geom = any(
 722            isinstance(val, cx_Oracle.DbObject) and val.type.name == "SDO_GEOMETRY"
 723            for val in vals_camps
 724        )
 725        if has_sdo_geom:
 726            vals_camps = [
 727                f_sd_geom(val) if isinstance(val, cx_Oracle.DbObject) and
 728                                  val.type.name == "SDO_GEOMETRY" else val
 729                for val in vals_camps
 730            ]
 731
 732        return a_row_class(*vals_camps)
 733
 734    return row_factory_func
 735
 736
 737def get_row_cursor(curs, rowfactory=None):
 738    """
 739    Retorna fila como clase que construye el rowfactory. Por defecto si no se informa esta clase heredará
 740    de 'namedtuple' con los campos como items más funcionalidad relacionada con la conexion y el descriptor de la fila
 741    con los tipos de campo para cada columna
 742
 743    Args:
 744        curs:
 745        rowfactory:
 746
 747    Returns:
 748
 749    """
 750    set_cursor_row_factory(curs, rowfactory)
 751
 752    row = curs.fetchone()
 753
 754    return row
 755
 756
 757def set_cursor_row_factory(curs, rowfactory=None):
 758    """
 759    Asigna al cursor la funcion rowfactory para crear las filas devueltas por el cursor
 760
 761    Args:
 762        curs (cx_Oracle.Cursor):
 763        rowfactory (function=None):
 764
 765    Returns:
 766        None
 767    """
 768    if not rowfactory and not curs.rowfactory:
 769        rowfactory = get_row_factory(curs)
 770
 771    if rowfactory:
 772        curs.rowfactory = rowfactory
 773
 774
 775def iter_execute_fetch_sql(con_db, sql_str, *args_sql, logger: Logger = None, **extra_params):
 776    """
 777    Itera y devuelve cada fila devuelta para la consulta sql_str
 778
 779    Args:
 780        con_db (cx_Oracle.Connection): conexión a Oracle
 781        sql_str (str): consulta SQL
 782        *args_sql: argumentos para la consulta SQL
 783        logger (Logger=None): Logger para registrar errores
 784        **extra_params: {
 785            "row_class": clase que se utilizará para cada fila. Vease get_row_factory()
 786            "as_format": formato en el que se devuelve cada fila.
 787                Las clases base row_cursor y row_table.
 788                (vease get_row_class_cursor() y get_row_class_tab()) responden
 789                por defecto a"as_xml()" y "as_json()"
 790            "geom_format": nombre función para formatear las geometrías SDO_GEOMETRY. (vease sdo_geom as_[format])
 791            "rowfactory": función rowfactory para crear las filas devueltas por el cursor
 792            "input_handler": función input_handler para el cursor (vease sdo_geom.get_sdo_input_handler())
 793        }
 794
 795    Returns:
 796        row_class o string en formato especificado en **extra_params["as_format"]
 797    """
 798    curs = None
 799
 800    try:
 801        curs = new_cursor(con_db,
 802                          input_handler=extra_params.pop("input_handler",
 803                                                         m_sdo_geom.get_sdo_input_handler()),
 804                          )
 805
 806        if (rowfactory := extra_params.pop("rowfactory", None)) is None:
 807            row_class = extra_params.pop("row_class", None)
 808            curs_aux = new_cursor(con_db)
 809            curs_aux.execute(sql_str, args_sql)
 810            rowfactory = get_row_factory(
 811                curs_aux, row_class,
 812                func_format_geom=extra_params.pop("geom_format", None)
 813            )
 814
 815        curs.prefetchrows = extra_params.pop("prefetchrows", 0)
 816        curs.arraysize = extra_params.pop("arraysize", 5_000)
 817        curs.execute(sql_str, args_sql)
 818        curs.rowfactory = rowfactory
 819
 820        num_rows = 0
 821        for reg in curs:
 822            if logger:
 823                num_rows += 1
 824                logger.debug(f"Num row {num_rows}: {reg}")
 825
 826            if "as_format" in extra_params:
 827                f_format = extra_params["as_format"]
 828                if f_format:
 829                    reg = getattr(reg, f_format)()
 830
 831            yield reg
 832
 833    except:
 834        if curs is not None:
 835            curs.close()
 836        raise
 837
 838    if curs is not None:
 839        curs.close()
 840
 841
 842def execute_fetch_sql(con_db, sql_str, *args_sql, **extra_params):
 843    """
 844    Devuelve la primera iteración sobre la consulta sql_str
 845
 846    Args:
 847        con_db (cx_Oracle.Connection): conexión a Oracle
 848        sql_str (str): consulta SQL
 849        *args_sql: argumentos para la consulta SQL
 850        **extra_params: {
 851            "row_class": clase que se utilizará para cada fila. Vease get_row_factory()
 852            "as_format": formato en el que se devuelve cada fila.
 853                Las clases base row_cursor y row_table.
 854                (vease get_row_class_cursor() y get_row_class_tab()) responden
 855                por defecto a"as_xml()" y "as_json()"
 856            "geom_format": nombre función para formatear las geometrías SDO_GEOMETRY. (vease sdo_geom as_[format])
 857            "rowfactory": función rowfactory para crear las filas devueltas por el cursor
 858            "input_handler": función input_handler para el cursor (vease sdo_geom.get_sdo_input_handler())
 859        }
 860
 861    Returns:
 862        row_class o string en formato especificado en **extra_params["as_format"]
 863    """
 864    reg = None
 865    for row in iter_execute_fetch_sql(con_db, sql_str, *args_sql, **extra_params):
 866        reg = row
 867        break
 868
 869    return reg
 870
 871
 872def dict_as_sql_bind_and_params(dict_vals, bool_rel="and", filter_oper="="):
 873    """
 874    A partir de un dict de {nom_camps:value_camps} devuelve un string en forma
 875    de SQL FILTER bindind (camp_a = :1 and camp_b = :2) y la lista de params que se asignarán via binding
 876    El BOOL_REL podrá ser 'AND', 'OR' o ',' para el SET de los updates
 877    El FILTER_OPER podrá ser '=', '!=' o ':=' para el SET de los updates
 878
 879    Args:
 880        dict_vals:
 881        bool_rel:
 882        filter_oper:
 883
 884    Returns:
 885        str, [params*]
 886    """
 887    query_elems = {}
 888    for nom_camp, val_clau in dict_vals.items():
 889        sql_str = str(nom_camp) + " " + filter_oper + " :" + str(nom_camp)
 890        query_elems[sql_str] = val_clau
 891
 892    sql = (" " + bool_rel.strip().upper() + " ").join(query_elems.keys())
 893
 894    return sql, list(query_elems.values())
 895
 896
 897class SerializableGenerator(list):
 898    """Generator that is serializable by JSON
 899
 900    It is useful for serializing huge data by JSON
 901    It can be used in a generator of json chunks used e.g. for a stream
 902    ('[1', ']')
 903    # >>> for chunk in iter_json:
 904    # ...     stream.write(chunk)
 905    # >>> SerializableGenerator((x for x in range(3)))
 906    # [<generator object <genexpr> at 0x7f858b5180f8>]
 907    """
 908
 909    def __init__(self, iterable):
 910        super().__init__()
 911        tmp_body = iter(iterable)
 912        try:
 913            self._head = iter([next(tmp_body)])
 914            self.append(tmp_body)
 915        except StopIteration:
 916            self._head = []
 917
 918    def __iter__(self):
 919        return itertools.chain(self._head, *self[:1])
 920
 921
 922def geojson_from_gen_ora_sql(generator_sql, as_string=False):
 923    """
 924    Devuelve diccionario geojson para un generator de rows de Oracle
 925
 926    Args:
 927        generator_sql (function generator):
 928        as_string (bool): (opcional) indica si se querrá el geojson como un string
 929
 930    Returns:
 931        geojson (dict ó str)
 932    """
 933    vals = {"type": "FeatureCollection",
 934            "features": [getattr(r, "__geo_interface__")
 935                         for r in generator_sql
 936                         if getattr(r, "__geo_interface__")]}
 937
 938    ret = vals
 939    if as_string:
 940        ret = json.dumps(vals,
 941                         ensure_ascii=False)
 942
 943    return ret
 944
 945
 946def vector_file_from_gen_ora_sql(file_path, vector_format, func_gen, zipped=False, indent_json=None, cols_csv=None,
 947                                 tip_cols_csv=None):
 948    """
 949    A partir del resultado de una query SQL devuelve un file_object formateado segun formato
 950
 951    Args:
 952        file_path (str): path del fichero a grabar
 953        vector_format (str): tipo formato (CSV, JSON, GEOJSON)
 954        func_gen (generator function): funcion que devuelva filas sql en forma de row_cursor o row_table
 955                                      (vease funciones generator_rows_sql() o generator_rows_table())
 956        zipped (bool=False): Devuelve fichero en un fichero comprimido (.zip)
 957        indent_json (int):
 958        cols_csv (list): Lista de columnes del CSV
 959        tip_cols_csv (list): Lista de tipos de columnas para CSV.
 960                             Revisar especificacion en https://giswiki.hsr.ch/GeoCSV
 961    Returns:
 962        str: pathfile del fichero generado
 963    """
 964    vector_format = vector_format.lower()
 965    newline = None if vector_format != "csv" else ""
 966    file_path_csvt = None
 967    str_csvt = None
 968    dir_base = os.path.dirname(file_path)
 969    if dir_base:
 970        os.makedirs(dir_base, exist_ok=True)
 971
 972    # Se hace en memoria o recursos locales (SpooledTemporaryFile) para evitar trabajar lo minimo en la red
 973    # si se da el caso en el path fichero del fichero indicado. Cuando se quiere grabar en recurso local el tiempo
 974    # perdido por utilizar un fichero temporal debería ser despreciable comparado con la complejidad añadida al código
 975    # para decidir si usar o no el SpooledTemporaryFile
 976    with SpooledTemporaryFile(mode="w+", encoding="utf-8", newline=newline) as temp_file:
 977        if vector_format == "geojson":
 978            json.dump(geojson_from_gen_ora_sql(func_gen),
 979                      temp_file,
 980                      ensure_ascii=False,
 981                      indent=indent_json,
 982                      cls=geojson_encoder)
 983        elif vector_format == "json":
 984            json.dump(SerializableGenerator((r.vals() for r in func_gen)),
 985                      temp_file,
 986                      ensure_ascii=False,
 987                      indent=indent_json,
 988                      cls=geojson_encoder)
 989        elif vector_format == "csv":
 990            writer = csv.DictWriter(temp_file, fieldnames=cols_csv)
 991            writer.writeheader()
 992
 993            for r in func_gen:
 994                writer.writerow(r.vals())
 995
 996        temp_file.seek(0)
 997        if tip_cols_csv:
 998            file_path_csvt = ".".join((os.path.splitext(file_path)[0], "csvt"))
 999            str_csvt = ",".join(tip_cols_csv)
1000
1001        if zipped:
1002            file_path_res = "{}.zip".format(os.path.splitext(file_path)[0])
1003            with SpooledTemporaryFile() as zip_temp_file:
1004                with ZipFile(zip_temp_file, "w", compression=ZIP_DEFLATED, allowZip64=True) as my_temp_zip:
1005                    my_temp_zip.writestr(zinfo_or_arcname=os.path.basename(file_path), data=temp_file.read())
1006                    if str_csvt:
1007                        my_temp_zip.writestr(zinfo_or_arcname=os.path.basename(file_path_csvt), data=str_csvt)
1008
1009                zip_temp_file.seek(0)
1010                with open(file_path_res, mode="wb") as file_res:
1011                    shutil.copyfileobj(zip_temp_file, file_res)
1012        else:
1013            file_path_res = file_path
1014            with open(file_path_res, mode="w", encoding="utf-8") as file_res:
1015                shutil.copyfileobj(temp_file, file_res)
1016            if str_csvt:
1017                with open(file_path_csvt, mode="w") as csvt_file:
1018                    csvt_file.write(str_csvt)
1019
1020    return file_path_res
1021
1022
1023class geojson_encoder(json.JSONEncoder):
1024    """
1025    Class Encoder to parser SDO_GEOM to GEOJSON
1026    """
1027    __num_decs__ = 9
1028
1029    def default(self, obj_val):
1030        """
1031        Redefine default para tratar las geometrias SDO_GEOM y convertirlas a geojson y las fechas a iso_format
1032        Args:
1033            obj_val: valor
1034
1035        Returns:
1036            object encoded
1037        """
1038        if isinstance(obj_val, m_sdo_geom.sdo_geom):
1039            return obj_val.as_geojson()
1040        elif isinstance(obj_val, (datetime.datetime, datetime.date)):
1041            return obj_val.isoformat()
1042        elif isinstance(obj_val, cx_Oracle.LOB):
1043            return obj_val.read()
1044        else:
1045            return json.JSONEncoder.default(self, obj_val)
1046
1047
1048def print_to_log_exception(a_type_exc=Exception, lanzar_exc=False):
1049    """
1050    Decorator para imprimir en el log una excepción capturada por un metodo de la clase
1051
1052    Returns:
1053        function
1054    """
1055
1056    def decor_print_log_exception(func):
1057        @wraps(func)
1058        def meth_wrapper(cls, *args, **kwargs):
1059            try:
1060                return func(cls, *args, **kwargs)
1061            except a_type_exc:
1062                error_type, error_instance, traceback = sys.exc_info()
1063
1064                error_msg = "Error al executar funció '{clas}.{func}()' \n" \
1065                            "Arguments: {args}\n" \
1066                            "Fitxer:    {file}".format(
1067                    file=inspect.getmodule(func).__file__,
1068                    clas=cls.__class__.__name__,
1069                    func=func.__name__,
1070                    args=", ".join(["'{}'".format(arg) for arg in args] +
1071                                   ["'{}={}'".format(a, b) for a, b in kwargs.items()]))
1072
1073                if hasattr(error_instance, "output"):
1074                    error_msg += "\n" \
1075                                 "Output: {}".format(error_instance.output)
1076
1077                cls.print_log_exception(error_msg)
1078                if lanzar_exc:
1079                    raise error_instance
1080
1081        return meth_wrapper
1082
1083    return decor_print_log_exception
1084
1085
1086class gestor_oracle(object):
1087    """
1088    Clase que gestionará distintas conexiones a Oracle y facilitará operaciones sobre la BBDD
1089    """
1090    tip_number = cx_Oracle.NUMBER
1091    tip_string = cx_Oracle.STRING
1092    tip_clob = cx_Oracle.CLOB
1093    tip_blob = cx_Oracle.BLOB
1094    tip_date = cx_Oracle.DATETIME
1095    tip_fix_char = cx_Oracle.FIXED_CHAR
1096
1097    __slots__ = 'nom_con_db', '__con_db__', '__user_con_db__', \
1098        '__psw_con_db__', '__dsn_ora__', '__call_timeout__', '__schema_con_db__', 'logger'
1099
1100    def __init__(self, user_ora, psw_ora, dsn_ora, a_logger=None, call_timeout: int = None, schema_ora=None):
1101        """
1102        Inicializa gestor de Oracle para una conexion cx_Oracle a Oracle
1103        Se puede pasar por parametro un logger o inicializar por defecto
1104
1105        Args:
1106            user_ora {str}: Usuario/schema Oracle
1107            psw_ora {str}: Password usuario
1108            dsn_ora {str}: DSN Oracle (Nombre instancia/datasource de Oracle
1109                    según TSN o string tal cual devuelve cx_Oracle.makedsn())
1110            call_timeout (int=None): miliseconds espera per transaccio
1111            a_logger:
1112            schema_ora(str=None): indicate scheme when it is different from the user, default schema = user
1113        """
1114        self.__con_db__ = None
1115        self.__call_timeout__ = call_timeout
1116        self.logger = a_logger
1117        self.__set_logger()
1118        self.__set_conexion(user_ora, psw_ora, dsn_ora, schema_ora=schema_ora)
1119
1120    def __del__(self):
1121        """
1122        Cierra la conexion al matar la instancia
1123        """
1124        try:
1125            if hasattr(self, "__con_db__"):
1126                self.__con_db__.close()
1127        except:
1128            pass
1129
1130    def __repr__(self):
1131        """
1132        built_in que actua cuando se representa clase como STRING
1133
1134        Returns:
1135            str
1136        """
1137        repr_txt = "{}".format(self.nom_con_db)
1138
1139        return repr_txt
1140
1141    @staticmethod
1142    def log_dir():
1143        """
1144        Devuelve el directorio donde irán los logs indicado en la funcion apb_logging.logs_dir()
1145
1146        Returns:
1147            {str} - path del directorio de logs
1148        """
1149        return utils_logging.logs_dir(True)
1150
1151    def log_name(self):
1152        """
1153        Devuelve el nombre del fichero de log por defecto
1154
1155        Returns:
1156            {str} - Nombre fichero log por defecto
1157        """
1158        return self.__class__.__name__
1159
1160    def log_file_name(self):
1161        return "{}.(LOG_LEVEL).log".format(os.path.join(self.log_dir(), self.log_name()))
1162
1163    def __set_logger(self):
1164        """
1165        Asigna el LOGGER po defecto si este no se ha informado al inicializar el gestor
1166
1167        Returns:
1168        """
1169        if self.logger is None:
1170            self.logger = utils_logging.get_file_logger(self.log_name(), dir_log=self.log_dir())
1171
1172    def path_logs(self, if_exist=True):
1173        """
1174        Devuelve lista paths base de los logs vinculados al gestor
1175
1176        Args:
1177            if_exist (bool): Devuelve los paths si el fichero existe
1178
1179        Returns:
1180            list:
1181        """
1182        return logger_path_logs(self.logger)
1183
1184    def print_log(self, msg):
1185        """
1186        Sobre el logger escribe mensaje de info
1187
1188        Args:
1189            msg {str}: String con el mensaje
1190        """
1191        self.logger.info(msg)
1192
1193    def print_log_error(self, msg):
1194        """
1195        Sobre el logger escribe mensaje de error
1196
1197        Args:
1198            msg {str}: String con el mensaje
1199        """
1200        self.logger.error(msg)
1201
1202    def print_log_exception(self, msg):
1203        """
1204        Sobre el logger escribe excepcion
1205
1206        Args:
1207            msg {str}: String con el mensaje
1208        """
1209        self.logger.exception(msg)
1210
1211    @print_to_log_exception(lanzar_exc=True)
1212    def __set_conexion(self, user_ora, psw_ora, dsn_ora, schema_ora=None):
1213        """
1214        Añade conexion Oracle al gestor a partir de nombre de usuario/schema (user_ora), contraseña (psw_ora) y
1215        nombre datasource de la bbdd según tns_names (ds_ora).
1216
1217        La conexión quedará registrada como 'user_ora@ds_ora'
1218
1219        Args:
1220            user_ora {str}: Usuario/schema Oracle
1221            psw_ora {str}: Password usuario
1222            dsn_ora {str}: DSN Oracle (Nombre instancia/datasource de Oracle según TSN o string tal cual devuelve cx_Oracle.makedsn())
1223            schema_ora(str=None): indicate scheme when it is different from the user, default schema = user
1224
1225        """
1226        nom_con = "@".join((user_ora.upper(), dsn_ora.upper()))
1227        self.nom_con_db = nom_con
1228        self.__user_con_db__ = user_ora
1229        self.__psw_con_db__ = psw_ora
1230        self.__schema_con_db__ = schema_ora
1231        self.__dsn_ora__ = dsn_ora
1232        self.__con_db__ = get_oracle_connection(user_ora, psw_ora, dsn_ora, self.__call_timeout__,
1233                                                schema_ora=schema_ora)
1234
1235    @property
1236    @print_to_log_exception(cx_Oracle.Error, lanzar_exc=True)
1237    def con_db(self):
1238        """
1239        Return a cx_Oracle Conection live
1240        
1241        Returns:
1242            cx_Oracle.Connection
1243        """
1244        reconnect = False
1245        if (con_ora := self.__con_db__) is not None:
1246            try:
1247                con_ora.ping()
1248            except cx_Oracle.Error as exc:
1249                # Borramos las entradas de cache asociadas a la conexión que no responde
1250                del_cache_rel_con_db(get_nom_conexion(con_ora))
1251                try:
1252                    con_ora.close()
1253                except cx_Oracle.Error:
1254                    pass
1255                self.__con_db__ = con_ora = None
1256
1257        if con_ora is None:
1258            self.__set_conexion(
1259                self.__user_con_db__,
1260                self.__psw_con_db__,
1261                self.__dsn_ora__, schema_ora=self.__schema_con_db__)
1262
1263            con_ora = self.__con_db__
1264
1265        return con_ora
1266
1267    @print_to_log_exception(cx_Oracle.DatabaseError)
1268    def exec_trans_db(self, sql_str, *args_sql, **types_sql_args):
1269        """
1270        Ejecuta transaccion SQL
1271
1272        Args:
1273            sql_str (str): sql transaction (update, insert, delete}
1274            *args_sql: Lista argumentos a pasar
1275            **types_sql_args (OPCIONAL): Lista tipos cx_Oracle para cada argumento
1276
1277        Returns:
1278            ok {bool}: Si ha ido bien True si no False
1279        """
1280        curs_db = None
1281        try:
1282            curs_db = new_cursor(self.con_db,
1283                                 input_handler=m_sdo_geom.get_sdo_input_handler())
1284
1285            curs_db.setinputsizes(*types_sql_args.values())
1286
1287            curs_db.execute(sql_str,
1288                            args_sql)
1289        finally:
1290            if curs_db:
1291                curs_db.close()
1292
1293        return True
1294
1295    @print_to_log_exception(cx_Oracle.DatabaseError)
1296    def exec_script_plsql(self, sql_str):
1297        """
1298        Ejecuta script SQL
1299
1300        Args:
1301            sql_str {str}: sql script
1302
1303        Returns:
1304            ok {bool}: Si ha ido bien True si no False
1305        """
1306        curs_db = None
1307        try:
1308            curs_db = new_cursor(self.con_db)
1309            curs_db.execute(sql_str)
1310        finally:
1311            if curs_db:
1312                curs_db.close()
1313
1314        return True
1315
1316    @print_to_log_exception(cx_Oracle.DatabaseError)
1317    def callfunc_sql(self, nom_func, ret_cx_ora_tipo, *args_func):
1318        """
1319        Ejecuta funcion PL/SQL y retorna el valor
1320
1321        Args:
1322            nom_func (str): Nombre de la funcion PL/SQL
1323            ret_cx_ora_tipo (cx_Oracle TIPO): El retorno de la función en cx_Oracle (cx_Oracle.NUMBER,
1324                                            cx_Oracle.STRING,...)
1325            *args_func: Argumentos de la funcion PL/SQL
1326
1327        Returns:
1328            Valor retornado por la función PL/SQL
1329        """
1330        curs = None
1331        try:
1332            curs = new_cursor(self.con_db)
1333            ret = curs.callfunc(nom_func,
1334                                ret_cx_ora_tipo,
1335                                args_func)
1336        finally:
1337            if curs:
1338                curs.close()
1339
1340        return ret
1341
1342    @print_to_log_exception(cx_Oracle.DatabaseError)
1343    def callproc_sql(self, nom_proc, *args_proc):
1344        """
1345        Ejecuta procedimiento PL/SQL
1346
1347        Args:
1348            nom_proc (str): Nombre del procedimiento PL/SQL
1349            *args_proc: Argumentos del procedimiento
1350
1351        Returns:
1352            ok {bool}: Si ha ido bien True si no False
1353        """
1354        curs = None
1355        try:
1356            curs = new_cursor(self.con_db)
1357            curs.callproc(nom_proc,
1358                          args_proc)
1359        finally:
1360            if curs:
1361                curs.close()
1362
1363        return True
1364
1365    @print_to_log_exception(cx_Oracle.DatabaseError)
1366    def row_sql(self, sql_str, *args_sql, **extra_params):
1367        """
1368        Retorna la fila resultante de la query sql SQL_STR con los parámetros *ARGS_SQL.
1369        Se puede informar **EXTRA_PARAMS con algunos de los siguientes parámetros:
1370            INPUT_HANDLER funcion que tratará los bindings de manera específica
1371            OUTPUT_HANLER funcion que tratará los valores de las columnas de modo específico
1372            ROW_CLASS define que con que clase se devolverá cada fila. Por defecto la que devuleve la funcion
1373                      'apb_cx_oracle_spatial.get_row_class_cursor()'
1374            AS_FORMAT (as_xml, as_json, as_geojson) devuelve fila en el formato especificado. La row_class deberá
1375                        responder a esas funciones
1376
1377        Args:
1378            sql_str:
1379            *args_sql:
1380            **extra_params: {
1381                "row_class": clase que se utilizará para cada fila. Vease get_row_factory()
1382                "as_format": formato en el que se devuelve cada fila.
1383                    Las clases base row_cursor y row_table.
1384                    (vease get_row_class_cursor() y get_row_class_tab()) responden
1385                    por defecto a"as_xml()" y "as_json()"
1386                "geom_format": nombre función para formatear las geometrías SDO_GEOMETRY. (vease sdo_geom as_[format])
1387                "rowfactory": función rowfactory para crear las filas devueltas por el cursor
1388                "input_handler": función input_handler para el cursor (vease sdo_geom.get_sdo_input_handler())
1389            }
1390
1391        Returns:
1392            object (instancia que debería ser o heredar de las clases row_cursor o row_table)
1393        """
1394        return execute_fetch_sql(self.con_db,
1395                                 sql_str,
1396                                 *args_sql,
1397                                 **extra_params)
1398
1399    @print_to_log_exception(cx_Oracle.DatabaseError)
1400    def generator_rows_sql(self, sql_str, *args_sql, **extra_params):
1401        """
1402        Ejecuta consulta SQL de forma iterativa retornando cada fila como un objeto row_class (por defecto row_cursor)
1403
1404        Se puede informar **EXTRA_PARAMS con algunos de los siguientes parámetros:
1405            INPUT_HANDLER funcion que tratará los bindings de manera específica
1406            ROW_CLASS define que con que clase se devolverá cada fila. Por defecto la que devuleve la funcion
1407                      'apb_cx_oracle_spatial.get_row_class_cursor()'
1408            AS_FORMAT (as_xml, as_json, as_geojson, ...) devuelve fila en el formato especificado. La row_class deberá
1409                        responder a esas funciones
1410
1411        Args:
1412            sql_str:
1413            *args_sql:
1414            **extra_params: {
1415                "row_class": clase que se utilizará para cada fila. Vease get_row_factory()
1416                "as_format": formato en el que se devuelve cada fila.
1417                    Las clases base row_cursor y row_table.
1418                    (vease get_row_class_cursor() y get_row_class_tab()) responden
1419                    por defecto a"as_xml()" y "as_json()"
1420                "geom_format": nombre función para formatear las geometrías SDO_GEOMETRY. (vease sdo_geom as_[format])
1421                "rowfactory": función rowfactory para crear las filas devueltas por el cursor
1422                "input_handler": función input_handler para el cursor (vease sdo_geom.get_sdo_input_handler())
1423            }
1424
1425        Returns:
1426            object (instancia que debería ser o heredar de las clases row_cursor o row_table)
1427        """
1428        for reg in iter_execute_fetch_sql(self.con_db, sql_str, *args_sql,
1429                                          logger=self.logger,
1430                                          **extra_params):
1431            yield reg
1432
1433    def rows_sql(self, sql_str, *args_sql):
1434        """
1435        Vease funcion 'generator_rows_sql()'
1436        Args:
1437            sql_str:
1438            *args_sql:
1439
1440        Returns:
1441            list
1442        """
1443        return list(self.generator_rows_sql(sql_str, *args_sql))
1444
1445    def get_primary_key_table(self, nom_tab_or_view):
1446        """
1447        Retorna lista con las columnas que conforman la primary key de una tabla/vista
1448        Args:
1449            nom_tab_or_view:
1450
1451        Returns:
1452            list con campos clave
1453        """
1454        return get_pk_tab(self.con_db, nom_tab_or_view)
1455
1456    @print_to_log_exception(lanzar_exc=True)
1457    def get_dd_table(self, nom_tab_or_view):
1458        """
1459        Retorna instancia row_table con los tipos para cada columna
1460        Args:
1461            nom_tab_or_view:
1462
1463        Returns:
1464            object de clase row_table con los tipos de cada columna como valores
1465        """
1466        return get_row_desc_tab(self.con_db, nom_tab_or_view)
1467
1468    def generator_rows_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql, **extra_params):
1469        """
1470        Retorna los registros que cumplan con el FILTER_SQL sobre la tabla o vista indicada.
1471
1472        La tabla se puede referenciar en el filtro con el alias 'TAB'
1473
1474        Si el FILTER_SQL utiliza valores por binding (:1, :2,...) estos se indicaran por orden en ARGS_FILTER_SQL
1475
1476        Args:
1477            nom_tab_or_view: Nombre de la tabla o vista sobre la que se hará la consulta
1478            filter_sql: Filtre sobre la taula
1479            args_filter_sql: Valors en ordre de binding per passar
1480            extra_params: Vease generator_rows_sql()
1481
1482        Yields:
1483             row_table: regs. clase row_table (mirar get_row_class_tab())
1484        """
1485        for reg in self.generator_rows_sql(sql_tab(nom_tab_or_view,
1486                                                   filter_sql),
1487                                           *args_filter_sql,
1488                                           row_class=extra_params.pop(
1489                                               "row_class",
1490                                               get_row_class_tab(self.con_db, nom_tab_or_view)),
1491                                           **extra_params):
1492            yield reg
1493
1494    def generator_rows_interact_geom(self, nom_tab, a_sdo_geom, cols_geom=None, geom_format=None):
1495        """
1496        Retorna las filas de una tabla que interactuan con una geometria
1497
1498        Args:
1499            nom_tab: nombre de la tabla
1500            a_sdo_geom: geometria clase sdo_geom
1501            cols_geom (default=None): Lista con nombre de columnas geométricas sobre las que se quiere aplicar filtro
1502            geom_format:
1503        Yields:
1504            row_table: regs. clase row_table (mirar get_row_class_tab())
1505        """
1506        # Uso de " <>'FALSE'" por fallo de Oracle usando "= 'TRUE'"
1507        filter_interact_base = "SDO_ANYINTERACT({camp_geom}, :1) <> 'FALSE'"
1508
1509        if not cols_geom:
1510            cols_geom = get_tips_geom_tab(self.con_db, nom_tab).keys()
1511
1512        if cols_geom:
1513            filter_sql = " OR ".join([filter_interact_base.format(camp_geom=ng) for ng in cols_geom])
1514            for reg in self.generator_rows_table(nom_tab, filter_sql, a_sdo_geom.as_ora_sdo_geometry(),
1515                                                 geom_format=geom_format):
1516                yield reg
1517
1518    def rows_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql):
1519        """
1520        Vease funcion 'generator_rows_table()'
1521
1522        Args:
1523            nom_tab_or_view:
1524            filter_sql:
1525            *args_filter_sql:
1526
1527        Returns:
1528            dict
1529        """
1530        gen_tab = self.generator_rows_table(nom_tab_or_view,
1531                                            filter_sql,
1532                                            *args_filter_sql)
1533        pk_tab = self.get_primary_key_table(nom_tab_or_view)
1534        l_pk = len(pk_tab)
1535        if l_pk == 0:
1536            return [r for r in gen_tab]
1537        else:
1538            def f_key(r):
1539                return getattr(r, pk_tab[0])
1540
1541            if l_pk > 1:
1542                def f_key(r): return tuple(map(lambda nf: getattr(r, nf), pk_tab))
1543            return {f_key(r): r for r in gen_tab}
1544
1545    def row_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql, **extra_params):
1546        """
1547        Retorna primer registro para el FILTER_SQL sobre la tabla o vista indicada
1548        Si el FILTER_SQL utiliza valores por binding (:1, :2,...) estos se indicaran por orden en ARGS_FILTER_SQL
1549
1550        Args:
1551            nom_tab_or_view:
1552            filter_sql:
1553            *args_filter_sql:
1554            **extra_params:
1555
1556        Returns:
1557            object de la clase row_table o especificada en **extra_params['row_class']
1558        """
1559        gen = self.generator_rows_table(nom_tab_or_view,
1560                                        filter_sql,
1561                                        *args_filter_sql,
1562                                        **extra_params)
1563        return next(gen, None)
1564
1565    def row_table_at(self, nom_tab_or_view, *vals_key):
1566        """
1567        Devuelve row_tabla_class para el registro que de la tabla_vista que cumpla con la clave
1568
1569        Args:
1570            nom_tab_or_view:
1571            *vals_key:
1572
1573        Returns:
1574            object de la clase row_table o especificada en **extra_params['row_class']
1575        """
1576        return self.exist_row_tab(nom_tab_or_view,
1577                                  {nc: val for nc, val in zip(self.get_primary_key_table(nom_tab_or_view),
1578                                                              vals_key)})
1579
1580    def test_row_table(self, row_tab, a_sql, *args_sql):
1581        """
1582        Testea un registro de tabla (clase rwo_table) cumpla con sql indicado
1583
1584        Args:
1585            row_tab: registro de tabla en forma de clase row_table
1586            a_sql: string con sql a testear
1587
1588        Returns:
1589            bool: True o False según cumpla con el SQL indicado
1590        """
1591        sql_pk = dict_as_sql_bind_and_params(row_tab.pk_vals())
1592        query_sql = "{} AND ({})".format(sql_pk[0], a_sql)
1593
1594        ret = False
1595        if self.row_table(row_tab.nom_tab, query_sql, *(tuple(sql_pk[1]) + tuple(args_sql))):
1596            ret = True
1597
1598        return ret
1599
1600    def insert_row_tab(self, nom_tab, dict_vals_param=None, dict_vals_str=None, pasar_nulls=False):
1601        """
1602        Inserta registro en la tabla indicada. Los valores para cada columna se pasarán a través de dict_vals_param
1603        como bindings o a través de dict_vals_str directemente en el string del sql ejecutado
1604        Args:
1605            nom_tab: nombre de la tabla
1606            dict_vals_param: diccionario indexado por columnas-valores. Los valores se pasarán como bindings
1607            dict_vals_str: diccionario indexado por columnas-valores a asignar como strings. El valor se pasa
1608                        directamente como asignacion en la senetencia sql
1609            pasar_nulls: (opcional) por defecto False. Indica si los valores NULL se asignarán o no
1610
1611        Returns:
1612            row_table (si genera el registro) o False si va mal la operación
1613        """
1614        if not dict_vals_param:
1615            dict_vals_param = {}
1616        if not dict_vals_str:
1617            dict_vals_str = {}
1618
1619        ora_dict_vals_param = self.get_vals_tab_for_transdb(nom_tab, dict_vals_param, pasar_nulls=pasar_nulls)
1620
1621        if pasar_nulls:
1622            # Se revisa que en un campo geometrico se asigne None y por lo tanto se asigne valor via STR
1623            geoms_null = [ng for ng, val in ora_dict_vals_param.items()
1624                          if not val and self.get_tip_camp_geom(nom_tab, ng)]
1625            if geoms_null:
1626                keys_str = [nc.upper() for nc in dict_vals_str.keys()]
1627                for gn in geoms_null:
1628                    ora_dict_vals_param.pop(gn)
1629                    if gn.upper() not in keys_str:
1630                        dict_vals_str[gn.upper()] = "NULL"
1631
1632        params = []
1633        nom_camps = []
1634        vals_camps = []
1635        for nom_camp, val_camp in ora_dict_vals_param.items():
1636            if val_camp is None:
1637                continue
1638            nom_camps.append(nom_camp)
1639            vals_camps.append(":" + nom_camp)
1640            params.append(val_camp)
1641
1642        for nom_camp, val_camp_str in dict_vals_str.items():
1643            if val_camp_str is None:
1644                continue
1645            nom_camps.append(nom_camp)
1646            vals_camps.append(str(val_camp_str))
1647
1648        row_desc_tab = get_row_desc_tab(self.con_db, nom_tab)
1649        pk_binds = {k: new_cursor(self.con_db).var(ora_tip_camp) for k, ora_tip_camp in row_desc_tab.pk_vals().items()}
1650        if not pk_binds:
1651            pk_binds = {'ROWID': new_cursor(self.con_db).var(cx_Oracle.ROWID)}
1652        str_pk_camps = ",".join(pk_binds.keys())
1653        str_pk_binds = ",".join(list(map(lambda x: ":ret_" + str(x), pk_binds.keys())))
1654        params += list(pk_binds.values())
1655
1656        a_sql_res = f"insert into {nom_tab}({','.join(nom_camps)}) values({','.join(vals_camps)}) " \
1657                    f"returning {str_pk_camps} into {str_pk_binds}"
1658
1659        ok = self.exec_trans_db(a_sql_res, *params)
1660        if ok:
1661            pk_vals = {k: curs_var.getvalue(0)[0] for k, curs_var in pk_binds.items()}
1662            return self.exist_row_tab(nom_tab, pk_vals)
1663
1664        return ok
1665
1666    def update_row_tab(self, nom_tab, dict_clau_reg, dict_vals_param=None, dict_vals_str=None, pasar_nulls=None):
1667        """
1668        Actualiza registro en la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor}
1669        Los valores para cada columna se pasarán a través de dict_vals_param como bindings o a través de
1670        dict_vals_str directemente en el string del sql ejecutado
1671
1672        Args:
1673            nom_tab: nombre de la tabla
1674            dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar
1675            dict_vals_param: diccionario indexado por columnas-valores. Los valores se pasarán como bindings
1676            dict_vals_str: diccionario indexado por columnas-valores a asignar como strings. El valor se pasa
1677                        directamente como asignacion en la senetencia sql
1678            pasar_nulls: (opcional) por defecto False. Indica si los valores NULL se asignarán o no
1679
1680        Returns:
1681            row_table (si genera el registro) o False si va mal la operación
1682        """
1683        if not dict_vals_param:
1684            dict_vals_param = {}
1685        if not dict_vals_str:
1686            dict_vals_str = {}
1687
1688        ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg)
1689        ora_dict_vals_param = self.get_vals_tab_for_transdb(nom_tab, dict_vals_param, pasar_nulls=pasar_nulls)
1690
1691        if pasar_nulls:
1692            # Se revisa que en un campo geometrico se asigne None y por lo tanto se asigne valor via STR
1693            geoms_null = [ng for ng, val in ora_dict_vals_param.items()
1694                          if not val and self.get_tip_camp_geom(nom_tab, ng)]
1695            if geoms_null:
1696                keys_str = [nc.upper() for nc in dict_vals_str.keys()]
1697                for gn in geoms_null:
1698                    ora_dict_vals_param.pop(gn)
1699                    if gn.upper() not in keys_str:
1700                        dict_vals_str[gn.upper()] = "NULL"
1701
1702        (sql_set_camps, params_set_camps) = dict_as_sql_bind_and_params(ora_dict_vals_param,
1703                                                                        ",", "=")
1704
1705        (query_clau, params_filter) = dict_as_sql_bind_and_params(ora_dict_clau_reg)
1706
1707        params = params_set_camps + params_filter
1708
1709        for nom_camp, val_camp_str in dict_vals_str.items():
1710            if sql_set_camps:
1711                sql_set_camps += " , "
1712            sql_set_camps += nom_camp + "=" + val_camp_str
1713
1714        ok = None
1715        if sql_set_camps:
1716            a_sql_res = f"update {nom_tab} set {sql_set_camps} where {query_clau}"
1717
1718            ok = self.exec_trans_db(a_sql_res, *params)
1719
1720            if ok:
1721                return self.exist_row_tab(nom_tab, dict_clau_reg)
1722
1723        return ok
1724
1725    def remove_row_tab(self, nom_tab, dict_clau_reg):
1726        """
1727        Borra registro en la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor}
1728
1729        Args:
1730            nom_tab: nombre de la tabla
1731            dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar
1732
1733        Returns:
1734            bool según vaya la operación
1735        """
1736        ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg)
1737
1738        a_sql_tmpl = "delete {nom_tab} where {query_clau}"
1739
1740        (sql_filter, params) = dict_as_sql_bind_and_params(ora_dict_clau_reg)
1741
1742        a_sql_res = a_sql_tmpl.format(nom_tab=nom_tab,
1743                                      query_clau=sql_filter)
1744
1745        return self.exec_trans_db(a_sql_res, *params)
1746
1747    def exist_row_tab(self, nom_tab, dict_clau_reg, **extra_params):
1748        """
1749        Devuelve registro de la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor}
1750
1751        Args:
1752            nom_tab: nombre de la tabla
1753            dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar
1754            **extra_params:
1755
1756        Returns:
1757            object de la clase row_table o especificada en **extra_params['row_class']
1758        """
1759        ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg)
1760
1761        (filter_sql, params) = dict_as_sql_bind_and_params(ora_dict_clau_reg, "and", "=")
1762
1763        return self.row_table(nom_tab, filter_sql, *params, **extra_params)
1764
1765    def get_vals_tab_for_transdb(self, nom_tab, dict_camps_vals, pasar_nulls=True):
1766        """
1767        Para un tabla y diccionario columnas-valores devuelve diccionario indexado por las columnas con los valores
1768        convertidos a formato cx_Oracle según tipo de cada columna en Oracle
1769        Args:
1770            nom_tab: nombre de la tabla
1771            dict_camps_vals: diccionario indexado por columnas-valor a convertir a formato cx_Oracle
1772            pasar_nulls: (opcional) por defecto convertirá los None a NULL de Oracle
1773
1774        Returns:
1775            dict indexado por columnas con los valores convertidos a tipo cx_Oracle
1776        """
1777        dd_tab = get_row_desc_tab(self.con_db, nom_tab)
1778
1779        # Retorna dict con los campos a pasar por parametro
1780        d_params = {}
1781
1782        # Los nombres de campo siempre se buscarán en mayúsculas
1783        dict_camps_vals = {k.upper(): v for k, v in dict_camps_vals.items()}
1784
1785        for camp, tip_camp in dd_tab.vals().items():
1786            if camp not in dict_camps_vals:
1787                continue
1788
1789            val_camp = dict_camps_vals.get(camp)
1790            if not pasar_nulls and val_camp is None:
1791                continue
1792
1793            if isinstance(val_camp, m_sdo_geom.sdo_geom):
1794                var = val_camp.as_ora_sdo_geometry()
1795            else:
1796                try:
1797                    var = new_cursor(self.con_db).var(tip_camp)
1798                    var.setvalue(0, val_camp)
1799                except:
1800                    var = val_camp
1801
1802            d_params[camp] = var
1803
1804        return d_params
1805
1806    @print_to_log_exception()
1807    def run_sql_script(self, filename):
1808        """
1809        Ejecuta slq script (filename) sobre SQLPLUS
1810
1811        Args:
1812            filename: path del sql script
1813
1814        Returns:
1815
1816        """
1817        user_ora = self.con_db.username
1818        ds_ora = self.con_db.dsn
1819        nom_con = self.nom_con_db
1820        psw_ora = self.__psw_con_db__
1821        if psw_ora is None:
1822            print("ERROR - Conexión '" + nom_con + "' no está añadida al gestor!!")
1823            return
1824
1825        with open(filename, 'rb') as a_file:
1826            a_sql_command = a_file.read()
1827
1828        con_db_str = user_ora + "/" + psw_ora + "@" + ds_ora
1829        sqlplus = Popen(['sqlplus', '-S', con_db_str], stdin=PIPE, stdout=PIPE, stderr=PIPE)
1830        # sqlplus.stdin.write(a_sql_command)
1831
1832        (stdout, stderr) = sqlplus.communicate(a_sql_command)
1833
1834        self.print_log("Resultado lanzar script '{}': \n"
1835                       "{}".format(filename,
1836                                   stdout.decode("utf-8")))
1837
1838        if sqlplus is not None:
1839            sqlplus.terminate()
1840
1841    @staticmethod
1842    def get_nom_obj_sql(nom_base, prefix="", sufix=""):
1843        """
1844        Retorna nombre propuesto con prefijo/sufijos formateado para que cumpla longitud máxima de 32 caracteres en
1845        objetos sql Oracle
1846
1847        Args:
1848            nom_base: nombre propuesto
1849            prefix: (opc) prefijo
1850            sufix: (opc) sufijo
1851
1852        Returns:
1853            str formateado
1854        """
1855        return x_sql_parser.get_nom_obj_sql(nom_base, prefix, sufix)
1856
1857    def iter_sdo_gtypes_vals_camp_tab(self, nom_taula, nom_camp):
1858        """
1859        Retorna los distintos tipos de Geometria (codigo entero que define el tipo SDO_GTYPE)
1860        que se encuentran dentro de la columna sdo_geometry de una tabla
1861
1862        Args:
1863            nom_taula: nombre de la tabla
1864            nom_camp: nombre campo geometrico
1865
1866        Returns:
1867            int definiendo tipo de geometría
1868        """
1869        sql_tip_geoms = f"select distinct(tab.{nom_camp}.Get_GType()) as tip_geom from {nom_taula} tab " \
1870                        f"where {nom_camp} is not null"
1871
1872        for reg in self.generator_rows_sql(sql_tip_geoms):
1873            yield reg.TIP_GEOM
1874
1875    def iter_distinct_vals_camp_tab(self, nom_taula, nom_camp, filter_sql=None):
1876        """
1877        Retorna los distintos valores de la columna de una tabla
1878
1879        Args:
1880            nom_taula (str): Nombre de tabla
1881            nom_camp (str): Nombre de campo
1882            filter_sql(str): Filtro SQL sobre la tabla indicada
1883
1884        Returns:
1885            {str}: Itera los distintos valores encontrados en el campo indicado
1886        """
1887        sql_distinct_vals = f"select distinct(tab.{nom_camp}) as VAL from {nom_taula} tab"
1888        if filter_sql:
1889            sql_distinct_vals += " where " + filter_sql
1890
1891        for reg in self.generator_rows_sql(sql_distinct_vals):
1892            yield reg.VAL
1893
1894    def get_tip_camp_geom(self, nom_tab_or_view, nom_camp_geom):
1895        """
1896        Retorna el tipo de campo geométrico (class_tip_geom) registrados en la global __class_tips_geom_ora
1897        para el campo indicado
1898
1899        Args:
1900            nom_tab_or_view: nombre tabla/vista
1901            nom_camp_geom: nombre campo geom
1902
1903        Returns:
1904            namedtuple: con atributos ['TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID'])
1905        """
1906        tips_geom_tab = get_tips_geom_tab(self.con_db, nom_tab_or_view)
1907        if tips_geom_tab:
1908            return tips_geom_tab.get(nom_camp_geom.upper())
1909
1910    def get_epsg_for_srid(self, srid):
1911        """
1912        Rertorna WKT con la definicion del SRID dado
1913        """
1914        return self.callfunc_sql('SDO_CS.MAP_ORACLE_SRID_TO_EPSG', cx_Oracle.NUMBER, srid)
1915
1916    def get_gtype_camp_geom(self, nom_tab_or_view, nom_camp_geom):
1917        """
1918        Retorna el tipo GTYPE (int) de la geometria
1919
1920        Args:
1921            nom_tab_or_view: nombre tabla/vista
1922            nom_camp_geom: nombre campo geom
1923
1924        Returns:
1925            int
1926        """
1927        gtype = 0
1928        g_tip_ora = self.get_tip_camp_geom(nom_tab_or_view, nom_camp_geom)
1929        if g_tip_ora:
1930            gtype = GTYPES_ORA.index(g_tip_ora.GTYPE)
1931
1932        return gtype
1933
1934    @staticmethod
1935    def verificar_path_vector_file(nom_tab_or_view, dir, file_name, ext, zipped):
1936        """
1937        Compone el/los path para el/los vector_file de una tabla y determina si exists
1938        Args:
1939            nom_tab_or_view:
1940            dir:
1941            file_name:
1942            ext:
1943            zipped:
1944
1945        Returns:
1946            file_path (str), file_path_zip (str), exists (bool)
1947        """
1948        if file_name and not file_name.endswith(ext):
1949            file_name = ".".join((file_name, ext))
1950        elif not file_name:
1951            file_name = ".".join((nom_tab_or_view, ext)).lower()
1952
1953        file_path = os.path.join(dir, file_name)
1954        file_path_zip = None
1955        if zipped:
1956            file_path_zip = "{}.zip".format(os.path.splitext(file_path)[0])
1957
1958        exists = (os.path.exists(file_path) and not file_path_zip) or (file_path_zip and os.path.exists(file_path_zip))
1959
1960        return file_path, file_path_zip, exists
1961
1962    @print_to_log_exception()
1963    def create_json_tab_or_view(self, nom_tab_or_view, dir='.', file_name=None, overwrite=True, cols=None, zipped=True,
1964                                filter_sql=None, *args_sql):
1965        """
1966
1967        Args:
1968            nom_tab_or_view (str): Nombre tabla o vista
1969            dir (str):
1970            file_name (str):
1971            overwrite (bool):
1972            cols (list): columnas
1973            zipped (bool):
1974            filter_sql (str):
1975            *args_sql: lista de argumentos a pasar al filtro sql
1976        Returns:
1977            file_path (str)
1978        """
1979        file_path, file_path_zip, exists = self.verificar_path_vector_file(
1980            nom_tab_or_view, dir, file_name, "json", zipped)
1981
1982        if overwrite or not exists:
1983            # Se calculan las columnas para hacer get de la fila con el orden en las columnas de la tabla
1984            if not cols:
1985                dd_tab = self.get_dd_table(nom_tab_or_view)
1986                cols = dd_tab.cols
1987            sql = sql_tab(nom_tab_or_view,
1988                          filter_sql=filter_sql,
1989                          columns=cols)
1990
1991            file_path_res = vector_file_from_gen_ora_sql(file_path, "json", self.generator_rows_sql(sql, *args_sql),
1992                                                         zipped=zipped)
1993        else:
1994            file_path_res = file_path if not zipped else file_path_zip
1995
1996        return file_path_res
1997
1998    @print_to_log_exception()
1999    def create_csv_tab_or_view(self, nom_tab_or_view, dir='.', file_name=None, overwrite=True, cols=None, zipped=True,
2000                               filter_sql=None, *args_sql):
2001        """
2002
2003        Args:
2004            nom_tab_or_view (str): Nombre tabla o vista
2005            dir (str):
2006            file_name (str):
2007            overwrite (bool):
2008            cols (list): columnas
2009            zipped (bool):
2010            filter_sql (str):
2011            *args_sql: lista de argumentos a pasar al filtro sql
2012
2013        Returns:
2014            file_path_res (str)
2015        """
2016        file_path, file_path_zip, exists = self.verificar_path_vector_file(
2017            nom_tab_or_view, dir, file_name, "csv", zipped)
2018
2019        if overwrite or not exists:
2020            if not cols:
2021                dd_tab = self.get_dd_table(nom_tab_or_view)
2022                cols = dd_tab.cols
2023            sql = sql_tab(nom_tab_or_view,
2024                          filter_sql=filter_sql,
2025                          columns=cols)
2026
2027            # Para el formato geocsv que acepta GDAL se añade fichero con los tipos de columna
2028            tip_cols_csv = []
2029            for col in cols:
2030                r_tip_col = self.row_table("user_tab_columns",
2031                                           "table_name = :1 and column_name = :2",
2032                                           nom_tab_or_view.upper(), col.upper())
2033                dtype = r_tip_col.DATA_TYPE
2034                dlength = r_tip_col.DATA_LENGTH
2035                dprecision = r_tip_col.DATA_PRECISION
2036                dscale = r_tip_col.DATA_SCALE
2037
2038                if dtype == "DATE":
2039                    tip_cols_csv.append('"DateTime"')
2040                elif dtype == "FLOAT":
2041                    tip_cols_csv.append('"Real({}.{})"'.format(dlength, dprecision))
2042                elif dtype == "NUMBER":
2043                    if dscale and dscale != 0:
2044                        tip_cols_csv.append('"Real({}.{})"'.format(dprecision, dscale))
2045                    elif dprecision:
2046                        tip_cols_csv.append('"Integer({})"'.format(dprecision))
2047                    else:
2048                        tip_cols_csv.append('"Real(10.8)"')
2049                elif dtype == "SDO_GEOMETRY":
2050                    tip_cols_csv.append('"WKT"')
2051                else:
2052                    tip_cols_csv.append('"String({})"'.format(round(dlength * 1.25)))
2053
2054            file_path_res = vector_file_from_gen_ora_sql(file_path, "csv",
2055                                                         self.generator_rows_sql(sql, *args_sql, geom_format="as_wkt"),
2056                                                         zipped=zipped, cols_csv=cols, tip_cols_csv=tip_cols_csv)
2057        else:
2058            file_path_res = file_path if not zipped else file_path_zip
2059
2060        return file_path_res
2061
2062    @print_to_log_exception()
2063    def create_geojsons_tab_or_view(self, nom_tab_or_view, dir='.', file_name_prefix=None, by_geom=False,
2064                                    dir_topojson=None, overwrite=True, cols=None,
2065                                    filter_sql=None, *args_sql):
2066        """
2067
2068        Args:
2069            nom_tab_or_view (str): Nombre tabla (vigente o versionada) para entidad GIS
2070            dir (str="."):
2071            file_name_prefix (str=None): (opcional) prefijo del fichero
2072            by_geom (bool=False): (Opcional) si se querrán los geojsons por geometria. Si no se saca un unico geojson con
2073                            la columna geometry como una GeometryCollection si la tabla es multigeom
2074            dir_topojson (str=None): path donde irán las conversiones
2075            overwrite (bool=True):
2076            cols (list=None):
2077            filter_sql (str=None):
2078            *args_sql: lista de argumentos a pasar al filtro sql
2079
2080        Returns:
2081            ok (bool)
2082        """
2083        ext = "geo.json"
2084        sqls = {}
2085        dd_tab = self.get_dd_table(nom_tab_or_view)
2086        if not cols:
2087            cols = dd_tab.cols
2088
2089        if by_geom:
2090            c_alfas = [cn for cn in dd_tab.alfas() if cn in cols]
2091            c_geoms = [cn for cn in dd_tab.geoms() if cn in cols]
2092            for c_geom in c_geoms:
2093                sqls[c_geom] = sql_tab(nom_tab_or_view, filter_sql, c_alfas + [c_geom])
2094        else:
2095            sqls[None] = sql_tab(nom_tab_or_view, filter_sql, cols)
2096
2097        if not file_name_prefix:
2098            file_name_prefix = nom_tab_or_view
2099
2100        for ng, sql in sqls.items():
2101            file_name = file_name_prefix
2102            if ng:
2103                file_name = "-".join((file_name, ng))
2104
2105            file_name = ".".join((file_name, ext)).lower()
2106            file_path = os.path.join(dir, file_name)
2107
2108            if overwrite or not os.path.exists(file_path):
2109                file_path = vector_file_from_gen_ora_sql(file_path, "geojson",
2110                                                         self.generator_rows_sql(sql, *args_sql))
2111
2112            if by_geom and dir_topojson and file_path:
2113                tip_geom = getattr(dd_tab, ng).GTYPE
2114                simplify = True
2115                if tip_geom.endswith("POINT"):
2116                    simplify = False
2117
2118                topojson_utils.geojson_to_topojson(file_path, dir_topojson,
2119                                                   simplify=simplify,
2120                                                   overwrite=overwrite)
2121
2122        return True
2123
2124
2125if __name__ == '__main__':
2126    import fire
2127
2128    sys.exit(fire.Fire())
GTYPES_ORA = ['DEFAULT', 'POINT', 'LINE', 'POLYGON', 'COLLECTION', 'MULTIPOINT', 'MULTILINE', 'MULTIPOLYGON']
def class_tip_geom(gtype_ora='DEFAULT'):
51def class_tip_geom(gtype_ora="DEFAULT"):
52    """
53    Retorna NAMEDTUPLE 'cursor_desc_tip_geom_GTYPE' con columnas
54    'TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID'
55
56    Args:
57        gtype_ora: tipo geometrias como claves en __class_tips_geom_ora
58
59    Returns:
60        namedtuple('TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID')
61
62    """
63    gtype_ora = gtype_ora.upper()
64
65    if gtype_ora not in __class_tips_geom_ora:
66        gtype_ora = "DEFAULT"
67
68    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')

def del_cache_rel_con_db(con_db_name: str):
78def del_cache_rel_con_db(con_db_name: str):
79    """
80    Borra las Caches para atributos de tablas_o_vistas de Oracle
81    Args:
82        con_db_name: nom de la connexió
83
84    Returns:
85
86    """
87    all_cache = [__cache_pks_tab, __cache_row_desc_tab, __cache_tips_geom_tab, __cache_row_class_tab]
88    for cache_dic in all_cache:
89        # alamcenamos las claves porque no se puede eliminar del diccionario mientras se itera
90        keys_remove = []
91        for key in cache_dic:
92            if key.startswith(con_db_name):
93                keys_remove.append(key)
94        for key_r in keys_remove:
95            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:

def get_oracle_connection(user_ora, psw_ora, dsn_ora=None, call_timeout=None, schema_ora=None):
 98def get_oracle_connection(user_ora, psw_ora, dsn_ora=None, call_timeout=None, schema_ora=None):
 99    """
100    Return cx_Oracle Connection
101    Args:
102        user_ora (str):
103        psw_ora (str):
104        dsn_ora (str=None):
105        call_timeout (int=None): miliseconds
106        schema_ora(str=None): indicate scheme when it is different from the user, default schema = user
107
108    Returns:
109        cx_Oracle.Connection
110    """
111    connection = cx_Oracle.connect(dsn=dsn_ora, user=user_ora, password=psw_ora)
112    if call_timeout:
113        connection.call_timeout = call_timeout
114
115    if schema_ora:
116        connection.current_schema = schema_ora
117
118    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

def get_nom_conexion(con_db):
121def get_nom_conexion(con_db):
122    """
123    Devuelve el nombre de la conexion a Oracle
124
125    Args:
126        con_db:
127
128    Returns:
129
130    """
131    return "@".join((con_db.username.upper(), con_db.dsn.upper()))

Devuelve el nombre de la conexion a Oracle

Arguments:
  • con_db:

Returns:

def new_cursor(con_db, input_handler=None, output_handler=None):
134def new_cursor(con_db, input_handler=None, output_handler=None):
135    """
136    Retorna cx_Oracle.Cursor con los handlers pasados por parámetro
137
138    Args:
139        con_db:
140        input_handler:
141        output_handler:
142
143    Returns:
144        cx_Oracle.cursor
145    """
146    try:
147        curs = con_db.cursor()
148
149        if input_handler:
150            curs.inputtypehandler = input_handler
151
152        if output_handler:
153            curs.outputtypehandler = output_handler
154
155        return curs
156
157    except cx_Oracle.Error as exc:
158        print("!!ERROR!! - Error al instanciar Cursor para la conexion Oracle {}\n"
159              "     Error: {}".format(get_nom_conexion(con_db), exc))
160        raise

Retorna cx_Oracle.Cursor con los handlers pasados por parámetro

Arguments:
  • con_db:
  • input_handler:
  • output_handler:
Returns:

cx_Oracle.cursor

def get_row_descriptor(curs, nom_base_desc=None):
163def get_row_descriptor(curs, nom_base_desc=None):
164    """
165    Retorna instancia namedtuple indexada por las columnas de la query ejecutada en el cursor con los
166    tipos de columna por valores.
167
168    Si se pasa nom_base_desc se hará heredar el namedtuple de esa clase
169
170    Args:
171        curs:
172        nom_base_desc:
173
174    Returns:
175
176    """
177    ora_desc = None
178    dd_reg = curs.description
179    dict_camps = OrderedDict.fromkeys([def_col[0].replace(" ", "_") for def_col in dd_reg])
180    if nom_base_desc:
181        # Por si viene nombre de tabla con esquema delante nos quedamos solo con la ultima parte
182        nom_base_desc = nom_base_desc.split(".")[-1]
183        nom_base_desc += "_cursor"
184    else:
185        nom_base_desc = "cursor_" + str(id(curs))
186
187    try:
188        nt_class = namedtuple(nom_base_desc,
189                              list(dict_camps))
190        ora_desc = nt_class(*[def_cols[1] for def_cols in dd_reg])
191    except ValueError:
192        raise Exception("!ERROR! - No se puede crear descriptor de fila para el SQL especificado. "
193                        "El nombre de la columna {} no es válido".format(str(sys.exc_info()[1]).split(":")[1]),
194                        sys.exc_info())
195
196    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:

def get_row_class_cursor(cursor_desc, con_db):
199def get_row_class_cursor(cursor_desc, con_db):
200    """
201    Retorna clase de fila por defecto (class row_cursor) para un cursor
202
203    Args:
204        cursor_desc: (opcional) clase de la que se quiera heredar
205        con_db: conexión cx_Oracle
206
207    Returns:
208        class (default = row_cursor)
209    """
210    cls_curs_dsc = type(cursor_desc)
211
212    class row_cursor(cls_curs_dsc):
213        """
214        Clase para fila devuelta por query SQL (para queries que NO sean sobre una tabla/vista)
215        """
216        ora_descriptor = cursor_desc
217        con_ora = con_db
218
219        def vals(self):
220            """
221            Valores de la fila
222
223            Returns:
224                OrderedDict
225            """
226            return self._asdict()
227
228        def as_xml(self,
229                   nom_root_xml="root_reg_sql",
230                   atts_root_xml=None,
231                   excluded_cols=None,
232                   as_etree_elem=False):
233            """
234            Devuelve fila en formato XML
235
236            Args:
237                nom_root_xml: define el nombre del TAG root del XML. Por defecto 'root_reg_sql'
238                atts_root_xml: lista que define las columnas que se asignarán
239                               como atributos del TAG root
240                excluded_cols: lista con nombres de columnas que no se quieran incluir
241                as_etree_elem (default=False): Si True devuelve registro como objeto etree.Element.
242                                              Si False (por defecto) como str en formato XML
243
244            Returns:
245                str (formato XML) OR etree.Element
246            """
247            if not excluded_cols:
248                excluded_cols = list()
249
250            atts_xml = None
251            d_xml_elems = self.xml_elems()
252            if atts_root_xml:
253                atts_xml = OrderedDict({att.lower(): d_xml_elems[att.lower()]
254                                        for att in atts_root_xml})
255
256            tab_elem = etree.Element(nom_root_xml.lower(), atts_xml)
257            excluded_cols = [c.lower() for c in excluded_cols]
258            for tag, xml_val in d_xml_elems.items():
259                if tag in excluded_cols:
260                    continue
261
262                try:
263                    camp_elem = etree.SubElement(tab_elem, tag.lower())
264                    camp_elem.text = xml_val
265                except:
266                    print("!ERROR! - No se ha podido añadir el campo '",
267                          tag, "' al XML", sep="")
268
269            ret = tab_elem
270
271            if not as_etree_elem:
272                ret = etree.tostring(tab_elem, encoding="unicode")
273
274            return ret
275
276        def xml_elems(self):
277            """
278            Itera los campos del registro y devuelve su nombre y su valor parseado para XML
279
280            Yields:
281                n_camp, val_camp
282            """
283            d_xml_elems = dict()
284            for camp, val in self.vals().items():
285                ret_val = ""
286                if isinstance(val, datetime.datetime):
287                    ret_val = val.strftime("%Y-%m-%dT%H:%M:%S")
288                elif isinstance(val, m_sdo_geom.sdo_geom):
289                    ret_val = val.as_wkt()
290                elif val:
291                    ret_val = str(val)
292
293                d_xml_elems[camp.lower()] = ret_val
294
295            return d_xml_elems
296
297        def as_json(self):
298            """
299            Retorna la fila como JSON con las geometrias en formato GeoJson
300
301            Returns:
302                str con la fila en formato json
303            """
304            return json.dumps(self.vals(),
305                              ensure_ascii=False,
306                              cls=geojson_encoder)
307
308        def as_geojson(self):
309            """
310            Retorna la fila como GEOJSON
311
312            Returns:
313                str con la fila en formato geojson
314            """
315            return json.dumps(self.__geo_interface__,
316                              ensure_ascii=False,
317                              cls=geojson_encoder)
318
319        @property
320        def __geo_interface__(self):
321            """
322            GeoJSON-like protocol for geo-spatial para toda una fila devuelta por query SQL
323
324            Si la fila tiene varias geometrias devolverá geojson como "GeometryCollection"
325
326            Returns:
327                dict con las geometrias y campos alfanuméricos (properties de Feature) en formato geojson
328            """
329            geos = dict(**self.sdo_geoms)
330            num_geos = len(geos)
331            geom = None
332            if num_geos == 1:
333                geom = next(iter(geos.values()))
334            elif num_geos > 1:
335                geom = {"type": "GeometryCollection",
336                        "geometries": list(geos.values())}
337
338            if geom:
339                return dict({"type": "Feature",
340                             "geometry": geom,
341                             "properties": {nc: val for nc, val in self.vals().items() if nc not in geos}})
342
343        @property
344        def sdo_geoms(self):
345            """
346            Devuelve diccionario indexado por los nombres de columnas que tienen valor geometrico de la clase sdo_geom
347
348            Returns:
349                dict {nom_column:sdo_geom}
350            """
351            return {nc: g.__geo_interface__
352                    for nc, g in self.vals().items() if isinstance(g, m_sdo_geom.sdo_geom)}
353
354        def geometries(self, as_format=None):
355            """
356            Itera por los campos geométricos informados
357
358            Args:
359                as_format (default=None): Se puede informar con nombre funcion 'as_XXXX' a la que responda SDO_GEOM
360
361            Yields:
362                nom_camp, geom
363            """
364            for ng, g in self.vals().items():
365                if isinstance(g, m_sdo_geom.sdo_geom):
366                    g_val = g
367                    if as_format:
368                        g_val = getattr(g, as_format)()
369
370                    yield ng, g_val
371
372        @property
373        def cols_pts_angles(self):
374            """
375            Devuelve diccionario de campos angulo para geometrias puntuales con la clave el nombre del campo angulo y
376            el valor el nombre del campo puntual al que hace referencia
377
378            Returns:
379                dict
380            """
381            sufix_ang = "_ANG"
382            cols = self.cols
383            cols_ang = {}
384            for c in (c for c, gd in self.geoms_vals().items() if gd.GTYPE.endswith('POINT')):
385                col_ang = x_sql_parser.get_nom_obj_sql(c, sufix=sufix_ang)
386                if col_ang in cols:
387                    cols_ang[col_ang] = c
388
389            return cols_ang
390
391        @property
392        def cols(self):
393            """
394            Devuelve lista con el nombre de la columnas de la fila
395
396            Returns:
397                list con nombres columnas
398            """
399            return self._fields
400
401    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)

def get_pk_tab(con_db, nom_tab_or_view):
404def get_pk_tab(con_db, nom_tab_or_view):
405    """
406    Retorna la PRIMARY KEY de una tabla o vista de Oracle
407
408    Args:
409        con_db:
410        nom_tab_or_view:
411
412    Returns:
413        lista nombre columnas clave
414    """
415    nom_pk_cache = get_nom_conexion(con_db) + ":" + nom_tab_or_view.upper()
416    noms_camps_pk = __cache_pks_tab.get(nom_pk_cache)
417    if not noms_camps_pk:
418        sql_keys = "SELECT cols.column_name " \
419                   "FROM user_constraints cons, user_cons_columns cols " \
420                   "WHERE cols.table_name = :1 " \
421                   "AND cons.constraint_type = 'P' " \
422                   "AND cons.constraint_name = cols.constraint_name " \
423                   "AND cons.owner = cols.owner " \
424                   "ORDER BY cols.table_name, cols.position"
425
426        noms_camps_pk = [fila_val.COLUMN_NAME for fila_val in
427                         iter_execute_fetch_sql(con_db, sql_keys, nom_tab_or_view.upper())]
428        __cache_pks_tab[nom_pk_cache] = noms_camps_pk
429
430    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

def get_tips_geom_tab(con_db, nom_tab_or_view):
433def get_tips_geom_tab(con_db, nom_tab_or_view):
434    """
435    Retorna diccionario con nombre del campo como indice y los tipos de campos geométricos
436    (class_tip_geom) registrados en la global __class_tips_geom_ora como clases 'c_desc_tip_geom_?' siendo ?
437    el LAYER_GTYPE del indice de Oracle
438
439    Args:
440        con_db:
441        nom_tab_or_view:
442
443    Returns:
444        dict indexado por nombre columnas geometria y la clase de geometria (__class_tips_geom_ora)
445    """
446    nom_con = get_nom_conexion(con_db)
447
448    tips_geom_con = __cache_tips_geom_tab.get(nom_con)
449    if not tips_geom_con:
450        tips_geom_con = __cache_tips_geom_tab[nom_con] = {}
451        sql_tip_geom = "select  /*+ result_cache */ " \
452                       "        T_COLS.TABLE_NAME, " \
453                       "        T_COLS.COLUMN_NAME, " \
454                       "        V_IDX_META.SDO_LAYER_GTYPE GTYPE, " \
455                       "        V_G_META.SRID " \
456                       "from    user_tab_columns t_cols," \
457                       "        user_sdo_geom_metadata v_g_meta," \
458                       "        user_sdo_index_info v_idx_info," \
459                       "        user_sdo_index_metadata v_idx_meta " \
460                       "where v_g_meta.table_name = t_cols.table_name and " \
461                       "v_g_meta.column_name = t_cols.column_name and" \
462                       "    v_idx_info.table_name = t_cols.table_name and " \
463                       "v_idx_info.column_name = t_cols.column_name and" \
464                       "    v_idx_meta.sdo_index_name = v_idx_info.index_name"
465
466        for reg_tip_geom in iter_execute_fetch_sql(con_db, sql_tip_geom):
467            nom_cache = reg_tip_geom.TABLE_NAME.upper()
468            cache_tips_geom = tips_geom_con.get(nom_cache)
469            if not cache_tips_geom:
470                cache_tips_geom = {}
471                tips_geom_con[nom_cache] = cache_tips_geom
472
473            tip_geom = class_tip_geom(reg_tip_geom.GTYPE)(*reg_tip_geom.vals().values())
474
475            nom_camp_geom = reg_tip_geom.COLUMN_NAME.upper()
476            cache_tips_geom[nom_camp_geom] = tip_geom
477
478    tips_geom = tips_geom_con.get(nom_tab_or_view.upper())
479
480    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)

def get_row_desc_tab(con_db, nom_tab_or_view):
483def get_row_desc_tab(con_db, nom_tab_or_view):
484    """
485    Retorna el descriptor (cursor con los tipos de campo para cada columna) de una tabla o vista de Oracle
486
487    Args:
488        con_db:
489        nom_tab_or_view:
490
491    Returns:
492        namedtuple ó clase fila con las columnas y sus tipos
493    """
494    nom_dd_cache = get_nom_conexion(con_db) + ":" + nom_tab_or_view.upper()
495    tab_desc = __cache_row_desc_tab.get(nom_dd_cache)
496    if not tab_desc:
497        row_class_tab = get_row_class_tab(con_db, nom_tab_or_view)
498
499        camps_dd = row_class_tab.ora_descriptor
500        dict_camps = camps_dd._asdict()
501        dict_camps_geom = row_class_tab.geoms()
502        if dict_camps_geom:
503            for nom_camp, tip_camp_geom in dict_camps_geom.items():
504                dict_camps[nom_camp] = tip_camp_geom
505
506        tab_desc = row_class_tab(*dict_camps.values())
507        __cache_row_desc_tab[nom_dd_cache] = tab_desc
508
509    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

def get_tip_camp(con_db, nom_tab_or_view, nom_camp):
512def get_tip_camp(con_db, nom_tab_or_view, nom_camp):
513    """
514    Devuelve el tipo de campo para la tabla_vista y campo especificado. En el caso de campos alfanuméricos
515    devuelve los tipos de cx_Oracle relacionados (cx_Oracle.NUMBER, cx_Oracle.STRING, ...) y para las
516    geométricas del tipo CLASS_TIP_GEOM
517
518    Args:
519        con_db:
520        nom_tab_or_view:
521        nom_camp:
522
523    Returns:
524        object: (cx_Oracle.NUMBER, cx_Oracle.STRING, ...) o si geometria tipo en __class_tips_geom_ora
525    """
526    nom_tab_or_view = nom_tab_or_view.upper()
527    nom_camp = nom_camp.upper()
528
529    dd_tab = get_row_desc_tab(con_db, nom_tab_or_view)
530    if dd_tab:
531        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

def sql_tab(nom_tab_or_view, filter_sql=None, columns=None):
534def sql_tab(nom_tab_or_view, filter_sql=None, columns=None):
535    """
536    Devuelve sql para tabla o vista
537
538    Args:
539        nom_tab_or_view:
540        filter_sql:
541        columns (list): Lista nombre de columnas a mostrar. Default '*' (todas)
542
543    Returns:
544        str: con select sql para tabla o vista
545    """
546    cols = "*"
547    if columns:
548        cols = ",".join(columns)
549
550    sql_tab = "select {cols} from {taula} TAB".format(
551        taula=nom_tab_or_view,
552        cols=cols)
553
554    if filter_sql:
555        sql_tab += " where " + filter_sql
556
557    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

def get_row_class_tab(con_db, nom_tab_or_view):
560def get_row_class_tab(con_db, nom_tab_or_view):
561    """
562    Retorna clase que hereda de namedtuple para instanciar con los valores de un nuevo registro
563
564    Args:
565        con_db:
566        nom_tab_or_view:
567
568    Returns:
569        object: clase que hereda del namedtuple para una fila
570    """
571    nom_tab_or_view = nom_tab_or_view.upper()
572    nom_row_class_tab_cache = get_nom_conexion(con_db) + ":" + nom_tab_or_view
573    row_class_tab = __cache_row_class_tab.get(nom_row_class_tab_cache)
574
575    if not row_class_tab:
576        curs = con_db.cursor()
577        curs.execute(sql_tab(nom_tab_or_view))
578        ora_desc = get_row_descriptor(curs, nom_tab_or_view)
579
580        row_cursor_cls = get_row_class_cursor(ora_desc, con_db)
581
582        class row_table(row_cursor_cls):
583            """
584            Clase para fila devuelta por SQL sobre tabla
585            """
586            nom_tab = nom_tab_or_view
587
588            def __repr__(self):
589                """
590                built_in que actua cuando se representa clase como STRING
591
592                Returns:
593                    str
594                """
595                repr_txt = nom_tab_or_view + "({pk_vals})"
596                pk_txt = []
597                for i, v in self.pk_vals().items():
598                    pk_txt.append(str(i) + "=" + str(v))
599
600                return repr_txt.format(pk_vals=",".join(pk_txt))
601
602            @staticmethod
603            def pk():
604                """
605                Retorna lista con los nombres de campos clave
606
607                Returns:
608                    list con nombres campos clave
609                """
610                return get_pk_tab(con_db, nom_tab_or_view)
611
612            def pk_vals(self):
613                """
614                Retorna OrderedDict con los pares nombres:valor para los campos clave
615
616                Returns:
617                    OrderedDict
618                """
619                pk_vals = OrderedDict.fromkeys(self.pk())
620                for k in self.pk():
621                    pk_vals[k] = self.vals().get(k)
622
623                return pk_vals
624
625            @staticmethod
626            def geoms():
627                """
628                Devuelve diccionario {nom_camp_geom:tip_geom} para todas las columnas de la tabla que son de
629                tipo geométrico.
630                Lo deduce de la definición de la tabla independientemente de si la columna está informada o no
631
632                Returns:
633                    dict {nom_geom:tip_geom} (vease funcion clas_tip_geom())
634                """
635                return get_tips_geom_tab(con_db, nom_tab_or_view)
636
637            def geoms_vals(self):
638                """
639                Valores de los campos geometricos
640
641                Returns:
642                    dict
643                """
644                vals = self.vals()
645                return {nc: vals[nc] for nc in self.geoms()}
646
647            def as_xml(self, excluded_cols=[],
648                       as_etree_elem=False):
649                """
650                Devuelve row como XML
651
652                Args:
653                    excluded_cols: lista de nombre de columnas que se quieren excluir
654                    as_etree_elem {bool} (default=False): Si True devuelve registro como objeto etree.Element. Si False (por defecto) como str en formato XML
655
656                Returns:
657                    str (formato XML) OR etree.Element
658                """
659                return super(row_table, self).as_xml(nom_tab_or_view,
660                                                     self.pk_vals(),
661                                                     excluded_cols,
662                                                     as_etree_elem)
663
664            @staticmethod
665            def get_row_desc():
666                return get_row_desc_tab(con_db, nom_tab_or_view)
667
668            @staticmethod
669            def alfas(include_pk=True):
670                """
671                Devuelve diccionario {nom_camp:tip_camp} para las columnas alfanuméricas (NO son geométricas)
672                Lo deduce de la definición de la tabla independientemente de si la columna está informada o no
673
674                Args:
675                    include_pk: incluir primary key
676
677                Returns:
678                    dict {nom_camp:tip_camp}
679                """
680                return {nc: tc
681                        for nc, tc in get_row_desc_tab(con_db, nom_tab_or_view).vals().items()
682                        if nc not in get_tips_geom_tab(con_db, nom_tab_or_view) and
683                        (include_pk or nc not in get_pk_tab(con_db, nom_tab_or_view))}
684
685            def alfas_vals(self):
686                """
687                Devuelve diccionario con valores campos alfanuméricos
688
689                Returns:
690                    dict {nom_camp:val_camp}
691                """
692                vals = self.vals()
693                return {nc: vals[nc] for nc in self.alfas()}
694
695        row_class_tab = row_table
696        __cache_row_class_tab[nom_row_class_tab_cache] = row_class_tab
697
698    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

def get_row_factory(curs, a_row_class=None, func_format_geom: str = None):
701def get_row_factory(curs, a_row_class=None, func_format_geom: str = None):
702    """
703    Retorna funcion para crear instancia clase a partir de los valores de una fila
704
705    Args:
706        curs (cx_Oracle.Cursor): cursor de la consulta
707        a_row_class (object=None): clase que se utilizará para crear la fila. Si no se informa se crea una clase
708        func_format_geom (str=None): nombre función para formatear las geometrías SDO_GEOMETRY.
709                (veanse las funciones sdo_geom as_[format])
710
711    Returns:
712        function row_factory_func
713    """
714    con_db = curs.connection
715    if not a_row_class:
716        cursor_desc = get_row_descriptor(curs)
717        a_row_class = get_row_class_cursor(cursor_desc, con_db)
718
719    f_sd_geom = m_sdo_geom.get_build_sdo_geom(con_db, func_format_geom=func_format_geom)
720
721    def row_factory_func(*vals_camps):
722        has_sdo_geom = any(
723            isinstance(val, cx_Oracle.DbObject) and val.type.name == "SDO_GEOMETRY"
724            for val in vals_camps
725        )
726        if has_sdo_geom:
727            vals_camps = [
728                f_sd_geom(val) if isinstance(val, cx_Oracle.DbObject) and
729                                  val.type.name == "SDO_GEOMETRY" else val
730                for val in vals_camps
731            ]
732
733        return a_row_class(*vals_camps)
734
735    return row_factory_func

Retorna funcion para crear instancia clase a partir de los valores de una fila

Arguments:
  • curs (cx_Oracle.Cursor): cursor de la consulta
  • a_row_class (object=None): clase que se utilizará para crear la fila. Si no se informa se crea una clase
  • func_format_geom (str=None): nombre función para formatear las geometrías SDO_GEOMETRY. (veanse las funciones sdo_geom as_[format])
Returns:

function row_factory_func

def get_row_cursor(curs, rowfactory=None):
738def get_row_cursor(curs, rowfactory=None):
739    """
740    Retorna fila como clase que construye el rowfactory. Por defecto si no se informa esta clase heredará
741    de 'namedtuple' con los campos como items más funcionalidad relacionada con la conexion y el descriptor de la fila
742    con los tipos de campo para cada columna
743
744    Args:
745        curs:
746        rowfactory:
747
748    Returns:
749
750    """
751    set_cursor_row_factory(curs, rowfactory)
752
753    row = curs.fetchone()
754
755    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:

def set_cursor_row_factory(curs, rowfactory=None):
758def set_cursor_row_factory(curs, rowfactory=None):
759    """
760    Asigna al cursor la funcion rowfactory para crear las filas devueltas por el cursor
761
762    Args:
763        curs (cx_Oracle.Cursor):
764        rowfactory (function=None):
765
766    Returns:
767        None
768    """
769    if not rowfactory and not curs.rowfactory:
770        rowfactory = get_row_factory(curs)
771
772    if rowfactory:
773        curs.rowfactory = rowfactory

Asigna al cursor la funcion rowfactory para crear las filas devueltas por el cursor

Arguments:
  • curs (cx_Oracle.Cursor):
  • rowfactory (function=None):
Returns:

None

def iter_execute_fetch_sql( con_db, sql_str, *args_sql, logger: logging.Logger = None, **extra_params):
776def iter_execute_fetch_sql(con_db, sql_str, *args_sql, logger: Logger = None, **extra_params):
777    """
778    Itera y devuelve cada fila devuelta para la consulta sql_str
779
780    Args:
781        con_db (cx_Oracle.Connection): conexión a Oracle
782        sql_str (str): consulta SQL
783        *args_sql: argumentos para la consulta SQL
784        logger (Logger=None): Logger para registrar errores
785        **extra_params: {
786            "row_class": clase que se utilizará para cada fila. Vease get_row_factory()
787            "as_format": formato en el que se devuelve cada fila.
788                Las clases base row_cursor y row_table.
789                (vease get_row_class_cursor() y get_row_class_tab()) responden
790                por defecto a"as_xml()" y "as_json()"
791            "geom_format": nombre función para formatear las geometrías SDO_GEOMETRY. (vease sdo_geom as_[format])
792            "rowfactory": función rowfactory para crear las filas devueltas por el cursor
793            "input_handler": función input_handler para el cursor (vease sdo_geom.get_sdo_input_handler())
794        }
795
796    Returns:
797        row_class o string en formato especificado en **extra_params["as_format"]
798    """
799    curs = None
800
801    try:
802        curs = new_cursor(con_db,
803                          input_handler=extra_params.pop("input_handler",
804                                                         m_sdo_geom.get_sdo_input_handler()),
805                          )
806
807        if (rowfactory := extra_params.pop("rowfactory", None)) is None:
808            row_class = extra_params.pop("row_class", None)
809            curs_aux = new_cursor(con_db)
810            curs_aux.execute(sql_str, args_sql)
811            rowfactory = get_row_factory(
812                curs_aux, row_class,
813                func_format_geom=extra_params.pop("geom_format", None)
814            )
815
816        curs.prefetchrows = extra_params.pop("prefetchrows", 0)
817        curs.arraysize = extra_params.pop("arraysize", 5_000)
818        curs.execute(sql_str, args_sql)
819        curs.rowfactory = rowfactory
820
821        num_rows = 0
822        for reg in curs:
823            if logger:
824                num_rows += 1
825                logger.debug(f"Num row {num_rows}: {reg}")
826
827            if "as_format" in extra_params:
828                f_format = extra_params["as_format"]
829                if f_format:
830                    reg = getattr(reg, f_format)()
831
832            yield reg
833
834    except:
835        if curs is not None:
836            curs.close()
837        raise
838
839    if curs is not None:
840        curs.close()

Itera y devuelve cada fila devuelta para la consulta sql_str

Arguments:
  • con_db (cx_Oracle.Connection): conexión a Oracle
  • sql_str (str): consulta SQL
  • *args_sql: argumentos para la consulta SQL
  • logger (Logger=None): Logger para registrar errores
  • **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()" "geom_format": nombre función para formatear las geometrías SDO_GEOMETRY. (vease sdo_geom as_[format]) "rowfactory": función rowfactory para crear las filas devueltas por el cursor "input_handler": función input_handler para el cursor (vease sdo_geom.get_sdo_input_handler())
  • }
Returns:

row_class o string en formato especificado en **extra_params["as_format"]

def execute_fetch_sql(con_db, sql_str, *args_sql, **extra_params):
843def execute_fetch_sql(con_db, sql_str, *args_sql, **extra_params):
844    """
845    Devuelve la primera iteración sobre la consulta sql_str
846
847    Args:
848        con_db (cx_Oracle.Connection): conexión a Oracle
849        sql_str (str): consulta SQL
850        *args_sql: argumentos para la consulta SQL
851        **extra_params: {
852            "row_class": clase que se utilizará para cada fila. Vease get_row_factory()
853            "as_format": formato en el que se devuelve cada fila.
854                Las clases base row_cursor y row_table.
855                (vease get_row_class_cursor() y get_row_class_tab()) responden
856                por defecto a"as_xml()" y "as_json()"
857            "geom_format": nombre función para formatear las geometrías SDO_GEOMETRY. (vease sdo_geom as_[format])
858            "rowfactory": función rowfactory para crear las filas devueltas por el cursor
859            "input_handler": función input_handler para el cursor (vease sdo_geom.get_sdo_input_handler())
860        }
861
862    Returns:
863        row_class o string en formato especificado en **extra_params["as_format"]
864    """
865    reg = None
866    for row in iter_execute_fetch_sql(con_db, sql_str, *args_sql, **extra_params):
867        reg = row
868        break
869
870    return reg

Devuelve la primera iteración sobre la consulta sql_str

Arguments:
  • con_db (cx_Oracle.Connection): conexión a Oracle
  • sql_str (str): consulta SQL
  • *args_sql: argumentos para la consulta 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()" "geom_format": nombre función para formatear las geometrías SDO_GEOMETRY. (vease sdo_geom as_[format]) "rowfactory": función rowfactory para crear las filas devueltas por el cursor "input_handler": función input_handler para el cursor (vease sdo_geom.get_sdo_input_handler())
  • }
Returns:

row_class o string en formato especificado en **extra_params["as_format"]

def dict_as_sql_bind_and_params(dict_vals, bool_rel='and', filter_oper='='):
873def dict_as_sql_bind_and_params(dict_vals, bool_rel="and", filter_oper="="):
874    """
875    A partir de un dict de {nom_camps:value_camps} devuelve un string en forma
876    de SQL FILTER bindind (camp_a = :1 and camp_b = :2) y la lista de params que se asignarán via binding
877    El BOOL_REL podrá ser 'AND', 'OR' o ',' para el SET de los updates
878    El FILTER_OPER podrá ser '=', '!=' o ':=' para el SET de los updates
879
880    Args:
881        dict_vals:
882        bool_rel:
883        filter_oper:
884
885    Returns:
886        str, [params*]
887    """
888    query_elems = {}
889    for nom_camp, val_clau in dict_vals.items():
890        sql_str = str(nom_camp) + " " + filter_oper + " :" + str(nom_camp)
891        query_elems[sql_str] = val_clau
892
893    sql = (" " + bool_rel.strip().upper() + " ").join(query_elems.keys())
894
895    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*]

class SerializableGenerator(builtins.list):
898class SerializableGenerator(list):
899    """Generator that is serializable by JSON
900
901    It is useful for serializing huge data by JSON
902    It can be used in a generator of json chunks used e.g. for a stream
903    ('[1', ']')
904    # >>> for chunk in iter_json:
905    # ...     stream.write(chunk)
906    # >>> SerializableGenerator((x for x in range(3)))
907    # [<generator object <genexpr> at 0x7f858b5180f8>]
908    """
909
910    def __init__(self, iterable):
911        super().__init__()
912        tmp_body = iter(iterable)
913        try:
914            self._head = iter([next(tmp_body)])
915            self.append(tmp_body)
916        except StopIteration:
917            self._head = []
918
919    def __iter__(self):
920        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>]

SerializableGenerator(iterable)
910    def __init__(self, iterable):
911        super().__init__()
912        tmp_body = iter(iterable)
913        try:
914            self._head = iter([next(tmp_body)])
915            self.append(tmp_body)
916        except StopIteration:
917            self._head = []
def geojson_from_gen_ora_sql(generator_sql, as_string=False):
923def geojson_from_gen_ora_sql(generator_sql, as_string=False):
924    """
925    Devuelve diccionario geojson para un generator de rows de Oracle
926
927    Args:
928        generator_sql (function generator):
929        as_string (bool): (opcional) indica si se querrá el geojson como un string
930
931    Returns:
932        geojson (dict ó str)
933    """
934    vals = {"type": "FeatureCollection",
935            "features": [getattr(r, "__geo_interface__")
936                         for r in generator_sql
937                         if getattr(r, "__geo_interface__")]}
938
939    ret = vals
940    if as_string:
941        ret = json.dumps(vals,
942                         ensure_ascii=False)
943
944    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)

def vector_file_from_gen_ora_sql( file_path, vector_format, func_gen, zipped=False, indent_json=None, cols_csv=None, tip_cols_csv=None):
 947def vector_file_from_gen_ora_sql(file_path, vector_format, func_gen, zipped=False, indent_json=None, cols_csv=None,
 948                                 tip_cols_csv=None):
 949    """
 950    A partir del resultado de una query SQL devuelve un file_object formateado segun formato
 951
 952    Args:
 953        file_path (str): path del fichero a grabar
 954        vector_format (str): tipo formato (CSV, JSON, GEOJSON)
 955        func_gen (generator function): funcion que devuelva filas sql en forma de row_cursor o row_table
 956                                      (vease funciones generator_rows_sql() o generator_rows_table())
 957        zipped (bool=False): Devuelve fichero en un fichero comprimido (.zip)
 958        indent_json (int):
 959        cols_csv (list): Lista de columnes del CSV
 960        tip_cols_csv (list): Lista de tipos de columnas para CSV.
 961                             Revisar especificacion en https://giswiki.hsr.ch/GeoCSV
 962    Returns:
 963        str: pathfile del fichero generado
 964    """
 965    vector_format = vector_format.lower()
 966    newline = None if vector_format != "csv" else ""
 967    file_path_csvt = None
 968    str_csvt = None
 969    dir_base = os.path.dirname(file_path)
 970    if dir_base:
 971        os.makedirs(dir_base, exist_ok=True)
 972
 973    # Se hace en memoria o recursos locales (SpooledTemporaryFile) para evitar trabajar lo minimo en la red
 974    # si se da el caso en el path fichero del fichero indicado. Cuando se quiere grabar en recurso local el tiempo
 975    # perdido por utilizar un fichero temporal debería ser despreciable comparado con la complejidad añadida al código
 976    # para decidir si usar o no el SpooledTemporaryFile
 977    with SpooledTemporaryFile(mode="w+", encoding="utf-8", newline=newline) as temp_file:
 978        if vector_format == "geojson":
 979            json.dump(geojson_from_gen_ora_sql(func_gen),
 980                      temp_file,
 981                      ensure_ascii=False,
 982                      indent=indent_json,
 983                      cls=geojson_encoder)
 984        elif vector_format == "json":
 985            json.dump(SerializableGenerator((r.vals() for r in func_gen)),
 986                      temp_file,
 987                      ensure_ascii=False,
 988                      indent=indent_json,
 989                      cls=geojson_encoder)
 990        elif vector_format == "csv":
 991            writer = csv.DictWriter(temp_file, fieldnames=cols_csv)
 992            writer.writeheader()
 993
 994            for r in func_gen:
 995                writer.writerow(r.vals())
 996
 997        temp_file.seek(0)
 998        if tip_cols_csv:
 999            file_path_csvt = ".".join((os.path.splitext(file_path)[0], "csvt"))
1000            str_csvt = ",".join(tip_cols_csv)
1001
1002        if zipped:
1003            file_path_res = "{}.zip".format(os.path.splitext(file_path)[0])
1004            with SpooledTemporaryFile() as zip_temp_file:
1005                with ZipFile(zip_temp_file, "w", compression=ZIP_DEFLATED, allowZip64=True) as my_temp_zip:
1006                    my_temp_zip.writestr(zinfo_or_arcname=os.path.basename(file_path), data=temp_file.read())
1007                    if str_csvt:
1008                        my_temp_zip.writestr(zinfo_or_arcname=os.path.basename(file_path_csvt), data=str_csvt)
1009
1010                zip_temp_file.seek(0)
1011                with open(file_path_res, mode="wb") as file_res:
1012                    shutil.copyfileobj(zip_temp_file, file_res)
1013        else:
1014            file_path_res = file_path
1015            with open(file_path_res, mode="w", encoding="utf-8") as file_res:
1016                shutil.copyfileobj(temp_file, file_res)
1017            if str_csvt:
1018                with open(file_path_csvt, mode="w") as csvt_file:
1019                    csvt_file.write(str_csvt)
1020
1021    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

class geojson_encoder(json.encoder.JSONEncoder):
1024class geojson_encoder(json.JSONEncoder):
1025    """
1026    Class Encoder to parser SDO_GEOM to GEOJSON
1027    """
1028    __num_decs__ = 9
1029
1030    def default(self, obj_val):
1031        """
1032        Redefine default para tratar las geometrias SDO_GEOM y convertirlas a geojson y las fechas a iso_format
1033        Args:
1034            obj_val: valor
1035
1036        Returns:
1037            object encoded
1038        """
1039        if isinstance(obj_val, m_sdo_geom.sdo_geom):
1040            return obj_val.as_geojson()
1041        elif isinstance(obj_val, (datetime.datetime, datetime.date)):
1042            return obj_val.isoformat()
1043        elif isinstance(obj_val, cx_Oracle.LOB):
1044            return obj_val.read()
1045        else:
1046            return json.JSONEncoder.default(self, obj_val)

Class Encoder to parser SDO_GEOM to GEOJSON

def default(self, obj_val):
1030    def default(self, obj_val):
1031        """
1032        Redefine default para tratar las geometrias SDO_GEOM y convertirlas a geojson y las fechas a iso_format
1033        Args:
1034            obj_val: valor
1035
1036        Returns:
1037            object encoded
1038        """
1039        if isinstance(obj_val, m_sdo_geom.sdo_geom):
1040            return obj_val.as_geojson()
1041        elif isinstance(obj_val, (datetime.datetime, datetime.date)):
1042            return obj_val.isoformat()
1043        elif isinstance(obj_val, cx_Oracle.LOB):
1044            return obj_val.read()
1045        else:
1046            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

class gestor_oracle:
1087class gestor_oracle(object):
1088    """
1089    Clase que gestionará distintas conexiones a Oracle y facilitará operaciones sobre la BBDD
1090    """
1091    tip_number = cx_Oracle.NUMBER
1092    tip_string = cx_Oracle.STRING
1093    tip_clob = cx_Oracle.CLOB
1094    tip_blob = cx_Oracle.BLOB
1095    tip_date = cx_Oracle.DATETIME
1096    tip_fix_char = cx_Oracle.FIXED_CHAR
1097
1098    __slots__ = 'nom_con_db', '__con_db__', '__user_con_db__', \
1099        '__psw_con_db__', '__dsn_ora__', '__call_timeout__', '__schema_con_db__', 'logger'
1100
1101    def __init__(self, user_ora, psw_ora, dsn_ora, a_logger=None, call_timeout: int = None, schema_ora=None):
1102        """
1103        Inicializa gestor de Oracle para una conexion cx_Oracle a Oracle
1104        Se puede pasar por parametro un logger o inicializar por defecto
1105
1106        Args:
1107            user_ora {str}: Usuario/schema Oracle
1108            psw_ora {str}: Password usuario
1109            dsn_ora {str}: DSN Oracle (Nombre instancia/datasource de Oracle
1110                    según TSN o string tal cual devuelve cx_Oracle.makedsn())
1111            call_timeout (int=None): miliseconds espera per transaccio
1112            a_logger:
1113            schema_ora(str=None): indicate scheme when it is different from the user, default schema = user
1114        """
1115        self.__con_db__ = None
1116        self.__call_timeout__ = call_timeout
1117        self.logger = a_logger
1118        self.__set_logger()
1119        self.__set_conexion(user_ora, psw_ora, dsn_ora, schema_ora=schema_ora)
1120
1121    def __del__(self):
1122        """
1123        Cierra la conexion al matar la instancia
1124        """
1125        try:
1126            if hasattr(self, "__con_db__"):
1127                self.__con_db__.close()
1128        except:
1129            pass
1130
1131    def __repr__(self):
1132        """
1133        built_in que actua cuando se representa clase como STRING
1134
1135        Returns:
1136            str
1137        """
1138        repr_txt = "{}".format(self.nom_con_db)
1139
1140        return repr_txt
1141
1142    @staticmethod
1143    def log_dir():
1144        """
1145        Devuelve el directorio donde irán los logs indicado en la funcion apb_logging.logs_dir()
1146
1147        Returns:
1148            {str} - path del directorio de logs
1149        """
1150        return utils_logging.logs_dir(True)
1151
1152    def log_name(self):
1153        """
1154        Devuelve el nombre del fichero de log por defecto
1155
1156        Returns:
1157            {str} - Nombre fichero log por defecto
1158        """
1159        return self.__class__.__name__
1160
1161    def log_file_name(self):
1162        return "{}.(LOG_LEVEL).log".format(os.path.join(self.log_dir(), self.log_name()))
1163
1164    def __set_logger(self):
1165        """
1166        Asigna el LOGGER po defecto si este no se ha informado al inicializar el gestor
1167
1168        Returns:
1169        """
1170        if self.logger is None:
1171            self.logger = utils_logging.get_file_logger(self.log_name(), dir_log=self.log_dir())
1172
1173    def path_logs(self, if_exist=True):
1174        """
1175        Devuelve lista paths base de los logs vinculados al gestor
1176
1177        Args:
1178            if_exist (bool): Devuelve los paths si el fichero existe
1179
1180        Returns:
1181            list:
1182        """
1183        return logger_path_logs(self.logger)
1184
1185    def print_log(self, msg):
1186        """
1187        Sobre el logger escribe mensaje de info
1188
1189        Args:
1190            msg {str}: String con el mensaje
1191        """
1192        self.logger.info(msg)
1193
1194    def print_log_error(self, msg):
1195        """
1196        Sobre el logger escribe mensaje de error
1197
1198        Args:
1199            msg {str}: String con el mensaje
1200        """
1201        self.logger.error(msg)
1202
1203    def print_log_exception(self, msg):
1204        """
1205        Sobre el logger escribe excepcion
1206
1207        Args:
1208            msg {str}: String con el mensaje
1209        """
1210        self.logger.exception(msg)
1211
1212    @print_to_log_exception(lanzar_exc=True)
1213    def __set_conexion(self, user_ora, psw_ora, dsn_ora, schema_ora=None):
1214        """
1215        Añade conexion Oracle al gestor a partir de nombre de usuario/schema (user_ora), contraseña (psw_ora) y
1216        nombre datasource de la bbdd según tns_names (ds_ora).
1217
1218        La conexión quedará registrada como 'user_ora@ds_ora'
1219
1220        Args:
1221            user_ora {str}: Usuario/schema Oracle
1222            psw_ora {str}: Password usuario
1223            dsn_ora {str}: DSN Oracle (Nombre instancia/datasource de Oracle según TSN o string tal cual devuelve cx_Oracle.makedsn())
1224            schema_ora(str=None): indicate scheme when it is different from the user, default schema = user
1225
1226        """
1227        nom_con = "@".join((user_ora.upper(), dsn_ora.upper()))
1228        self.nom_con_db = nom_con
1229        self.__user_con_db__ = user_ora
1230        self.__psw_con_db__ = psw_ora
1231        self.__schema_con_db__ = schema_ora
1232        self.__dsn_ora__ = dsn_ora
1233        self.__con_db__ = get_oracle_connection(user_ora, psw_ora, dsn_ora, self.__call_timeout__,
1234                                                schema_ora=schema_ora)
1235
1236    @property
1237    @print_to_log_exception(cx_Oracle.Error, lanzar_exc=True)
1238    def con_db(self):
1239        """
1240        Return a cx_Oracle Conection live
1241        
1242        Returns:
1243            cx_Oracle.Connection
1244        """
1245        reconnect = False
1246        if (con_ora := self.__con_db__) is not None:
1247            try:
1248                con_ora.ping()
1249            except cx_Oracle.Error as exc:
1250                # Borramos las entradas de cache asociadas a la conexión que no responde
1251                del_cache_rel_con_db(get_nom_conexion(con_ora))
1252                try:
1253                    con_ora.close()
1254                except cx_Oracle.Error:
1255                    pass
1256                self.__con_db__ = con_ora = None
1257
1258        if con_ora is None:
1259            self.__set_conexion(
1260                self.__user_con_db__,
1261                self.__psw_con_db__,
1262                self.__dsn_ora__, schema_ora=self.__schema_con_db__)
1263
1264            con_ora = self.__con_db__
1265
1266        return con_ora
1267
1268    @print_to_log_exception(cx_Oracle.DatabaseError)
1269    def exec_trans_db(self, sql_str, *args_sql, **types_sql_args):
1270        """
1271        Ejecuta transaccion SQL
1272
1273        Args:
1274            sql_str (str): sql transaction (update, insert, delete}
1275            *args_sql: Lista argumentos a pasar
1276            **types_sql_args (OPCIONAL): Lista tipos cx_Oracle para cada argumento
1277
1278        Returns:
1279            ok {bool}: Si ha ido bien True si no False
1280        """
1281        curs_db = None
1282        try:
1283            curs_db = new_cursor(self.con_db,
1284                                 input_handler=m_sdo_geom.get_sdo_input_handler())
1285
1286            curs_db.setinputsizes(*types_sql_args.values())
1287
1288            curs_db.execute(sql_str,
1289                            args_sql)
1290        finally:
1291            if curs_db:
1292                curs_db.close()
1293
1294        return True
1295
1296    @print_to_log_exception(cx_Oracle.DatabaseError)
1297    def exec_script_plsql(self, sql_str):
1298        """
1299        Ejecuta script SQL
1300
1301        Args:
1302            sql_str {str}: sql script
1303
1304        Returns:
1305            ok {bool}: Si ha ido bien True si no False
1306        """
1307        curs_db = None
1308        try:
1309            curs_db = new_cursor(self.con_db)
1310            curs_db.execute(sql_str)
1311        finally:
1312            if curs_db:
1313                curs_db.close()
1314
1315        return True
1316
1317    @print_to_log_exception(cx_Oracle.DatabaseError)
1318    def callfunc_sql(self, nom_func, ret_cx_ora_tipo, *args_func):
1319        """
1320        Ejecuta funcion PL/SQL y retorna el valor
1321
1322        Args:
1323            nom_func (str): Nombre de la funcion PL/SQL
1324            ret_cx_ora_tipo (cx_Oracle TIPO): El retorno de la función en cx_Oracle (cx_Oracle.NUMBER,
1325                                            cx_Oracle.STRING,...)
1326            *args_func: Argumentos de la funcion PL/SQL
1327
1328        Returns:
1329            Valor retornado por la función PL/SQL
1330        """
1331        curs = None
1332        try:
1333            curs = new_cursor(self.con_db)
1334            ret = curs.callfunc(nom_func,
1335                                ret_cx_ora_tipo,
1336                                args_func)
1337        finally:
1338            if curs:
1339                curs.close()
1340
1341        return ret
1342
1343    @print_to_log_exception(cx_Oracle.DatabaseError)
1344    def callproc_sql(self, nom_proc, *args_proc):
1345        """
1346        Ejecuta procedimiento PL/SQL
1347
1348        Args:
1349            nom_proc (str): Nombre del procedimiento PL/SQL
1350            *args_proc: Argumentos del procedimiento
1351
1352        Returns:
1353            ok {bool}: Si ha ido bien True si no False
1354        """
1355        curs = None
1356        try:
1357            curs = new_cursor(self.con_db)
1358            curs.callproc(nom_proc,
1359                          args_proc)
1360        finally:
1361            if curs:
1362                curs.close()
1363
1364        return True
1365
1366    @print_to_log_exception(cx_Oracle.DatabaseError)
1367    def row_sql(self, sql_str, *args_sql, **extra_params):
1368        """
1369        Retorna la fila resultante de la query sql SQL_STR con los parámetros *ARGS_SQL.
1370        Se puede informar **EXTRA_PARAMS con algunos de los siguientes parámetros:
1371            INPUT_HANDLER funcion que tratará los bindings de manera específica
1372            OUTPUT_HANLER funcion que tratará los valores de las columnas de modo específico
1373            ROW_CLASS define que con que clase se devolverá cada fila. Por defecto la que devuleve la funcion
1374                      'apb_cx_oracle_spatial.get_row_class_cursor()'
1375            AS_FORMAT (as_xml, as_json, as_geojson) devuelve fila en el formato especificado. La row_class deberá
1376                        responder a esas funciones
1377
1378        Args:
1379            sql_str:
1380            *args_sql:
1381            **extra_params: {
1382                "row_class": clase que se utilizará para cada fila. Vease get_row_factory()
1383                "as_format": formato en el que se devuelve cada fila.
1384                    Las clases base row_cursor y row_table.
1385                    (vease get_row_class_cursor() y get_row_class_tab()) responden
1386                    por defecto a"as_xml()" y "as_json()"
1387                "geom_format": nombre función para formatear las geometrías SDO_GEOMETRY. (vease sdo_geom as_[format])
1388                "rowfactory": función rowfactory para crear las filas devueltas por el cursor
1389                "input_handler": función input_handler para el cursor (vease sdo_geom.get_sdo_input_handler())
1390            }
1391
1392        Returns:
1393            object (instancia que debería ser o heredar de las clases row_cursor o row_table)
1394        """
1395        return execute_fetch_sql(self.con_db,
1396                                 sql_str,
1397                                 *args_sql,
1398                                 **extra_params)
1399
1400    @print_to_log_exception(cx_Oracle.DatabaseError)
1401    def generator_rows_sql(self, sql_str, *args_sql, **extra_params):
1402        """
1403        Ejecuta consulta SQL de forma iterativa retornando cada fila como un objeto row_class (por defecto row_cursor)
1404
1405        Se puede informar **EXTRA_PARAMS con algunos de los siguientes parámetros:
1406            INPUT_HANDLER funcion que tratará los bindings de manera específica
1407            ROW_CLASS define que con que clase se devolverá cada fila. Por defecto la que devuleve la funcion
1408                      'apb_cx_oracle_spatial.get_row_class_cursor()'
1409            AS_FORMAT (as_xml, as_json, as_geojson, ...) devuelve fila en el formato especificado. La row_class deberá
1410                        responder a esas funciones
1411
1412        Args:
1413            sql_str:
1414            *args_sql:
1415            **extra_params: {
1416                "row_class": clase que se utilizará para cada fila. Vease get_row_factory()
1417                "as_format": formato en el que se devuelve cada fila.
1418                    Las clases base row_cursor y row_table.
1419                    (vease get_row_class_cursor() y get_row_class_tab()) responden
1420                    por defecto a"as_xml()" y "as_json()"
1421                "geom_format": nombre función para formatear las geometrías SDO_GEOMETRY. (vease sdo_geom as_[format])
1422                "rowfactory": función rowfactory para crear las filas devueltas por el cursor
1423                "input_handler": función input_handler para el cursor (vease sdo_geom.get_sdo_input_handler())
1424            }
1425
1426        Returns:
1427            object (instancia que debería ser o heredar de las clases row_cursor o row_table)
1428        """
1429        for reg in iter_execute_fetch_sql(self.con_db, sql_str, *args_sql,
1430                                          logger=self.logger,
1431                                          **extra_params):
1432            yield reg
1433
1434    def rows_sql(self, sql_str, *args_sql):
1435        """
1436        Vease funcion 'generator_rows_sql()'
1437        Args:
1438            sql_str:
1439            *args_sql:
1440
1441        Returns:
1442            list
1443        """
1444        return list(self.generator_rows_sql(sql_str, *args_sql))
1445
1446    def get_primary_key_table(self, nom_tab_or_view):
1447        """
1448        Retorna lista con las columnas que conforman la primary key de una tabla/vista
1449        Args:
1450            nom_tab_or_view:
1451
1452        Returns:
1453            list con campos clave
1454        """
1455        return get_pk_tab(self.con_db, nom_tab_or_view)
1456
1457    @print_to_log_exception(lanzar_exc=True)
1458    def get_dd_table(self, nom_tab_or_view):
1459        """
1460        Retorna instancia row_table con los tipos para cada columna
1461        Args:
1462            nom_tab_or_view:
1463
1464        Returns:
1465            object de clase row_table con los tipos de cada columna como valores
1466        """
1467        return get_row_desc_tab(self.con_db, nom_tab_or_view)
1468
1469    def generator_rows_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql, **extra_params):
1470        """
1471        Retorna los registros que cumplan con el FILTER_SQL sobre la tabla o vista indicada.
1472
1473        La tabla se puede referenciar en el filtro con el alias 'TAB'
1474
1475        Si el FILTER_SQL utiliza valores por binding (:1, :2,...) estos se indicaran por orden en ARGS_FILTER_SQL
1476
1477        Args:
1478            nom_tab_or_view: Nombre de la tabla o vista sobre la que se hará la consulta
1479            filter_sql: Filtre sobre la taula
1480            args_filter_sql: Valors en ordre de binding per passar
1481            extra_params: Vease generator_rows_sql()
1482
1483        Yields:
1484             row_table: regs. clase row_table (mirar get_row_class_tab())
1485        """
1486        for reg in self.generator_rows_sql(sql_tab(nom_tab_or_view,
1487                                                   filter_sql),
1488                                           *args_filter_sql,
1489                                           row_class=extra_params.pop(
1490                                               "row_class",
1491                                               get_row_class_tab(self.con_db, nom_tab_or_view)),
1492                                           **extra_params):
1493            yield reg
1494
1495    def generator_rows_interact_geom(self, nom_tab, a_sdo_geom, cols_geom=None, geom_format=None):
1496        """
1497        Retorna las filas de una tabla que interactuan con una geometria
1498
1499        Args:
1500            nom_tab: nombre de la tabla
1501            a_sdo_geom: geometria clase sdo_geom
1502            cols_geom (default=None): Lista con nombre de columnas geométricas sobre las que se quiere aplicar filtro
1503            geom_format:
1504        Yields:
1505            row_table: regs. clase row_table (mirar get_row_class_tab())
1506        """
1507        # Uso de " <>'FALSE'" por fallo de Oracle usando "= 'TRUE'"
1508        filter_interact_base = "SDO_ANYINTERACT({camp_geom}, :1) <> 'FALSE'"
1509
1510        if not cols_geom:
1511            cols_geom = get_tips_geom_tab(self.con_db, nom_tab).keys()
1512
1513        if cols_geom:
1514            filter_sql = " OR ".join([filter_interact_base.format(camp_geom=ng) for ng in cols_geom])
1515            for reg in self.generator_rows_table(nom_tab, filter_sql, a_sdo_geom.as_ora_sdo_geometry(),
1516                                                 geom_format=geom_format):
1517                yield reg
1518
1519    def rows_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql):
1520        """
1521        Vease funcion 'generator_rows_table()'
1522
1523        Args:
1524            nom_tab_or_view:
1525            filter_sql:
1526            *args_filter_sql:
1527
1528        Returns:
1529            dict
1530        """
1531        gen_tab = self.generator_rows_table(nom_tab_or_view,
1532                                            filter_sql,
1533                                            *args_filter_sql)
1534        pk_tab = self.get_primary_key_table(nom_tab_or_view)
1535        l_pk = len(pk_tab)
1536        if l_pk == 0:
1537            return [r for r in gen_tab]
1538        else:
1539            def f_key(r):
1540                return getattr(r, pk_tab[0])
1541
1542            if l_pk > 1:
1543                def f_key(r): return tuple(map(lambda nf: getattr(r, nf), pk_tab))
1544            return {f_key(r): r for r in gen_tab}
1545
1546    def row_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql, **extra_params):
1547        """
1548        Retorna primer registro para el FILTER_SQL sobre la tabla o vista indicada
1549        Si el FILTER_SQL utiliza valores por binding (:1, :2,...) estos se indicaran por orden en ARGS_FILTER_SQL
1550
1551        Args:
1552            nom_tab_or_view:
1553            filter_sql:
1554            *args_filter_sql:
1555            **extra_params:
1556
1557        Returns:
1558            object de la clase row_table o especificada en **extra_params['row_class']
1559        """
1560        gen = self.generator_rows_table(nom_tab_or_view,
1561                                        filter_sql,
1562                                        *args_filter_sql,
1563                                        **extra_params)
1564        return next(gen, None)
1565
1566    def row_table_at(self, nom_tab_or_view, *vals_key):
1567        """
1568        Devuelve row_tabla_class para el registro que de la tabla_vista que cumpla con la clave
1569
1570        Args:
1571            nom_tab_or_view:
1572            *vals_key:
1573
1574        Returns:
1575            object de la clase row_table o especificada en **extra_params['row_class']
1576        """
1577        return self.exist_row_tab(nom_tab_or_view,
1578                                  {nc: val for nc, val in zip(self.get_primary_key_table(nom_tab_or_view),
1579                                                              vals_key)})
1580
1581    def test_row_table(self, row_tab, a_sql, *args_sql):
1582        """
1583        Testea un registro de tabla (clase rwo_table) cumpla con sql indicado
1584
1585        Args:
1586            row_tab: registro de tabla en forma de clase row_table
1587            a_sql: string con sql a testear
1588
1589        Returns:
1590            bool: True o False según cumpla con el SQL indicado
1591        """
1592        sql_pk = dict_as_sql_bind_and_params(row_tab.pk_vals())
1593        query_sql = "{} AND ({})".format(sql_pk[0], a_sql)
1594
1595        ret = False
1596        if self.row_table(row_tab.nom_tab, query_sql, *(tuple(sql_pk[1]) + tuple(args_sql))):
1597            ret = True
1598
1599        return ret
1600
1601    def insert_row_tab(self, nom_tab, dict_vals_param=None, dict_vals_str=None, pasar_nulls=False):
1602        """
1603        Inserta registro en la tabla indicada. Los valores para cada columna se pasarán a través de dict_vals_param
1604        como bindings o a través de dict_vals_str directemente en el string del sql ejecutado
1605        Args:
1606            nom_tab: nombre de la tabla
1607            dict_vals_param: diccionario indexado por columnas-valores. Los valores se pasarán como bindings
1608            dict_vals_str: diccionario indexado por columnas-valores a asignar como strings. El valor se pasa
1609                        directamente como asignacion en la senetencia sql
1610            pasar_nulls: (opcional) por defecto False. Indica si los valores NULL se asignarán o no
1611
1612        Returns:
1613            row_table (si genera el registro) o False si va mal la operación
1614        """
1615        if not dict_vals_param:
1616            dict_vals_param = {}
1617        if not dict_vals_str:
1618            dict_vals_str = {}
1619
1620        ora_dict_vals_param = self.get_vals_tab_for_transdb(nom_tab, dict_vals_param, pasar_nulls=pasar_nulls)
1621
1622        if pasar_nulls:
1623            # Se revisa que en un campo geometrico se asigne None y por lo tanto se asigne valor via STR
1624            geoms_null = [ng for ng, val in ora_dict_vals_param.items()
1625                          if not val and self.get_tip_camp_geom(nom_tab, ng)]
1626            if geoms_null:
1627                keys_str = [nc.upper() for nc in dict_vals_str.keys()]
1628                for gn in geoms_null:
1629                    ora_dict_vals_param.pop(gn)
1630                    if gn.upper() not in keys_str:
1631                        dict_vals_str[gn.upper()] = "NULL"
1632
1633        params = []
1634        nom_camps = []
1635        vals_camps = []
1636        for nom_camp, val_camp in ora_dict_vals_param.items():
1637            if val_camp is None:
1638                continue
1639            nom_camps.append(nom_camp)
1640            vals_camps.append(":" + nom_camp)
1641            params.append(val_camp)
1642
1643        for nom_camp, val_camp_str in dict_vals_str.items():
1644            if val_camp_str is None:
1645                continue
1646            nom_camps.append(nom_camp)
1647            vals_camps.append(str(val_camp_str))
1648
1649        row_desc_tab = get_row_desc_tab(self.con_db, nom_tab)
1650        pk_binds = {k: new_cursor(self.con_db).var(ora_tip_camp) for k, ora_tip_camp in row_desc_tab.pk_vals().items()}
1651        if not pk_binds:
1652            pk_binds = {'ROWID': new_cursor(self.con_db).var(cx_Oracle.ROWID)}
1653        str_pk_camps = ",".join(pk_binds.keys())
1654        str_pk_binds = ",".join(list(map(lambda x: ":ret_" + str(x), pk_binds.keys())))
1655        params += list(pk_binds.values())
1656
1657        a_sql_res = f"insert into {nom_tab}({','.join(nom_camps)}) values({','.join(vals_camps)}) " \
1658                    f"returning {str_pk_camps} into {str_pk_binds}"
1659
1660        ok = self.exec_trans_db(a_sql_res, *params)
1661        if ok:
1662            pk_vals = {k: curs_var.getvalue(0)[0] for k, curs_var in pk_binds.items()}
1663            return self.exist_row_tab(nom_tab, pk_vals)
1664
1665        return ok
1666
1667    def update_row_tab(self, nom_tab, dict_clau_reg, dict_vals_param=None, dict_vals_str=None, pasar_nulls=None):
1668        """
1669        Actualiza registro en la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor}
1670        Los valores para cada columna se pasarán a través de dict_vals_param como bindings o a través de
1671        dict_vals_str directemente en el string del sql ejecutado
1672
1673        Args:
1674            nom_tab: nombre de la tabla
1675            dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar
1676            dict_vals_param: diccionario indexado por columnas-valores. Los valores se pasarán como bindings
1677            dict_vals_str: diccionario indexado por columnas-valores a asignar como strings. El valor se pasa
1678                        directamente como asignacion en la senetencia sql
1679            pasar_nulls: (opcional) por defecto False. Indica si los valores NULL se asignarán o no
1680
1681        Returns:
1682            row_table (si genera el registro) o False si va mal la operación
1683        """
1684        if not dict_vals_param:
1685            dict_vals_param = {}
1686        if not dict_vals_str:
1687            dict_vals_str = {}
1688
1689        ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg)
1690        ora_dict_vals_param = self.get_vals_tab_for_transdb(nom_tab, dict_vals_param, pasar_nulls=pasar_nulls)
1691
1692        if pasar_nulls:
1693            # Se revisa que en un campo geometrico se asigne None y por lo tanto se asigne valor via STR
1694            geoms_null = [ng for ng, val in ora_dict_vals_param.items()
1695                          if not val and self.get_tip_camp_geom(nom_tab, ng)]
1696            if geoms_null:
1697                keys_str = [nc.upper() for nc in dict_vals_str.keys()]
1698                for gn in geoms_null:
1699                    ora_dict_vals_param.pop(gn)
1700                    if gn.upper() not in keys_str:
1701                        dict_vals_str[gn.upper()] = "NULL"
1702
1703        (sql_set_camps, params_set_camps) = dict_as_sql_bind_and_params(ora_dict_vals_param,
1704                                                                        ",", "=")
1705
1706        (query_clau, params_filter) = dict_as_sql_bind_and_params(ora_dict_clau_reg)
1707
1708        params = params_set_camps + params_filter
1709
1710        for nom_camp, val_camp_str in dict_vals_str.items():
1711            if sql_set_camps:
1712                sql_set_camps += " , "
1713            sql_set_camps += nom_camp + "=" + val_camp_str
1714
1715        ok = None
1716        if sql_set_camps:
1717            a_sql_res = f"update {nom_tab} set {sql_set_camps} where {query_clau}"
1718
1719            ok = self.exec_trans_db(a_sql_res, *params)
1720
1721            if ok:
1722                return self.exist_row_tab(nom_tab, dict_clau_reg)
1723
1724        return ok
1725
1726    def remove_row_tab(self, nom_tab, dict_clau_reg):
1727        """
1728        Borra registro en la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor}
1729
1730        Args:
1731            nom_tab: nombre de la tabla
1732            dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar
1733
1734        Returns:
1735            bool según vaya la operación
1736        """
1737        ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg)
1738
1739        a_sql_tmpl = "delete {nom_tab} where {query_clau}"
1740
1741        (sql_filter, params) = dict_as_sql_bind_and_params(ora_dict_clau_reg)
1742
1743        a_sql_res = a_sql_tmpl.format(nom_tab=nom_tab,
1744                                      query_clau=sql_filter)
1745
1746        return self.exec_trans_db(a_sql_res, *params)
1747
1748    def exist_row_tab(self, nom_tab, dict_clau_reg, **extra_params):
1749        """
1750        Devuelve registro de la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor}
1751
1752        Args:
1753            nom_tab: nombre de la tabla
1754            dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar
1755            **extra_params:
1756
1757        Returns:
1758            object de la clase row_table o especificada en **extra_params['row_class']
1759        """
1760        ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg)
1761
1762        (filter_sql, params) = dict_as_sql_bind_and_params(ora_dict_clau_reg, "and", "=")
1763
1764        return self.row_table(nom_tab, filter_sql, *params, **extra_params)
1765
1766    def get_vals_tab_for_transdb(self, nom_tab, dict_camps_vals, pasar_nulls=True):
1767        """
1768        Para un tabla y diccionario columnas-valores devuelve diccionario indexado por las columnas con los valores
1769        convertidos a formato cx_Oracle según tipo de cada columna en Oracle
1770        Args:
1771            nom_tab: nombre de la tabla
1772            dict_camps_vals: diccionario indexado por columnas-valor a convertir a formato cx_Oracle
1773            pasar_nulls: (opcional) por defecto convertirá los None a NULL de Oracle
1774
1775        Returns:
1776            dict indexado por columnas con los valores convertidos a tipo cx_Oracle
1777        """
1778        dd_tab = get_row_desc_tab(self.con_db, nom_tab)
1779
1780        # Retorna dict con los campos a pasar por parametro
1781        d_params = {}
1782
1783        # Los nombres de campo siempre se buscarán en mayúsculas
1784        dict_camps_vals = {k.upper(): v for k, v in dict_camps_vals.items()}
1785
1786        for camp, tip_camp in dd_tab.vals().items():
1787            if camp not in dict_camps_vals:
1788                continue
1789
1790            val_camp = dict_camps_vals.get(camp)
1791            if not pasar_nulls and val_camp is None:
1792                continue
1793
1794            if isinstance(val_camp, m_sdo_geom.sdo_geom):
1795                var = val_camp.as_ora_sdo_geometry()
1796            else:
1797                try:
1798                    var = new_cursor(self.con_db).var(tip_camp)
1799                    var.setvalue(0, val_camp)
1800                except:
1801                    var = val_camp
1802
1803            d_params[camp] = var
1804
1805        return d_params
1806
1807    @print_to_log_exception()
1808    def run_sql_script(self, filename):
1809        """
1810        Ejecuta slq script (filename) sobre SQLPLUS
1811
1812        Args:
1813            filename: path del sql script
1814
1815        Returns:
1816
1817        """
1818        user_ora = self.con_db.username
1819        ds_ora = self.con_db.dsn
1820        nom_con = self.nom_con_db
1821        psw_ora = self.__psw_con_db__
1822        if psw_ora is None:
1823            print("ERROR - Conexión '" + nom_con + "' no está añadida al gestor!!")
1824            return
1825
1826        with open(filename, 'rb') as a_file:
1827            a_sql_command = a_file.read()
1828
1829        con_db_str = user_ora + "/" + psw_ora + "@" + ds_ora
1830        sqlplus = Popen(['sqlplus', '-S', con_db_str], stdin=PIPE, stdout=PIPE, stderr=PIPE)
1831        # sqlplus.stdin.write(a_sql_command)
1832
1833        (stdout, stderr) = sqlplus.communicate(a_sql_command)
1834
1835        self.print_log("Resultado lanzar script '{}': \n"
1836                       "{}".format(filename,
1837                                   stdout.decode("utf-8")))
1838
1839        if sqlplus is not None:
1840            sqlplus.terminate()
1841
1842    @staticmethod
1843    def get_nom_obj_sql(nom_base, prefix="", sufix=""):
1844        """
1845        Retorna nombre propuesto con prefijo/sufijos formateado para que cumpla longitud máxima de 32 caracteres en
1846        objetos sql Oracle
1847
1848        Args:
1849            nom_base: nombre propuesto
1850            prefix: (opc) prefijo
1851            sufix: (opc) sufijo
1852
1853        Returns:
1854            str formateado
1855        """
1856        return x_sql_parser.get_nom_obj_sql(nom_base, prefix, sufix)
1857
1858    def iter_sdo_gtypes_vals_camp_tab(self, nom_taula, nom_camp):
1859        """
1860        Retorna los distintos tipos de Geometria (codigo entero que define el tipo SDO_GTYPE)
1861        que se encuentran dentro de la columna sdo_geometry de una tabla
1862
1863        Args:
1864            nom_taula: nombre de la tabla
1865            nom_camp: nombre campo geometrico
1866
1867        Returns:
1868            int definiendo tipo de geometría
1869        """
1870        sql_tip_geoms = f"select distinct(tab.{nom_camp}.Get_GType()) as tip_geom from {nom_taula} tab " \
1871                        f"where {nom_camp} is not null"
1872
1873        for reg in self.generator_rows_sql(sql_tip_geoms):
1874            yield reg.TIP_GEOM
1875
1876    def iter_distinct_vals_camp_tab(self, nom_taula, nom_camp, filter_sql=None):
1877        """
1878        Retorna los distintos valores de la columna de una tabla
1879
1880        Args:
1881            nom_taula (str): Nombre de tabla
1882            nom_camp (str): Nombre de campo
1883            filter_sql(str): Filtro SQL sobre la tabla indicada
1884
1885        Returns:
1886            {str}: Itera los distintos valores encontrados en el campo indicado
1887        """
1888        sql_distinct_vals = f"select distinct(tab.{nom_camp}) as VAL from {nom_taula} tab"
1889        if filter_sql:
1890            sql_distinct_vals += " where " + filter_sql
1891
1892        for reg in self.generator_rows_sql(sql_distinct_vals):
1893            yield reg.VAL
1894
1895    def get_tip_camp_geom(self, nom_tab_or_view, nom_camp_geom):
1896        """
1897        Retorna el tipo de campo geométrico (class_tip_geom) registrados en la global __class_tips_geom_ora
1898        para el campo indicado
1899
1900        Args:
1901            nom_tab_or_view: nombre tabla/vista
1902            nom_camp_geom: nombre campo geom
1903
1904        Returns:
1905            namedtuple: con atributos ['TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID'])
1906        """
1907        tips_geom_tab = get_tips_geom_tab(self.con_db, nom_tab_or_view)
1908        if tips_geom_tab:
1909            return tips_geom_tab.get(nom_camp_geom.upper())
1910
1911    def get_epsg_for_srid(self, srid):
1912        """
1913        Rertorna WKT con la definicion del SRID dado
1914        """
1915        return self.callfunc_sql('SDO_CS.MAP_ORACLE_SRID_TO_EPSG', cx_Oracle.NUMBER, srid)
1916
1917    def get_gtype_camp_geom(self, nom_tab_or_view, nom_camp_geom):
1918        """
1919        Retorna el tipo GTYPE (int) de la geometria
1920
1921        Args:
1922            nom_tab_or_view: nombre tabla/vista
1923            nom_camp_geom: nombre campo geom
1924
1925        Returns:
1926            int
1927        """
1928        gtype = 0
1929        g_tip_ora = self.get_tip_camp_geom(nom_tab_or_view, nom_camp_geom)
1930        if g_tip_ora:
1931            gtype = GTYPES_ORA.index(g_tip_ora.GTYPE)
1932
1933        return gtype
1934
1935    @staticmethod
1936    def verificar_path_vector_file(nom_tab_or_view, dir, file_name, ext, zipped):
1937        """
1938        Compone el/los path para el/los vector_file de una tabla y determina si exists
1939        Args:
1940            nom_tab_or_view:
1941            dir:
1942            file_name:
1943            ext:
1944            zipped:
1945
1946        Returns:
1947            file_path (str), file_path_zip (str), exists (bool)
1948        """
1949        if file_name and not file_name.endswith(ext):
1950            file_name = ".".join((file_name, ext))
1951        elif not file_name:
1952            file_name = ".".join((nom_tab_or_view, ext)).lower()
1953
1954        file_path = os.path.join(dir, file_name)
1955        file_path_zip = None
1956        if zipped:
1957            file_path_zip = "{}.zip".format(os.path.splitext(file_path)[0])
1958
1959        exists = (os.path.exists(file_path) and not file_path_zip) or (file_path_zip and os.path.exists(file_path_zip))
1960
1961        return file_path, file_path_zip, exists
1962
1963    @print_to_log_exception()
1964    def create_json_tab_or_view(self, nom_tab_or_view, dir='.', file_name=None, overwrite=True, cols=None, zipped=True,
1965                                filter_sql=None, *args_sql):
1966        """
1967
1968        Args:
1969            nom_tab_or_view (str): Nombre tabla o vista
1970            dir (str):
1971            file_name (str):
1972            overwrite (bool):
1973            cols (list): columnas
1974            zipped (bool):
1975            filter_sql (str):
1976            *args_sql: lista de argumentos a pasar al filtro sql
1977        Returns:
1978            file_path (str)
1979        """
1980        file_path, file_path_zip, exists = self.verificar_path_vector_file(
1981            nom_tab_or_view, dir, file_name, "json", zipped)
1982
1983        if overwrite or not exists:
1984            # Se calculan las columnas para hacer get de la fila con el orden en las columnas de la tabla
1985            if not cols:
1986                dd_tab = self.get_dd_table(nom_tab_or_view)
1987                cols = dd_tab.cols
1988            sql = sql_tab(nom_tab_or_view,
1989                          filter_sql=filter_sql,
1990                          columns=cols)
1991
1992            file_path_res = vector_file_from_gen_ora_sql(file_path, "json", self.generator_rows_sql(sql, *args_sql),
1993                                                         zipped=zipped)
1994        else:
1995            file_path_res = file_path if not zipped else file_path_zip
1996
1997        return file_path_res
1998
1999    @print_to_log_exception()
2000    def create_csv_tab_or_view(self, nom_tab_or_view, dir='.', file_name=None, overwrite=True, cols=None, zipped=True,
2001                               filter_sql=None, *args_sql):
2002        """
2003
2004        Args:
2005            nom_tab_or_view (str): Nombre tabla o vista
2006            dir (str):
2007            file_name (str):
2008            overwrite (bool):
2009            cols (list): columnas
2010            zipped (bool):
2011            filter_sql (str):
2012            *args_sql: lista de argumentos a pasar al filtro sql
2013
2014        Returns:
2015            file_path_res (str)
2016        """
2017        file_path, file_path_zip, exists = self.verificar_path_vector_file(
2018            nom_tab_or_view, dir, file_name, "csv", zipped)
2019
2020        if overwrite or not exists:
2021            if not cols:
2022                dd_tab = self.get_dd_table(nom_tab_or_view)
2023                cols = dd_tab.cols
2024            sql = sql_tab(nom_tab_or_view,
2025                          filter_sql=filter_sql,
2026                          columns=cols)
2027
2028            # Para el formato geocsv que acepta GDAL se añade fichero con los tipos de columna
2029            tip_cols_csv = []
2030            for col in cols:
2031                r_tip_col = self.row_table("user_tab_columns",
2032                                           "table_name = :1 and column_name = :2",
2033                                           nom_tab_or_view.upper(), col.upper())
2034                dtype = r_tip_col.DATA_TYPE
2035                dlength = r_tip_col.DATA_LENGTH
2036                dprecision = r_tip_col.DATA_PRECISION
2037                dscale = r_tip_col.DATA_SCALE
2038
2039                if dtype == "DATE":
2040                    tip_cols_csv.append('"DateTime"')
2041                elif dtype == "FLOAT":
2042                    tip_cols_csv.append('"Real({}.{})"'.format(dlength, dprecision))
2043                elif dtype == "NUMBER":
2044                    if dscale and dscale != 0:
2045                        tip_cols_csv.append('"Real({}.{})"'.format(dprecision, dscale))
2046                    elif dprecision:
2047                        tip_cols_csv.append('"Integer({})"'.format(dprecision))
2048                    else:
2049                        tip_cols_csv.append('"Real(10.8)"')
2050                elif dtype == "SDO_GEOMETRY":
2051                    tip_cols_csv.append('"WKT"')
2052                else:
2053                    tip_cols_csv.append('"String({})"'.format(round(dlength * 1.25)))
2054
2055            file_path_res = vector_file_from_gen_ora_sql(file_path, "csv",
2056                                                         self.generator_rows_sql(sql, *args_sql, geom_format="as_wkt"),
2057                                                         zipped=zipped, cols_csv=cols, tip_cols_csv=tip_cols_csv)
2058        else:
2059            file_path_res = file_path if not zipped else file_path_zip
2060
2061        return file_path_res
2062
2063    @print_to_log_exception()
2064    def create_geojsons_tab_or_view(self, nom_tab_or_view, dir='.', file_name_prefix=None, by_geom=False,
2065                                    dir_topojson=None, overwrite=True, cols=None,
2066                                    filter_sql=None, *args_sql):
2067        """
2068
2069        Args:
2070            nom_tab_or_view (str): Nombre tabla (vigente o versionada) para entidad GIS
2071            dir (str="."):
2072            file_name_prefix (str=None): (opcional) prefijo del fichero
2073            by_geom (bool=False): (Opcional) si se querrán los geojsons por geometria. Si no se saca un unico geojson con
2074                            la columna geometry como una GeometryCollection si la tabla es multigeom
2075            dir_topojson (str=None): path donde irán las conversiones
2076            overwrite (bool=True):
2077            cols (list=None):
2078            filter_sql (str=None):
2079            *args_sql: lista de argumentos a pasar al filtro sql
2080
2081        Returns:
2082            ok (bool)
2083        """
2084        ext = "geo.json"
2085        sqls = {}
2086        dd_tab = self.get_dd_table(nom_tab_or_view)
2087        if not cols:
2088            cols = dd_tab.cols
2089
2090        if by_geom:
2091            c_alfas = [cn for cn in dd_tab.alfas() if cn in cols]
2092            c_geoms = [cn for cn in dd_tab.geoms() if cn in cols]
2093            for c_geom in c_geoms:
2094                sqls[c_geom] = sql_tab(nom_tab_or_view, filter_sql, c_alfas + [c_geom])
2095        else:
2096            sqls[None] = sql_tab(nom_tab_or_view, filter_sql, cols)
2097
2098        if not file_name_prefix:
2099            file_name_prefix = nom_tab_or_view
2100
2101        for ng, sql in sqls.items():
2102            file_name = file_name_prefix
2103            if ng:
2104                file_name = "-".join((file_name, ng))
2105
2106            file_name = ".".join((file_name, ext)).lower()
2107            file_path = os.path.join(dir, file_name)
2108
2109            if overwrite or not os.path.exists(file_path):
2110                file_path = vector_file_from_gen_ora_sql(file_path, "geojson",
2111                                                         self.generator_rows_sql(sql, *args_sql))
2112
2113            if by_geom and dir_topojson and file_path:
2114                tip_geom = getattr(dd_tab, ng).GTYPE
2115                simplify = True
2116                if tip_geom.endswith("POINT"):
2117                    simplify = False
2118
2119                topojson_utils.geojson_to_topojson(file_path, dir_topojson,
2120                                                   simplify=simplify,
2121                                                   overwrite=overwrite)
2122
2123        return True

Clase que gestionará distintas conexiones a Oracle y facilitará operaciones sobre la BBDD

gestor_oracle( user_ora, psw_ora, dsn_ora, a_logger=None, call_timeout: int = None, schema_ora=None)
1101    def __init__(self, user_ora, psw_ora, dsn_ora, a_logger=None, call_timeout: int = None, schema_ora=None):
1102        """
1103        Inicializa gestor de Oracle para una conexion cx_Oracle a Oracle
1104        Se puede pasar por parametro un logger o inicializar por defecto
1105
1106        Args:
1107            user_ora {str}: Usuario/schema Oracle
1108            psw_ora {str}: Password usuario
1109            dsn_ora {str}: DSN Oracle (Nombre instancia/datasource de Oracle
1110                    según TSN o string tal cual devuelve cx_Oracle.makedsn())
1111            call_timeout (int=None): miliseconds espera per transaccio
1112            a_logger:
1113            schema_ora(str=None): indicate scheme when it is different from the user, default schema = user
1114        """
1115        self.__con_db__ = None
1116        self.__call_timeout__ = call_timeout
1117        self.logger = a_logger
1118        self.__set_logger()
1119        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
tip_number = <ApiType NUMBER>
tip_string = <ApiType STRING>
tip_clob = <DbType DB_TYPE_CLOB>
tip_blob = <DbType DB_TYPE_BLOB>
tip_date = <ApiType DATETIME>
tip_fix_char = <DbType DB_TYPE_CHAR>
logger
@staticmethod
def log_dir():
1142    @staticmethod
1143    def log_dir():
1144        """
1145        Devuelve el directorio donde irán los logs indicado en la funcion apb_logging.logs_dir()
1146
1147        Returns:
1148            {str} - path del directorio de logs
1149        """
1150        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

def log_name(self):
1152    def log_name(self):
1153        """
1154        Devuelve el nombre del fichero de log por defecto
1155
1156        Returns:
1157            {str} - Nombre fichero log por defecto
1158        """
1159        return self.__class__.__name__

Devuelve el nombre del fichero de log por defecto

Returns:

{str} - Nombre fichero log por defecto

def log_file_name(self):
1161    def log_file_name(self):
1162        return "{}.(LOG_LEVEL).log".format(os.path.join(self.log_dir(), self.log_name()))
def path_logs(self, if_exist=True):
1173    def path_logs(self, if_exist=True):
1174        """
1175        Devuelve lista paths base de los logs vinculados al gestor
1176
1177        Args:
1178            if_exist (bool): Devuelve los paths si el fichero existe
1179
1180        Returns:
1181            list:
1182        """
1183        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:

def print_log(self, msg):
1185    def print_log(self, msg):
1186        """
1187        Sobre el logger escribe mensaje de info
1188
1189        Args:
1190            msg {str}: String con el mensaje
1191        """
1192        self.logger.info(msg)

Sobre el logger escribe mensaje de info

Arguments:
  • msg {str}: String con el mensaje
def print_log_error(self, msg):
1194    def print_log_error(self, msg):
1195        """
1196        Sobre el logger escribe mensaje de error
1197
1198        Args:
1199            msg {str}: String con el mensaje
1200        """
1201        self.logger.error(msg)

Sobre el logger escribe mensaje de error

Arguments:
  • msg {str}: String con el mensaje
def print_log_exception(self, msg):
1203    def print_log_exception(self, msg):
1204        """
1205        Sobre el logger escribe excepcion
1206
1207        Args:
1208            msg {str}: String con el mensaje
1209        """
1210        self.logger.exception(msg)

Sobre el logger escribe excepcion

Arguments:
  • msg {str}: String con el mensaje
con_db
1236    @property
1237    @print_to_log_exception(cx_Oracle.Error, lanzar_exc=True)
1238    def con_db(self):
1239        """
1240        Return a cx_Oracle Conection live
1241        
1242        Returns:
1243            cx_Oracle.Connection
1244        """
1245        reconnect = False
1246        if (con_ora := self.__con_db__) is not None:
1247            try:
1248                con_ora.ping()
1249            except cx_Oracle.Error as exc:
1250                # Borramos las entradas de cache asociadas a la conexión que no responde
1251                del_cache_rel_con_db(get_nom_conexion(con_ora))
1252                try:
1253                    con_ora.close()
1254                except cx_Oracle.Error:
1255                    pass
1256                self.__con_db__ = con_ora = None
1257
1258        if con_ora is None:
1259            self.__set_conexion(
1260                self.__user_con_db__,
1261                self.__psw_con_db__,
1262                self.__dsn_ora__, schema_ora=self.__schema_con_db__)
1263
1264            con_ora = self.__con_db__
1265
1266        return con_ora

Return a cx_Oracle Conection live

Returns:

cx_Oracle.Connection

@print_to_log_exception(cx_Oracle.DatabaseError)
def exec_trans_db(self, sql_str, *args_sql, **types_sql_args):
1268    @print_to_log_exception(cx_Oracle.DatabaseError)
1269    def exec_trans_db(self, sql_str, *args_sql, **types_sql_args):
1270        """
1271        Ejecuta transaccion SQL
1272
1273        Args:
1274            sql_str (str): sql transaction (update, insert, delete}
1275            *args_sql: Lista argumentos a pasar
1276            **types_sql_args (OPCIONAL): Lista tipos cx_Oracle para cada argumento
1277
1278        Returns:
1279            ok {bool}: Si ha ido bien True si no False
1280        """
1281        curs_db = None
1282        try:
1283            curs_db = new_cursor(self.con_db,
1284                                 input_handler=m_sdo_geom.get_sdo_input_handler())
1285
1286            curs_db.setinputsizes(*types_sql_args.values())
1287
1288            curs_db.execute(sql_str,
1289                            args_sql)
1290        finally:
1291            if curs_db:
1292                curs_db.close()
1293
1294        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

@print_to_log_exception(cx_Oracle.DatabaseError)
def exec_script_plsql(self, sql_str):
1296    @print_to_log_exception(cx_Oracle.DatabaseError)
1297    def exec_script_plsql(self, sql_str):
1298        """
1299        Ejecuta script SQL
1300
1301        Args:
1302            sql_str {str}: sql script
1303
1304        Returns:
1305            ok {bool}: Si ha ido bien True si no False
1306        """
1307        curs_db = None
1308        try:
1309            curs_db = new_cursor(self.con_db)
1310            curs_db.execute(sql_str)
1311        finally:
1312            if curs_db:
1313                curs_db.close()
1314
1315        return True

Ejecuta script SQL

Arguments:
  • sql_str {str}: sql script
Returns:

ok {bool}: Si ha ido bien True si no False

@print_to_log_exception(cx_Oracle.DatabaseError)
def callfunc_sql(self, nom_func, ret_cx_ora_tipo, *args_func):
1317    @print_to_log_exception(cx_Oracle.DatabaseError)
1318    def callfunc_sql(self, nom_func, ret_cx_ora_tipo, *args_func):
1319        """
1320        Ejecuta funcion PL/SQL y retorna el valor
1321
1322        Args:
1323            nom_func (str): Nombre de la funcion PL/SQL
1324            ret_cx_ora_tipo (cx_Oracle TIPO): El retorno de la función en cx_Oracle (cx_Oracle.NUMBER,
1325                                            cx_Oracle.STRING,...)
1326            *args_func: Argumentos de la funcion PL/SQL
1327
1328        Returns:
1329            Valor retornado por la función PL/SQL
1330        """
1331        curs = None
1332        try:
1333            curs = new_cursor(self.con_db)
1334            ret = curs.callfunc(nom_func,
1335                                ret_cx_ora_tipo,
1336                                args_func)
1337        finally:
1338            if curs:
1339                curs.close()
1340
1341        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

@print_to_log_exception(cx_Oracle.DatabaseError)
def callproc_sql(self, nom_proc, *args_proc):
1343    @print_to_log_exception(cx_Oracle.DatabaseError)
1344    def callproc_sql(self, nom_proc, *args_proc):
1345        """
1346        Ejecuta procedimiento PL/SQL
1347
1348        Args:
1349            nom_proc (str): Nombre del procedimiento PL/SQL
1350            *args_proc: Argumentos del procedimiento
1351
1352        Returns:
1353            ok {bool}: Si ha ido bien True si no False
1354        """
1355        curs = None
1356        try:
1357            curs = new_cursor(self.con_db)
1358            curs.callproc(nom_proc,
1359                          args_proc)
1360        finally:
1361            if curs:
1362                curs.close()
1363
1364        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

@print_to_log_exception(cx_Oracle.DatabaseError)
def row_sql(self, sql_str, *args_sql, **extra_params):
1366    @print_to_log_exception(cx_Oracle.DatabaseError)
1367    def row_sql(self, sql_str, *args_sql, **extra_params):
1368        """
1369        Retorna la fila resultante de la query sql SQL_STR con los parámetros *ARGS_SQL.
1370        Se puede informar **EXTRA_PARAMS con algunos de los siguientes parámetros:
1371            INPUT_HANDLER funcion que tratará los bindings de manera específica
1372            OUTPUT_HANLER funcion que tratará los valores de las columnas de modo específico
1373            ROW_CLASS define que con que clase se devolverá cada fila. Por defecto la que devuleve la funcion
1374                      'apb_cx_oracle_spatial.get_row_class_cursor()'
1375            AS_FORMAT (as_xml, as_json, as_geojson) devuelve fila en el formato especificado. La row_class deberá
1376                        responder a esas funciones
1377
1378        Args:
1379            sql_str:
1380            *args_sql:
1381            **extra_params: {
1382                "row_class": clase que se utilizará para cada fila. Vease get_row_factory()
1383                "as_format": formato en el que se devuelve cada fila.
1384                    Las clases base row_cursor y row_table.
1385                    (vease get_row_class_cursor() y get_row_class_tab()) responden
1386                    por defecto a"as_xml()" y "as_json()"
1387                "geom_format": nombre función para formatear las geometrías SDO_GEOMETRY. (vease sdo_geom as_[format])
1388                "rowfactory": función rowfactory para crear las filas devueltas por el cursor
1389                "input_handler": función input_handler para el cursor (vease sdo_geom.get_sdo_input_handler())
1390            }
1391
1392        Returns:
1393            object (instancia que debería ser o heredar de las clases row_cursor o row_table)
1394        """
1395        return execute_fetch_sql(self.con_db,
1396                                 sql_str,
1397                                 *args_sql,
1398                                 **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: { "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()" "geom_format": nombre función para formatear las geometrías SDO_GEOMETRY. (vease sdo_geom as_[format]) "rowfactory": función rowfactory para crear las filas devueltas por el cursor "input_handler": función input_handler para el cursor (vease sdo_geom.get_sdo_input_handler())
  • }
Returns:

object (instancia que debería ser o heredar de las clases row_cursor o row_table)

@print_to_log_exception(cx_Oracle.DatabaseError)
def generator_rows_sql(self, sql_str, *args_sql, **extra_params):
1400    @print_to_log_exception(cx_Oracle.DatabaseError)
1401    def generator_rows_sql(self, sql_str, *args_sql, **extra_params):
1402        """
1403        Ejecuta consulta SQL de forma iterativa retornando cada fila como un objeto row_class (por defecto row_cursor)
1404
1405        Se puede informar **EXTRA_PARAMS con algunos de los siguientes parámetros:
1406            INPUT_HANDLER funcion que tratará los bindings de manera específica
1407            ROW_CLASS define que con que clase se devolverá cada fila. Por defecto la que devuleve la funcion
1408                      'apb_cx_oracle_spatial.get_row_class_cursor()'
1409            AS_FORMAT (as_xml, as_json, as_geojson, ...) devuelve fila en el formato especificado. La row_class deberá
1410                        responder a esas funciones
1411
1412        Args:
1413            sql_str:
1414            *args_sql:
1415            **extra_params: {
1416                "row_class": clase que se utilizará para cada fila. Vease get_row_factory()
1417                "as_format": formato en el que se devuelve cada fila.
1418                    Las clases base row_cursor y row_table.
1419                    (vease get_row_class_cursor() y get_row_class_tab()) responden
1420                    por defecto a"as_xml()" y "as_json()"
1421                "geom_format": nombre función para formatear las geometrías SDO_GEOMETRY. (vease sdo_geom as_[format])
1422                "rowfactory": función rowfactory para crear las filas devueltas por el cursor
1423                "input_handler": función input_handler para el cursor (vease sdo_geom.get_sdo_input_handler())
1424            }
1425
1426        Returns:
1427            object (instancia que debería ser o heredar de las clases row_cursor o row_table)
1428        """
1429        for reg in iter_execute_fetch_sql(self.con_db, sql_str, *args_sql,
1430                                          logger=self.logger,
1431                                          **extra_params):
1432            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 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: { "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()" "geom_format": nombre función para formatear las geometrías SDO_GEOMETRY. (vease sdo_geom as_[format]) "rowfactory": función rowfactory para crear las filas devueltas por el cursor "input_handler": función input_handler para el cursor (vease sdo_geom.get_sdo_input_handler())
  • }
Returns:

object (instancia que debería ser o heredar de las clases row_cursor o row_table)

def rows_sql(self, sql_str, *args_sql):
1434    def rows_sql(self, sql_str, *args_sql):
1435        """
1436        Vease funcion 'generator_rows_sql()'
1437        Args:
1438            sql_str:
1439            *args_sql:
1440
1441        Returns:
1442            list
1443        """
1444        return list(self.generator_rows_sql(sql_str, *args_sql))

Vease funcion 'generator_rows_sql()'

Arguments:
  • sql_str:
  • *args_sql:
Returns:

list

def get_primary_key_table(self, nom_tab_or_view):
1446    def get_primary_key_table(self, nom_tab_or_view):
1447        """
1448        Retorna lista con las columnas que conforman la primary key de una tabla/vista
1449        Args:
1450            nom_tab_or_view:
1451
1452        Returns:
1453            list con campos clave
1454        """
1455        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

@print_to_log_exception(lanzar_exc=True)
def get_dd_table(self, nom_tab_or_view):
1457    @print_to_log_exception(lanzar_exc=True)
1458    def get_dd_table(self, nom_tab_or_view):
1459        """
1460        Retorna instancia row_table con los tipos para cada columna
1461        Args:
1462            nom_tab_or_view:
1463
1464        Returns:
1465            object de clase row_table con los tipos de cada columna como valores
1466        """
1467        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

def generator_rows_table( self, nom_tab_or_view, filter_sql=None, *args_filter_sql, **extra_params):
1469    def generator_rows_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql, **extra_params):
1470        """
1471        Retorna los registros que cumplan con el FILTER_SQL sobre la tabla o vista indicada.
1472
1473        La tabla se puede referenciar en el filtro con el alias 'TAB'
1474
1475        Si el FILTER_SQL utiliza valores por binding (:1, :2,...) estos se indicaran por orden en ARGS_FILTER_SQL
1476
1477        Args:
1478            nom_tab_or_view: Nombre de la tabla o vista sobre la que se hará la consulta
1479            filter_sql: Filtre sobre la taula
1480            args_filter_sql: Valors en ordre de binding per passar
1481            extra_params: Vease generator_rows_sql()
1482
1483        Yields:
1484             row_table: regs. clase row_table (mirar get_row_class_tab())
1485        """
1486        for reg in self.generator_rows_sql(sql_tab(nom_tab_or_view,
1487                                                   filter_sql),
1488                                           *args_filter_sql,
1489                                           row_class=extra_params.pop(
1490                                               "row_class",
1491                                               get_row_class_tab(self.con_db, nom_tab_or_view)),
1492                                           **extra_params):
1493            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())

def generator_rows_interact_geom(self, nom_tab, a_sdo_geom, cols_geom=None, geom_format=None):
1495    def generator_rows_interact_geom(self, nom_tab, a_sdo_geom, cols_geom=None, geom_format=None):
1496        """
1497        Retorna las filas de una tabla que interactuan con una geometria
1498
1499        Args:
1500            nom_tab: nombre de la tabla
1501            a_sdo_geom: geometria clase sdo_geom
1502            cols_geom (default=None): Lista con nombre de columnas geométricas sobre las que se quiere aplicar filtro
1503            geom_format:
1504        Yields:
1505            row_table: regs. clase row_table (mirar get_row_class_tab())
1506        """
1507        # Uso de " <>'FALSE'" por fallo de Oracle usando "= 'TRUE'"
1508        filter_interact_base = "SDO_ANYINTERACT({camp_geom}, :1) <> 'FALSE'"
1509
1510        if not cols_geom:
1511            cols_geom = get_tips_geom_tab(self.con_db, nom_tab).keys()
1512
1513        if cols_geom:
1514            filter_sql = " OR ".join([filter_interact_base.format(camp_geom=ng) for ng in cols_geom])
1515            for reg in self.generator_rows_table(nom_tab, filter_sql, a_sdo_geom.as_ora_sdo_geometry(),
1516                                                 geom_format=geom_format):
1517                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())

def rows_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql):
1519    def rows_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql):
1520        """
1521        Vease funcion 'generator_rows_table()'
1522
1523        Args:
1524            nom_tab_or_view:
1525            filter_sql:
1526            *args_filter_sql:
1527
1528        Returns:
1529            dict
1530        """
1531        gen_tab = self.generator_rows_table(nom_tab_or_view,
1532                                            filter_sql,
1533                                            *args_filter_sql)
1534        pk_tab = self.get_primary_key_table(nom_tab_or_view)
1535        l_pk = len(pk_tab)
1536        if l_pk == 0:
1537            return [r for r in gen_tab]
1538        else:
1539            def f_key(r):
1540                return getattr(r, pk_tab[0])
1541
1542            if l_pk > 1:
1543                def f_key(r): return tuple(map(lambda nf: getattr(r, nf), pk_tab))
1544            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

def row_table( self, nom_tab_or_view, filter_sql=None, *args_filter_sql, **extra_params):
1546    def row_table(self, nom_tab_or_view, filter_sql=None, *args_filter_sql, **extra_params):
1547        """
1548        Retorna primer registro para el FILTER_SQL sobre la tabla o vista indicada
1549        Si el FILTER_SQL utiliza valores por binding (:1, :2,...) estos se indicaran por orden en ARGS_FILTER_SQL
1550
1551        Args:
1552            nom_tab_or_view:
1553            filter_sql:
1554            *args_filter_sql:
1555            **extra_params:
1556
1557        Returns:
1558            object de la clase row_table o especificada en **extra_params['row_class']
1559        """
1560        gen = self.generator_rows_table(nom_tab_or_view,
1561                                        filter_sql,
1562                                        *args_filter_sql,
1563                                        **extra_params)
1564        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']

def row_table_at(self, nom_tab_or_view, *vals_key):
1566    def row_table_at(self, nom_tab_or_view, *vals_key):
1567        """
1568        Devuelve row_tabla_class para el registro que de la tabla_vista que cumpla con la clave
1569
1570        Args:
1571            nom_tab_or_view:
1572            *vals_key:
1573
1574        Returns:
1575            object de la clase row_table o especificada en **extra_params['row_class']
1576        """
1577        return self.exist_row_tab(nom_tab_or_view,
1578                                  {nc: val for nc, val in zip(self.get_primary_key_table(nom_tab_or_view),
1579                                                              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']

def test_row_table(self, row_tab, a_sql, *args_sql):
1581    def test_row_table(self, row_tab, a_sql, *args_sql):
1582        """
1583        Testea un registro de tabla (clase rwo_table) cumpla con sql indicado
1584
1585        Args:
1586            row_tab: registro de tabla en forma de clase row_table
1587            a_sql: string con sql a testear
1588
1589        Returns:
1590            bool: True o False según cumpla con el SQL indicado
1591        """
1592        sql_pk = dict_as_sql_bind_and_params(row_tab.pk_vals())
1593        query_sql = "{} AND ({})".format(sql_pk[0], a_sql)
1594
1595        ret = False
1596        if self.row_table(row_tab.nom_tab, query_sql, *(tuple(sql_pk[1]) + tuple(args_sql))):
1597            ret = True
1598
1599        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

def insert_row_tab( self, nom_tab, dict_vals_param=None, dict_vals_str=None, pasar_nulls=False):
1601    def insert_row_tab(self, nom_tab, dict_vals_param=None, dict_vals_str=None, pasar_nulls=False):
1602        """
1603        Inserta registro en la tabla indicada. Los valores para cada columna se pasarán a través de dict_vals_param
1604        como bindings o a través de dict_vals_str directemente en el string del sql ejecutado
1605        Args:
1606            nom_tab: nombre de la tabla
1607            dict_vals_param: diccionario indexado por columnas-valores. Los valores se pasarán como bindings
1608            dict_vals_str: diccionario indexado por columnas-valores a asignar como strings. El valor se pasa
1609                        directamente como asignacion en la senetencia sql
1610            pasar_nulls: (opcional) por defecto False. Indica si los valores NULL se asignarán o no
1611
1612        Returns:
1613            row_table (si genera el registro) o False si va mal la operación
1614        """
1615        if not dict_vals_param:
1616            dict_vals_param = {}
1617        if not dict_vals_str:
1618            dict_vals_str = {}
1619
1620        ora_dict_vals_param = self.get_vals_tab_for_transdb(nom_tab, dict_vals_param, pasar_nulls=pasar_nulls)
1621
1622        if pasar_nulls:
1623            # Se revisa que en un campo geometrico se asigne None y por lo tanto se asigne valor via STR
1624            geoms_null = [ng for ng, val in ora_dict_vals_param.items()
1625                          if not val and self.get_tip_camp_geom(nom_tab, ng)]
1626            if geoms_null:
1627                keys_str = [nc.upper() for nc in dict_vals_str.keys()]
1628                for gn in geoms_null:
1629                    ora_dict_vals_param.pop(gn)
1630                    if gn.upper() not in keys_str:
1631                        dict_vals_str[gn.upper()] = "NULL"
1632
1633        params = []
1634        nom_camps = []
1635        vals_camps = []
1636        for nom_camp, val_camp in ora_dict_vals_param.items():
1637            if val_camp is None:
1638                continue
1639            nom_camps.append(nom_camp)
1640            vals_camps.append(":" + nom_camp)
1641            params.append(val_camp)
1642
1643        for nom_camp, val_camp_str in dict_vals_str.items():
1644            if val_camp_str is None:
1645                continue
1646            nom_camps.append(nom_camp)
1647            vals_camps.append(str(val_camp_str))
1648
1649        row_desc_tab = get_row_desc_tab(self.con_db, nom_tab)
1650        pk_binds = {k: new_cursor(self.con_db).var(ora_tip_camp) for k, ora_tip_camp in row_desc_tab.pk_vals().items()}
1651        if not pk_binds:
1652            pk_binds = {'ROWID': new_cursor(self.con_db).var(cx_Oracle.ROWID)}
1653        str_pk_camps = ",".join(pk_binds.keys())
1654        str_pk_binds = ",".join(list(map(lambda x: ":ret_" + str(x), pk_binds.keys())))
1655        params += list(pk_binds.values())
1656
1657        a_sql_res = f"insert into {nom_tab}({','.join(nom_camps)}) values({','.join(vals_camps)}) " \
1658                    f"returning {str_pk_camps} into {str_pk_binds}"
1659
1660        ok = self.exec_trans_db(a_sql_res, *params)
1661        if ok:
1662            pk_vals = {k: curs_var.getvalue(0)[0] for k, curs_var in pk_binds.items()}
1663            return self.exist_row_tab(nom_tab, pk_vals)
1664
1665        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

def update_row_tab( self, nom_tab, dict_clau_reg, dict_vals_param=None, dict_vals_str=None, pasar_nulls=None):
1667    def update_row_tab(self, nom_tab, dict_clau_reg, dict_vals_param=None, dict_vals_str=None, pasar_nulls=None):
1668        """
1669        Actualiza registro en la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor}
1670        Los valores para cada columna se pasarán a través de dict_vals_param como bindings o a través de
1671        dict_vals_str directemente en el string del sql ejecutado
1672
1673        Args:
1674            nom_tab: nombre de la tabla
1675            dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar
1676            dict_vals_param: diccionario indexado por columnas-valores. Los valores se pasarán como bindings
1677            dict_vals_str: diccionario indexado por columnas-valores a asignar como strings. El valor se pasa
1678                        directamente como asignacion en la senetencia sql
1679            pasar_nulls: (opcional) por defecto False. Indica si los valores NULL se asignarán o no
1680
1681        Returns:
1682            row_table (si genera el registro) o False si va mal la operación
1683        """
1684        if not dict_vals_param:
1685            dict_vals_param = {}
1686        if not dict_vals_str:
1687            dict_vals_str = {}
1688
1689        ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg)
1690        ora_dict_vals_param = self.get_vals_tab_for_transdb(nom_tab, dict_vals_param, pasar_nulls=pasar_nulls)
1691
1692        if pasar_nulls:
1693            # Se revisa que en un campo geometrico se asigne None y por lo tanto se asigne valor via STR
1694            geoms_null = [ng for ng, val in ora_dict_vals_param.items()
1695                          if not val and self.get_tip_camp_geom(nom_tab, ng)]
1696            if geoms_null:
1697                keys_str = [nc.upper() for nc in dict_vals_str.keys()]
1698                for gn in geoms_null:
1699                    ora_dict_vals_param.pop(gn)
1700                    if gn.upper() not in keys_str:
1701                        dict_vals_str[gn.upper()] = "NULL"
1702
1703        (sql_set_camps, params_set_camps) = dict_as_sql_bind_and_params(ora_dict_vals_param,
1704                                                                        ",", "=")
1705
1706        (query_clau, params_filter) = dict_as_sql_bind_and_params(ora_dict_clau_reg)
1707
1708        params = params_set_camps + params_filter
1709
1710        for nom_camp, val_camp_str in dict_vals_str.items():
1711            if sql_set_camps:
1712                sql_set_camps += " , "
1713            sql_set_camps += nom_camp + "=" + val_camp_str
1714
1715        ok = None
1716        if sql_set_camps:
1717            a_sql_res = f"update {nom_tab} set {sql_set_camps} where {query_clau}"
1718
1719            ok = self.exec_trans_db(a_sql_res, *params)
1720
1721            if ok:
1722                return self.exist_row_tab(nom_tab, dict_clau_reg)
1723
1724        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

def remove_row_tab(self, nom_tab, dict_clau_reg):
1726    def remove_row_tab(self, nom_tab, dict_clau_reg):
1727        """
1728        Borra registro en la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor}
1729
1730        Args:
1731            nom_tab: nombre de la tabla
1732            dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar
1733
1734        Returns:
1735            bool según vaya la operación
1736        """
1737        ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg)
1738
1739        a_sql_tmpl = "delete {nom_tab} where {query_clau}"
1740
1741        (sql_filter, params) = dict_as_sql_bind_and_params(ora_dict_clau_reg)
1742
1743        a_sql_res = a_sql_tmpl.format(nom_tab=nom_tab,
1744                                      query_clau=sql_filter)
1745
1746        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

def exist_row_tab(self, nom_tab, dict_clau_reg, **extra_params):
1748    def exist_row_tab(self, nom_tab, dict_clau_reg, **extra_params):
1749        """
1750        Devuelve registro de la tabla indicada que cunpla con la clave pasada por dict_clau_reg {clave:valor}
1751
1752        Args:
1753            nom_tab: nombre de la tabla
1754            dict_clau_reg: diccionario indexado por clave-valor del registro a actualizar
1755            **extra_params:
1756
1757        Returns:
1758            object de la clase row_table o especificada en **extra_params['row_class']
1759        """
1760        ora_dict_clau_reg = self.get_vals_tab_for_transdb(nom_tab, dict_clau_reg)
1761
1762        (filter_sql, params) = dict_as_sql_bind_and_params(ora_dict_clau_reg, "and", "=")
1763
1764        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']

def get_vals_tab_for_transdb(self, nom_tab, dict_camps_vals, pasar_nulls=True):
1766    def get_vals_tab_for_transdb(self, nom_tab, dict_camps_vals, pasar_nulls=True):
1767        """
1768        Para un tabla y diccionario columnas-valores devuelve diccionario indexado por las columnas con los valores
1769        convertidos a formato cx_Oracle según tipo de cada columna en Oracle
1770        Args:
1771            nom_tab: nombre de la tabla
1772            dict_camps_vals: diccionario indexado por columnas-valor a convertir a formato cx_Oracle
1773            pasar_nulls: (opcional) por defecto convertirá los None a NULL de Oracle
1774
1775        Returns:
1776            dict indexado por columnas con los valores convertidos a tipo cx_Oracle
1777        """
1778        dd_tab = get_row_desc_tab(self.con_db, nom_tab)
1779
1780        # Retorna dict con los campos a pasar por parametro
1781        d_params = {}
1782
1783        # Los nombres de campo siempre se buscarán en mayúsculas
1784        dict_camps_vals = {k.upper(): v for k, v in dict_camps_vals.items()}
1785
1786        for camp, tip_camp in dd_tab.vals().items():
1787            if camp not in dict_camps_vals:
1788                continue
1789
1790            val_camp = dict_camps_vals.get(camp)
1791            if not pasar_nulls and val_camp is None:
1792                continue
1793
1794            if isinstance(val_camp, m_sdo_geom.sdo_geom):
1795                var = val_camp.as_ora_sdo_geometry()
1796            else:
1797                try:
1798                    var = new_cursor(self.con_db).var(tip_camp)
1799                    var.setvalue(0, val_camp)
1800                except:
1801                    var = val_camp
1802
1803            d_params[camp] = var
1804
1805        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

@print_to_log_exception()
def run_sql_script(self, filename):
1807    @print_to_log_exception()
1808    def run_sql_script(self, filename):
1809        """
1810        Ejecuta slq script (filename) sobre SQLPLUS
1811
1812        Args:
1813            filename: path del sql script
1814
1815        Returns:
1816
1817        """
1818        user_ora = self.con_db.username
1819        ds_ora = self.con_db.dsn
1820        nom_con = self.nom_con_db
1821        psw_ora = self.__psw_con_db__
1822        if psw_ora is None:
1823            print("ERROR - Conexión '" + nom_con + "' no está añadida al gestor!!")
1824            return
1825
1826        with open(filename, 'rb') as a_file:
1827            a_sql_command = a_file.read()
1828
1829        con_db_str = user_ora + "/" + psw_ora + "@" + ds_ora
1830        sqlplus = Popen(['sqlplus', '-S', con_db_str], stdin=PIPE, stdout=PIPE, stderr=PIPE)
1831        # sqlplus.stdin.write(a_sql_command)
1832
1833        (stdout, stderr) = sqlplus.communicate(a_sql_command)
1834
1835        self.print_log("Resultado lanzar script '{}': \n"
1836                       "{}".format(filename,
1837                                   stdout.decode("utf-8")))
1838
1839        if sqlplus is not None:
1840            sqlplus.terminate()

Ejecuta slq script (filename) sobre SQLPLUS

Arguments:
  • filename: path del sql script

Returns:

@staticmethod
def get_nom_obj_sql(nom_base, prefix='', sufix=''):
1842    @staticmethod
1843    def get_nom_obj_sql(nom_base, prefix="", sufix=""):
1844        """
1845        Retorna nombre propuesto con prefijo/sufijos formateado para que cumpla longitud máxima de 32 caracteres en
1846        objetos sql Oracle
1847
1848        Args:
1849            nom_base: nombre propuesto
1850            prefix: (opc) prefijo
1851            sufix: (opc) sufijo
1852
1853        Returns:
1854            str formateado
1855        """
1856        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

def iter_sdo_gtypes_vals_camp_tab(self, nom_taula, nom_camp):
1858    def iter_sdo_gtypes_vals_camp_tab(self, nom_taula, nom_camp):
1859        """
1860        Retorna los distintos tipos de Geometria (codigo entero que define el tipo SDO_GTYPE)
1861        que se encuentran dentro de la columna sdo_geometry de una tabla
1862
1863        Args:
1864            nom_taula: nombre de la tabla
1865            nom_camp: nombre campo geometrico
1866
1867        Returns:
1868            int definiendo tipo de geometría
1869        """
1870        sql_tip_geoms = f"select distinct(tab.{nom_camp}.Get_GType()) as tip_geom from {nom_taula} tab " \
1871                        f"where {nom_camp} is not null"
1872
1873        for reg in self.generator_rows_sql(sql_tip_geoms):
1874            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

def iter_distinct_vals_camp_tab(self, nom_taula, nom_camp, filter_sql=None):
1876    def iter_distinct_vals_camp_tab(self, nom_taula, nom_camp, filter_sql=None):
1877        """
1878        Retorna los distintos valores de la columna de una tabla
1879
1880        Args:
1881            nom_taula (str): Nombre de tabla
1882            nom_camp (str): Nombre de campo
1883            filter_sql(str): Filtro SQL sobre la tabla indicada
1884
1885        Returns:
1886            {str}: Itera los distintos valores encontrados en el campo indicado
1887        """
1888        sql_distinct_vals = f"select distinct(tab.{nom_camp}) as VAL from {nom_taula} tab"
1889        if filter_sql:
1890            sql_distinct_vals += " where " + filter_sql
1891
1892        for reg in self.generator_rows_sql(sql_distinct_vals):
1893            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

def get_tip_camp_geom(self, nom_tab_or_view, nom_camp_geom):
1895    def get_tip_camp_geom(self, nom_tab_or_view, nom_camp_geom):
1896        """
1897        Retorna el tipo de campo geométrico (class_tip_geom) registrados en la global __class_tips_geom_ora
1898        para el campo indicado
1899
1900        Args:
1901            nom_tab_or_view: nombre tabla/vista
1902            nom_camp_geom: nombre campo geom
1903
1904        Returns:
1905            namedtuple: con atributos ['TABLE_NAME', 'COLUMN_NAME', 'GTYPE', 'SRID'])
1906        """
1907        tips_geom_tab = get_tips_geom_tab(self.con_db, nom_tab_or_view)
1908        if tips_geom_tab:
1909            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'])

def get_epsg_for_srid(self, srid):
1911    def get_epsg_for_srid(self, srid):
1912        """
1913        Rertorna WKT con la definicion del SRID dado
1914        """
1915        return self.callfunc_sql('SDO_CS.MAP_ORACLE_SRID_TO_EPSG', cx_Oracle.NUMBER, srid)

Rertorna WKT con la definicion del SRID dado

def get_gtype_camp_geom(self, nom_tab_or_view, nom_camp_geom):
1917    def get_gtype_camp_geom(self, nom_tab_or_view, nom_camp_geom):
1918        """
1919        Retorna el tipo GTYPE (int) de la geometria
1920
1921        Args:
1922            nom_tab_or_view: nombre tabla/vista
1923            nom_camp_geom: nombre campo geom
1924
1925        Returns:
1926            int
1927        """
1928        gtype = 0
1929        g_tip_ora = self.get_tip_camp_geom(nom_tab_or_view, nom_camp_geom)
1930        if g_tip_ora:
1931            gtype = GTYPES_ORA.index(g_tip_ora.GTYPE)
1932
1933        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

@staticmethod
def verificar_path_vector_file(nom_tab_or_view, dir, file_name, ext, zipped):
1935    @staticmethod
1936    def verificar_path_vector_file(nom_tab_or_view, dir, file_name, ext, zipped):
1937        """
1938        Compone el/los path para el/los vector_file de una tabla y determina si exists
1939        Args:
1940            nom_tab_or_view:
1941            dir:
1942            file_name:
1943            ext:
1944            zipped:
1945
1946        Returns:
1947            file_path (str), file_path_zip (str), exists (bool)
1948        """
1949        if file_name and not file_name.endswith(ext):
1950            file_name = ".".join((file_name, ext))
1951        elif not file_name:
1952            file_name = ".".join((nom_tab_or_view, ext)).lower()
1953
1954        file_path = os.path.join(dir, file_name)
1955        file_path_zip = None
1956        if zipped:
1957            file_path_zip = "{}.zip".format(os.path.splitext(file_path)[0])
1958
1959        exists = (os.path.exists(file_path) and not file_path_zip) or (file_path_zip and os.path.exists(file_path_zip))
1960
1961        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)

@print_to_log_exception()
def create_json_tab_or_view( self, nom_tab_or_view, dir='.', file_name=None, overwrite=True, cols=None, zipped=True, filter_sql=None, *args_sql):
1963    @print_to_log_exception()
1964    def create_json_tab_or_view(self, nom_tab_or_view, dir='.', file_name=None, overwrite=True, cols=None, zipped=True,
1965                                filter_sql=None, *args_sql):
1966        """
1967
1968        Args:
1969            nom_tab_or_view (str): Nombre tabla o vista
1970            dir (str):
1971            file_name (str):
1972            overwrite (bool):
1973            cols (list): columnas
1974            zipped (bool):
1975            filter_sql (str):
1976            *args_sql: lista de argumentos a pasar al filtro sql
1977        Returns:
1978            file_path (str)
1979        """
1980        file_path, file_path_zip, exists = self.verificar_path_vector_file(
1981            nom_tab_or_view, dir, file_name, "json", zipped)
1982
1983        if overwrite or not exists:
1984            # Se calculan las columnas para hacer get de la fila con el orden en las columnas de la tabla
1985            if not cols:
1986                dd_tab = self.get_dd_table(nom_tab_or_view)
1987                cols = dd_tab.cols
1988            sql = sql_tab(nom_tab_or_view,
1989                          filter_sql=filter_sql,
1990                          columns=cols)
1991
1992            file_path_res = vector_file_from_gen_ora_sql(file_path, "json", self.generator_rows_sql(sql, *args_sql),
1993                                                         zipped=zipped)
1994        else:
1995            file_path_res = file_path if not zipped else file_path_zip
1996
1997        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)

@print_to_log_exception()
def create_csv_tab_or_view( self, nom_tab_or_view, dir='.', file_name=None, overwrite=True, cols=None, zipped=True, filter_sql=None, *args_sql):
1999    @print_to_log_exception()
2000    def create_csv_tab_or_view(self, nom_tab_or_view, dir='.', file_name=None, overwrite=True, cols=None, zipped=True,
2001                               filter_sql=None, *args_sql):
2002        """
2003
2004        Args:
2005            nom_tab_or_view (str): Nombre tabla o vista
2006            dir (str):
2007            file_name (str):
2008            overwrite (bool):
2009            cols (list): columnas
2010            zipped (bool):
2011            filter_sql (str):
2012            *args_sql: lista de argumentos a pasar al filtro sql
2013
2014        Returns:
2015            file_path_res (str)
2016        """
2017        file_path, file_path_zip, exists = self.verificar_path_vector_file(
2018            nom_tab_or_view, dir, file_name, "csv", zipped)
2019
2020        if overwrite or not exists:
2021            if not cols:
2022                dd_tab = self.get_dd_table(nom_tab_or_view)
2023                cols = dd_tab.cols
2024            sql = sql_tab(nom_tab_or_view,
2025                          filter_sql=filter_sql,
2026                          columns=cols)
2027
2028            # Para el formato geocsv que acepta GDAL se añade fichero con los tipos de columna
2029            tip_cols_csv = []
2030            for col in cols:
2031                r_tip_col = self.row_table("user_tab_columns",
2032                                           "table_name = :1 and column_name = :2",
2033                                           nom_tab_or_view.upper(), col.upper())
2034                dtype = r_tip_col.DATA_TYPE
2035                dlength = r_tip_col.DATA_LENGTH
2036                dprecision = r_tip_col.DATA_PRECISION
2037                dscale = r_tip_col.DATA_SCALE
2038
2039                if dtype == "DATE":
2040                    tip_cols_csv.append('"DateTime"')
2041                elif dtype == "FLOAT":
2042                    tip_cols_csv.append('"Real({}.{})"'.format(dlength, dprecision))
2043                elif dtype == "NUMBER":
2044                    if dscale and dscale != 0:
2045                        tip_cols_csv.append('"Real({}.{})"'.format(dprecision, dscale))
2046                    elif dprecision:
2047                        tip_cols_csv.append('"Integer({})"'.format(dprecision))
2048                    else:
2049                        tip_cols_csv.append('"Real(10.8)"')
2050                elif dtype == "SDO_GEOMETRY":
2051                    tip_cols_csv.append('"WKT"')
2052                else:
2053                    tip_cols_csv.append('"String({})"'.format(round(dlength * 1.25)))
2054
2055            file_path_res = vector_file_from_gen_ora_sql(file_path, "csv",
2056                                                         self.generator_rows_sql(sql, *args_sql, geom_format="as_wkt"),
2057                                                         zipped=zipped, cols_csv=cols, tip_cols_csv=tip_cols_csv)
2058        else:
2059            file_path_res = file_path if not zipped else file_path_zip
2060
2061        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)

@print_to_log_exception()
def create_geojsons_tab_or_view( self, nom_tab_or_view, dir='.', file_name_prefix=None, by_geom=False, dir_topojson=None, overwrite=True, cols=None, filter_sql=None, *args_sql):
2063    @print_to_log_exception()
2064    def create_geojsons_tab_or_view(self, nom_tab_or_view, dir='.', file_name_prefix=None, by_geom=False,
2065                                    dir_topojson=None, overwrite=True, cols=None,
2066                                    filter_sql=None, *args_sql):
2067        """
2068
2069        Args:
2070            nom_tab_or_view (str): Nombre tabla (vigente o versionada) para entidad GIS
2071            dir (str="."):
2072            file_name_prefix (str=None): (opcional) prefijo del fichero
2073            by_geom (bool=False): (Opcional) si se querrán los geojsons por geometria. Si no se saca un unico geojson con
2074                            la columna geometry como una GeometryCollection si la tabla es multigeom
2075            dir_topojson (str=None): path donde irán las conversiones
2076            overwrite (bool=True):
2077            cols (list=None):
2078            filter_sql (str=None):
2079            *args_sql: lista de argumentos a pasar al filtro sql
2080
2081        Returns:
2082            ok (bool)
2083        """
2084        ext = "geo.json"
2085        sqls = {}
2086        dd_tab = self.get_dd_table(nom_tab_or_view)
2087        if not cols:
2088            cols = dd_tab.cols
2089
2090        if by_geom:
2091            c_alfas = [cn for cn in dd_tab.alfas() if cn in cols]
2092            c_geoms = [cn for cn in dd_tab.geoms() if cn in cols]
2093            for c_geom in c_geoms:
2094                sqls[c_geom] = sql_tab(nom_tab_or_view, filter_sql, c_alfas + [c_geom])
2095        else:
2096            sqls[None] = sql_tab(nom_tab_or_view, filter_sql, cols)
2097
2098        if not file_name_prefix:
2099            file_name_prefix = nom_tab_or_view
2100
2101        for ng, sql in sqls.items():
2102            file_name = file_name_prefix
2103            if ng:
2104                file_name = "-".join((file_name, ng))
2105
2106            file_name = ".".join((file_name, ext)).lower()
2107            file_path = os.path.join(dir, file_name)
2108
2109            if overwrite or not os.path.exists(file_path):
2110                file_path = vector_file_from_gen_ora_sql(file_path, "geojson",
2111                                                         self.generator_rows_sql(sql, *args_sql))
2112
2113            if by_geom and dir_topojson and file_path:
2114                tip_geom = getattr(dd_tab, ng).GTYPE
2115                simplify = True
2116                if tip_geom.endswith("POINT"):
2117                    simplify = False
2118
2119                topojson_utils.geojson_to_topojson(file_path, dir_topojson,
2120                                                   simplify=simplify,
2121                                                   overwrite=overwrite)
2122
2123        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)

nom_con_db