Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
__init__.py702 linesDownload Raw Back to erd
1##########################################################################2#3# pgAdmin 4 - PostgreSQL Tools4#5# Copyright (C) 2013 - 2024, The pgAdmin Development Team6# This software is released under the PostgreSQL Licence7#8##########################################################################9 10"""A blueprint module implementing the erd tool."""11import json12 13from flask import url_for, request, Response14from flask import render_template, current_app as app15from pgadmin.user_login_check import pga_login_required16from flask_babel import gettext17from werkzeug.user_agent import UserAgent18from pgadmin.utils import PgAdminModule, \19    SHORTCUT_FIELDS as shortcut_fields20from pgadmin.utils.ajax import make_json_response, bad_request, \21    internal_server_error22from pgadmin.model import Server23from config import PG_DEFAULT_DRIVER24from pgadmin.utils.driver import get_driver25from pgadmin.browser.utils import underscore_unescape26from pgadmin.browser.server_groups.servers.databases.schemas.utils \27    import get_schemas28from pgadmin.browser.server_groups.servers.databases.schemas.tables. \29    constraints.foreign_key import utils as fkey_utils30from pgadmin.utils.constants import PREF_LABEL_KEYBOARD_SHORTCUTS, \31    PREF_LABEL_DISPLAY, PREF_LABEL_OPTIONS32from .utils import ERDHelper33from pgadmin.utils.exception import ConnectionLost34from pgadmin.authenticate import socket_login_required35from ... import socketio36 37MODULE_NAME = 'erd'38SOCKETIO_NAMESPACE = '/{0}'.format(MODULE_NAME)39 40 41class ERDModule(PgAdminModule):42    """43    class ERDModule(PgAdminModule)44 45        A module class for ERD derived from PgAdminModule.46    """47 48    LABEL = gettext("ERD tool")49 50    def get_own_menuitems(self):51        return {}52 53    def get_exposed_url_endpoints(self):54        """55        Returns:56            list: URL endpoints57        """58        return [59            'erd.panel',60            'erd.initialize',61            'erd.prequisite',62            'erd.sql',63            'erd.close'64        ]65 66    def register_preferences(self):67        self.preference.register(68            'keyboard_shortcuts',69            'open_project',70            gettext('Open project'),71            'keyboardshortcut',72            {73                'alt': False,74                'shift': False,75                'control': True,76                'key': {77                    'key_code': 79,78                    'char': 'o'79                }80            },81            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,82            fields=shortcut_fields83        )84 85        self.preference.register(86            'keyboard_shortcuts',87            'save_project',88            gettext('Save project'),89            'keyboardshortcut',90            {91                'alt': False,92                'shift': False,93                'control': True,94                'key': {95                    'key_code': 83,96                    'char': 's'97                }98            },99            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,100            fields=shortcut_fields101        )102 103        self.preference.register(104            'keyboard_shortcuts',105            'save_project_as',106            gettext('Save project as'),107            'keyboardshortcut',108            {109                'alt': False,110                'shift': True,111                'control': True,112                'key': {113                    'key_code': 83,114                    'char': 's'115                }116            },117            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,118            fields=shortcut_fields119        )120 121        self.preference.register(122            'keyboard_shortcuts',123            'generate_sql',124            gettext('Generate SQL'),125            'keyboardshortcut',126            {127                'alt': True,128                'shift': False,129                'control': True,130                'key': {131                    'key_code': 83,132                    'char': 's'133                }134            },135            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,136            fields=shortcut_fields137        )138 139        self.preference.register(140            'keyboard_shortcuts',141            'download_image',142            gettext('Download image'),143            'keyboardshortcut',144            {145                'alt': True,146                'shift': False,147                'control': True,148                'key': {149                    'key_code': 73,150                    'char': 'i'151                }152            },153            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,154            fields=shortcut_fields155        )156 157        self.preference.register(158            'keyboard_shortcuts',159            'add_table',160            gettext('Add table'),161            'keyboardshortcut',162            {163                'alt': True,164                'shift': False,165                'control': True,166                'key': {167                    'key_code': 65,168                    'char': 'a'169                }170            },171            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,172            fields=shortcut_fields173        )174 175        self.preference.register(176            'keyboard_shortcuts',177            'edit_table',178            gettext('Edit table'),179            'keyboardshortcut',180            {181                'alt': True,182                'shift': False,183                'control': True,184                'key': {185                    'key_code': 69,186                    'char': 'e'187                }188            },189            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,190            fields=shortcut_fields191        )192 193        self.preference.register(194            'keyboard_shortcuts',195            'clone_table',196            gettext('Clone table'),197            'keyboardshortcut',198            {199                'alt': True,200                'shift': False,201                'control': True,202                'key': {203                    'key_code': 67,204                    'char': 'c'205                }206            },207            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,208            fields=shortcut_fields209        )210 211        self.preference.register(212            'keyboard_shortcuts',213            'drop_table',214            gettext('Drop table'),215            'keyboardshortcut',216            {217                'alt': True,218                'shift': False,219                'control': True,220                'key': {221                    'key_code': 68,222                    'char': 'd'223                }224            },225            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,226            fields=shortcut_fields227        )228 229        self.preference.register(230            'keyboard_shortcuts',231            'add_edit_note',232            gettext('Add/Edit note'),233            'keyboardshortcut',234            {235                'alt': True,236                'shift': False,237                'control': True,238                'key': {239                    'key_code': 78,240                    'char': 'n'241                }242            },243            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,244            fields=shortcut_fields245        )246 247        self.preference.register(248            'keyboard_shortcuts',249            'one_to_many',250            gettext('One to many link'),251            'keyboardshortcut',252            {253                'alt': True,254                'shift': False,255                'control': True,256                'key': {257                    'key_code': 79,258                    'char': 'o'259                }260            },261            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,262            fields=shortcut_fields263        )264 265        self.preference.register(266            'keyboard_shortcuts',267            'many_to_many',268            gettext('Many to many link'),269            'keyboardshortcut',270            {271                'alt': True,272                'shift': False,273                'control': True,274                'key': {275                    'key_code': 77,276                    'char': 'm'277                }278            },279            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,280            fields=shortcut_fields281        )282 283        self.preference.register(284            'keyboard_shortcuts',285            'auto_align',286            gettext('Auto align'),287            'keyboardshortcut',288            {289                'alt': True,290                'shift': False,291                'control': True,292                'key': {293                    'key_code': 76,294                    'char': 'l'295                }296            },297            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,298            fields=shortcut_fields299        )300 301        self.preference.register(302            'keyboard_shortcuts',303            'show_details',304            gettext('Show more/fewer details'),305            'keyboardshortcut',306            {307                'alt': True,308                'shift': False,309                'control': True,310                'key': {311                    'key_code': 84,312                    'char': 't'313                }314            },315            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,316            fields=shortcut_fields317        )318 319        self.preference.register(320            'keyboard_shortcuts',321            'zoom_to_fit',322            gettext('Zoom to fit'),323            'keyboardshortcut',324            {325                'alt': True,326                'shift': True,327                'control': False,328                'key': {329                    'key_code': 70,330                    'char': 'f'331                }332            },333            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,334            fields=shortcut_fields335        )336 337        self.preference.register(338            'keyboard_shortcuts',339            'zoom_in',340            gettext('Zoom in'),341            'keyboardshortcut',342            {343                'alt': True,344                'shift': True,345                'control': False,346                'key': {347                    'key_code': 187,348                    'char': '+'349                }350            },351            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,352            fields=shortcut_fields353        )354 355        self.preference.register(356            'keyboard_shortcuts',357            'zoom_out',358            gettext('Zoom out'),359            'keyboardshortcut',360            {361                'alt': True,362                'shift': True,363                'control': False,364                'key': {365                    'key_code': 189,366                    'char': '-'367                }368            },369            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,370            fields=shortcut_fields371        )372 373        self.preference.register(374            'options',375            'sql_with_drop',376            gettext('SQL With DROP Table'),377            'boolean',378            False,379            category_label=PREF_LABEL_OPTIONS,380            help_str=gettext(381                'If enabled, the SQL generated by the ERD Tool will add '382                'DROP table DDL before each CREATE table DDL.'383            )384        )385 386        self.preference.register(387            'options',388            'table_relation_depth',389            gettext('Table Relation Depth'),390            'integer',391            -1,392            category_label=PREF_LABEL_OPTIONS,393            help_str=gettext(394                'The maximum depth pgAdmin should traverse to find '395                'related tables when generating an ERD for a table. '396                'Use -1 for no limit.'397            )398        )399 400        self.preference.register(401            'options', 'cardinality_notation',402            gettext('Cardinality Notation'), 'radioModern', 'crows',403            category_label=PREF_LABEL_OPTIONS, options=[404                {'label': gettext('Crow\'s foot'), 'value': 'crows'},405                {'label': gettext('Chen'), 'value': 'chen'},406            ],407            help_str=gettext(408                'Notation to be used to present cardinality.'409            )410        )411 412        self.preference.register(413            'options',414            'sql_with_drop',415            gettext('SQL With DROP Table'),416            'boolean',417            False,418            category_label=PREF_LABEL_OPTIONS,419            help_str=gettext(420                'If enabled, the SQL generated by the ERD Tool will add '421                'DROP table DDL before each CREATE table DDL.'422            )423        )424 425 426blueprint = ERDModule(MODULE_NAME, __name__, static_url_path='/static')427 428 429@blueprint.route(430    '/panel/<int:trans_id>',431    methods=["POST"],432    endpoint='panel'433)434@pga_login_required435def panel(trans_id):436    """437    This method calls index.html to render the erd tool.438 439    Args:440        panel_title: Title of the panel441    """442 443    params = {444        'trans_id': trans_id,445        'title': request.form['title']446    }447    if request.args:448        params.update({k: v for k, v in request.args.items()})449 450    if 'gen' in params:451        params['gen'] = True if params['gen'] == 'true' else False452 453    # We need client OS information to render correct Keyboard shortcuts454    user_agent = UserAgent(request.headers.get('User-Agent'))455 456    """457    Animations and transitions are not automatically GPU accelerated and by458    default use browser's slow rendering engine. We need to set 'translate3d'459    value of '-webkit-transform' property in order to use GPU. After applying460    this property under linux, Webkit calculates wrong position of the461    elements so panel contents are not visible. To make it work, we need to462    explicitly set '-webkit-transform' property to 'none' for .ajs-notifier,463    .ajs-message, .ajs-modal classes.464 465    This issue is only with linux runtime application and observed in Query466    tool and debugger. When we open 'Open File' dialog then whole Query tool467    panel content is not visible though it contains HTML element in back end.468 469    The port number should have already been set by the runtime if we're470    running in desktop mode.471    """472    is_linux_platform = False473 474    from sys import platform as _platform475    if "linux" in _platform:476        is_linux_platform = True477 478    s = Server.query.filter_by(id=int(params['sid'])).first()479 480    params.update({481        'bgcolor': s.bgcolor,482        'fgcolor': s.fgcolor,483        'client_platform': user_agent.platform,484        'is_desktop_mode': app.PGADMIN_RUNTIME,485        'is_linux': is_linux_platform486    })487 488    return render_template(489        "erd/index.html",490        title=underscore_unescape(params['title']),491        params=json.dumps(params),492    )493 494 495@blueprint.route(496    '/initialize/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',497    methods=["POST"], endpoint='initialize'498)499@pga_login_required500def initialize_erd(trans_id, sgid, sid, did):501    """502    This method is responsible for instantiating and initializing503    the erd tool object. It will also create a unique504    transaction id and store the information into session variable.505 506    Args:507        sgid: Server group Id508        sid: Server Id509        did: Database Id510    """511    # Read the data if present. Skipping read may cause connection512    # reset error if data is sent from the client513    if request.data:514        _ = request.data515 516    conn = _get_connection(sid, did, trans_id)517 518    return make_json_response(519        data={520            'connId': str(trans_id),521            'database': conn.db,522            'serverVersion': conn.manager.version,523        }524    )525 526 527def _get_connection(sid, did, trans_id):528    """529    Get the connection object of ERD.530    :param sid:531    :param did:532    :param trans_id:533    :return:534    """535    manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)536    try:537        conn = manager.connection(did=did, conn_id=trans_id,538                                  auto_reconnect=True,539                                  use_binary_placeholder=True)540        status, msg = conn.connect()541        if not status:542            app.logger.error(msg)543            raise ConnectionLost(sid, conn.db, trans_id)544 545        return conn546    except Exception as e:547        app.logger.error(e)548        raise549 550 551@blueprint.route('/prequisite/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',552                 methods=["GET"],553                 endpoint='prequisite')554@pga_login_required555def prequisite(trans_id, sgid, sid, did):556    conn = _get_connection(sid, did, trans_id)557    helper = ERDHelper(trans_id, sid, did)558    status, col_types = helper.get_types()559 560    if not status:561        return internal_server_error(errormsg=col_types)562 563    status, schemas = get_schemas(conn, show_system_objects=False)564 565    if not status:566        return internal_server_error(errormsg=schemas)567 568    return make_json_response(569        data={570            'col_types': col_types,571            'schemas': schemas['rows']572        },573        status=200574    )575 576 577def translate_foreign_keys(tab_fks, tab_data, all_nodes):578    """579    This function will take the from table foreign keys and translate580    it into non oid based format. It will allow creating FK sql even581    if table is not already created.582    :param tab_fks: Table foreign keyss583    :param tab_data: Table data584    :param all_nodes: All the nodes info from ERD585    :return: Translated foreign key data586    """587    for tab_fk in tab_fks:588        if 'columns' not in tab_fk:589            continue590        try:591            remote_table = all_nodes[tab_fk['columns'][0]['references']]592        except KeyError:593            continue594        tab_fk['schema'] = tab_data['schema']595        tab_fk['table'] = tab_data['name']596        tab_fk['remote_schema'] = remote_table['schema']597        tab_fk['remote_table'] = remote_table['name']598 599        new_column = {600            'local_column': tab_fk['columns'][0]['local_column'],601            'referenced': tab_fk['columns'][0]['referenced']602        }603        tab_fk['columns'][0] = new_column604 605    return tab_fks606 607 608@blueprint.route('/sql/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',609                 methods=["POST"],610                 endpoint='sql')611@pga_login_required612def sql(trans_id, sgid, sid, did):613    data = json.loads(request.data)614    with_drop = False615    if request.args and 'with_drop' in request.args:616        with_drop = True if request.args.get('with_drop') == 'true' else False617 618    helper = ERDHelper(trans_id, sid, did)619    conn = _get_connection(sid, did, trans_id)620 621    sql = ''622    tab_foreign_keys = []623    all_nodes = data.get('nodes', {})624 625    table_sql = ''626    for tab_key, tab_data in all_nodes.items():627        tab_fks = tab_data.pop('foreign_key', [])628        tab_foreign_keys.extend(translate_foreign_keys(tab_fks, tab_data,629                                                       all_nodes))630        table_sql += '\n\n' + helper.get_table_sql(tab_data,631                                                   with_drop=with_drop)632 633    if with_drop:634        for tab_fk in tab_foreign_keys:635            fk_sql = fkey_utils.get_delete_sql(conn, tab_fk)636            sql += '\n\n' + fk_sql637 638    if sql != '':639        sql += '\n\n'640 641    sql += table_sql642    for tab_fk in tab_foreign_keys:643        fk_sql, _ = fkey_utils.get_sql(conn, tab_fk, None)644        sql += '\n\n' + fk_sql645 646    return make_json_response(647        data=sql,648        status=200649    )650 651 652@socketio.on('connect', namespace=SOCKETIO_NAMESPACE)653def connect():654    """655    Connect to the server through socket.656    :return:657    :rtype:658    """659    socketio.emit('connected', {'sid': request.sid},660                  namespace=SOCKETIO_NAMESPACE,661                  to=request.sid)662 663 664@socketio.on('tables', namespace=SOCKETIO_NAMESPACE)665@socket_login_required666def tables(params):667    try:668        helper = ERDHelper(params['trans_id'], params['sid'], params['did'])669        _get_connection(params['sid'], params['did'], params['trans_id'])670 671        status, tables = helper.get_all_tables(params.get('scid', None),672                                               params.get('tid', None))673 674        if not status:675            tables = tables.json if isinstance(tables, Response) else tables676            socketio.emit('tables_failed', tables,677                          namespace=SOCKETIO_NAMESPACE,678                          to=request.sid)679            return680        socketio.emit('tables_success', tables, namespace=SOCKETIO_NAMESPACE,681                      to=request.sid)682    except Exception as e:683        socketio.emit('tables_failed', str(e), namespace=SOCKETIO_NAMESPACE,684                      to=request.sid)685 686 687@blueprint.route('/close/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',688                 methods=["DELETE"],689                 endpoint='close')690@pga_login_required691def close(trans_id, sgid, sid, did):692    manager = get_driver(693        PG_DEFAULT_DRIVER).connection_manager(sid)694    if manager is not None:695        conn = manager.connection(did=did, conn_id=trans_id)696 697        # Release the connection698        if conn.connected():699            conn.cancel_transaction(trans_id, did=did)700            manager.release(did=did, conn_id=trans_id)701    return make_json_response(data={'status': True})702 
codekingpro/portable-devtools · Team Ai