2010年05月10日

【PythonでCGI】listを一覧表示(データ型変換と段落)

 PythonにはlistやTupleといった、入れ子構造で自由にデータを保持出来るデータ型を持っている。プログラムをしていると、リスト構造のデータをその時に必要とされる書式で出力べく毎回、似た様なコードを書いているのが現実ではないだろうか。当方のmire.htmやmire.std内のdisplay_err()の例外発生時のローカル変数一覧表示や「【Firebird】System Tables解析例 INDEX」でSQL文を箇条書きにしているlisting()関数等、飽きもせず毎回書いている。

 そこで今回のお題は、個人的にリスト出力の汎用的な関数を作上げ、後々楽することにある。
 さて、仕様たが、今回は、単純なテキスト出力では箇条書きや段落構造を表しずらい面があるのでとりあえずは、HTMLタグを使ったWeb上での段落箇条書き表記に対応させると同時に、そのレイアウト上問題とならない範囲で、より一般的なデータ書式で表示出来る様にしようと思う。

 この様な事を実現する為に必要な技術的要素を先ずは整理して試よう。
  1. リストの入れ子構造に対応する為、関数自身を内在させる再帰処理、
  2. そして、それを実行する条件としてリスト要素がリストか否かを判定したり、要素の型に合わせて適切な出力書式とする為のデータ型の判定を行なうこと、
  3. それに以前触れたUnicode利用時の文字幅「unicodedata.east_asian_width()」の活用

 今回は、以上3点がポイントになる。再帰処理と型判定は次の様な感じで、引数sがリストならリスト処理をする為、リスト処理を定義している自分自身を呼出すことで、型判定は、基本的な型はtypesに定義されたものをimportし利用、ないものは、例えば時間型なら、localtime(time.time())をtype()に入れたものと、比較すればよい。
          再帰処理と型判定の簡易な例
 
 
def ireko(s):
    from type import ListType
    if type(s) is ListType:
        ireko(s)
    else:
        print s
    
 残るUnicode利用時の文字幅は「【UTF8文字幅揃え】unicodedata.east_asian_width()」でも話題にしたが、unicodedata.east_asian_width()で1文字ずつ判定しその文字幅区分を取得、その区分毎の文字幅を適用すれば良い。
    Na: Narrow    1 半角英数
    H : Halfwidth 1 半角カナ
    W : Wide      2 全角文字
    F : Fullwidth 2 全角英数
    N : Neutral   1 アラビア文字
    A : Ambiguous 2 ギリシア文字、キリル文字
 尚、今回は、Pythonのデータ型を文字列化することを課題としたので、整数はLong以上の長整数が扱える等、他の処理系との互換がとれないものもある。Pythonでは、標準で12345678901234567890123456789という整数も有効だからだ。データベースへのデータ格納では注意しよう。  さらに、今回の技術の応用としてはクエリ文で得られるタプルデータでも同様にtype()で比較し、適切な表形式に出力したり、それを入力フォームの値として表示させ、データ更新用のツールを作ることも可能になったりする。

 また、今回は、データ構造のリストを文書構造の段落と箇条書きへの置換をした訳だが、構造自体は当然、リストの方が自由度が高い分、文書構造上では表せないものも出て来る。今回は、その辺りを段落記号文字の表示オプションで配慮している。この辺りは、用途や文書としての必要性で書式が変わることなので、人により仕様に差が出るところだろう。

 さて、今回のコーディングは成行き上「【Firebird】System Tables解析例 INDEX」のソースを修正する形で書いたので、書きかけの別の関数等、多少余計なものを含むこと、ご容赦頂きたい。

 先ずはテストコードの出力は以下の通り、リストは題材として、この関数のマニュアル的なリストを手打ちのものである。まあ、はっきり言って、リスト構造で、文書を書くのは大変だ。emacsで作業したので、リストの入れ子構造は、上手く改行すれば、入れ子状態を段付けしてくれるので若干判りやすいが、カンマ一つなくても怒られるし、括弧を閉じ忘れるてドジる等と大変だった。
Mire's Web - Mozilla Firefox

                    http://192.168.0.220/fb_test/sys_tbl.py
<上略>

listing()の使い方

機能

  1. python内のLISTまたはTUPLEを引数とし、その入れ子構造通りにリスティング。
  2. リスト表記時のタグとして、その階層別で次のものを指定することが出来る。
    • ul : 箇条書き
    • ol : 連番付の箇条書き
    • h1〜h8 : 表題
    • div : ブロック定義
  3. そのタグには、classやstyle等の修飾要素の指定も可能
  4. 連続したリスト要素間の表現は悩みどころ
    • 文字列要素の後に複数リストを連続して伴なう場合
    • そのリストどうしの関係をどう扱うかは用途次第。
    • ここでは段落記号の表示から控えめなdivタグによる行間開けで対応して試た。
    • 下は3ブロックに分かれているのが判るかと思う。
      • 第一ブロックの1
      • 第一ブロックの2
      • 第二ブロックの1
      • 第二ブロックの2
      • 第三ブロックの1
      • 第三ブロックの2
    • 気のせいではない。微妙に行間が空いている。
  5. 文字列以外の型への対応
    • 整数型
      • 1234567890123456789
    • 浮動小数点: 精度はプラットフォーム依存。通常14〜16桁程度で利用
      • 123.4567890123456664
    • time型: localtime(time.time())の吐くオブジェクト time.struct_time
      • 2010-05-10 23:40:12
    • datetime型: 精度はプラットフォーム依存。通常0.001秒単位程度迄で利用
      • 2010-05-10 23:40:12.492826
    • 辞書型: 辞書要素に日本語のリストが含まれても中身がコード表記に(現時点)
    • また、キーにタグ文字を含む様なときは表示幅が上手く取得出来ない(現時点)
      • 
        2.7180000000000000       : 自然対数
        3.1400000000000001       : 円周率
        293                      : 絶対零度
        Pythonスタートブック     : [u'\u8fbb\u3000\u771f\u543e', 2604]
        呟き(Unicode vs Bitecode): [u'\u545f\u304d(Unicode vs Bitecode)', '\xe5\x91\x9f\xe3\x81\x8d(Unicode vs Bitecode)']

引数

  1. str_list
    • 原則文字列のリストを指定
  2. title
    • リスト全体のタイトルを文字列で指定。
  3. h
    • タイトルの表示タグを文字列で指定
  4. tags
    • 各階層別のタグ名を指定(ol, ul, div, pre)。
    • 半角空白文字を挟み、style等の要素を指定可能
  5. nothing
    • 同列のリストが続くときに表示する文字を指定。規定値:¶
    • 文字を表示させたくない時は''(空文字)を指定
    • リスト要素が隣接していても、その系統分けをしたくない時は'nothing'
  6. nest
    • 入れ子階層位置を保持するシステム上の引数
 冗長で申し訳ないが、ソースコードについては余計なものも含んでいる。前半は全て無視して頂きたい。
 
 
 
#!/usr/local/bin/python2.6
# -*- coding: UTF-8 -*-
"""
Firebird System Table 解析 0.0.1
動作にはkinterbasdb mire.html, mire.std, mire.fb が必要

【更新履歴】
* 2010-04-30 初版 INDEXの定義SQL文の出力をするindex_ddls()を作成
* 2010-05-10 listing()関数を強化し汎用度を高めた
"""

__author__      = "Mire in Japan"
__version__     = '0.0.3'
__copyright__   = 'Copyright (c) 2010 Mire'
__license__     = 'GPL'
__url__         = 'http://pythonlife.seesaa.net/article/149444369.html'


## TABLE定義情報の抽出 ##
def table_dlls(tables=[], conn=None, cur=None, dsn=None,ip='127.0.0.1',
               db_file='', user='SYSDBA', password='masterkey', charset='UTF', debug=0):
    """
    TABLE定義情報の抽出
    """
    pass

## DB内のTable情報
def tbl_names(tables=[], conn=None, cur=None, dsn=None,ip='127.0.0.1',
              db_file='', user='SYSDBA', password='masterkey', charset='UTF', debug=0):
    from mire.fb import query
    sql0="""
    SELECT    RDB$VIEW_BLR              /*  0 */
            , RDB$VIEW_SOURCE           /*  1 */
            , RDB$DESCRIPTION           /*  2 */
            , RDB$RELATION_ID           /*  3 TABLE'S INTERNAL ID      */
            , RDB$SYSTEM_FLAG           /*  4 SYSTEM TABLE>=1, USERS=0 */
            , RDB$DBKEY_LENGTH          /*  5 */
            , RDB$FORMAT                /*  6 */
            , RDB$FIELD_ID              /*  7 HAS FIELDS */
            , RDB$RELATION_NAME         /*  8 UNIQUE TABLE NAME IN DB*/
            , RDB$SECURITY_CLASS        /*  9 RERATED WITH SECURITY_CLASSES */
            , RDB$EXTERNAL_FILE         /* 10 */
            , RDB$RUNTIME               /* 11 */
            , RDB$EXTERNAL_DESCRIPTION  /* 12 */
            , RDB$OWNER_NAME            /* 13 CREATED BY THIS OWNER ID */
            , RDB$DEFAULT_CLASS         /* 14 */
            , RDB$FLAGS                 /* 15 */
            , RDB$RELATION_TYPE         /* 16 */
    FROM RDB$RELATIONS
    """
    odr="    ORDER BY RDB$RELATION_ID"
    # WHERE句の生成 #
    if not tables==[]:    # table名の指定がある場合は
        wheres = like_values(field='RDB$RELATION_NAME', values=tables, and_or='or',
                             negative=0, prefix=0, suffix=0, funcs=[], debug=0)
    else:
        wheres = ''
    sql = '%s\n    %s\n%s' % (sql0, wheres, odr)
    datas = query(sql=sql, params=[], mode='all', size=None, conn=conn, cur=cur, dsn=dsn,
                  ip=ip, db_file=db_file, user=user, password=password, charset=charset,
                  debug=debug)

## Table定義情報 ##
def tbl_def():
    """
    """
    sql = """/* テーブル列定義 */
          SELECT RDB$FIELD_NAME         /* 内部列名 RDB$32-47*/
                ,RDB$QUERY_NAME
                ,RDB$VALIDATION_BLR
                ,RDB$VALIDATION_SOURCE
                ,RDB$COMPUTED_BLR
                ,RDB$COMPUTED_SOURCE
                ,RDB$DEFAULT_VALUE
                ,RDB$DEFAULT_SOURCE
                ,RDB$FIELD_LENGTH       /* バイト長 */
                ,RDB$FIELD_SCALE
                ,RDB$FIELD_TYPE         /* varchar37 integer8 */
                ,RDB$FIELD_SUB_TYPE
                ,RDB$MISSING_VALUE
                ,RDB$MISSING_SOURCE
                ,RDB$DESCRIPTION
                ,RDB$SYSTEM_FLAG        /* システム1、ユーザ0 */
                ,RDB$QUERY_HEADER
                ,RDB$SEGMENT_LENGTH
                ,RDB$EDIT_STRING
                ,RDB$EXTERNAL_LENGTH
                ,RDB$EXTERNAL_SCALE
                ,RDB$EXTERNAL_TYPE
                ,RDB$DIMENSIONS
                ,RDB$NULL_FLAG
                ,RDB$CHARACTER_LENGTH   /* 定義文字列長 */
                ,RDB$COLLATION_ID
                ,RDB$CHARACTER_SET_ID   /* 文字コードセット */
                ,RDB$FIELD_PRECISION
            FROM RDB$FIELDS
            """

    sql = """/* 制約 */
          SELECT RDB$CONSTRAINT_NAME      /* 制約内部名        */
                ,RDB$CONSTRAINT_TYPE      /* 制約内容 NOT NULL */
                ,RDB$RELATION_NAME        /* テーブル名*/
                ,RDB$DEFERRABLE
                ,RDB$INITIALLY_DEFERRED
                ,RDB$INDEX_NAME
          FROM RDB$RELATION_CONSTRAINTS;
          """

## RDBMSの関数実装辞書名辞書 ##
rdbms_dic     = {'firebird'  :'fb', 'fb':'fb',  # Firebirdは2.1以降を基準。
                 'sql-server':'ms',             # それ未満はUDFインストール
                 'sqlite'    :'sqlite',
                 'mysql'     :'my',
                 'oracle':'oracle'}

## 関数変換辞書 ##
conv_func_dic = {'fb':     {'rtrim'  :'trim(trailing from ___)',
                            'ltrim'  :'trim(leading from ___)' ,
                            'length' :'char_length'            ,
                            'lengthb':'octet_length'
                           },
                 'ms':     {},
                 'lite':   {},
                 'my':     {},
                 'postgre':{},
                 'oracle': {}
                 }

## SQL 内の関数でFIELD等を括る ##
def put_into_funcs(field, funcs=[], rdbms='fb', default='fb',
                   rdbms_dic=rdbms_dic, conv_func_dic=conv_func_dic):
    """
    funcsリスト内の関数でfieldsを囲む関数

    RDBMS実装の違いを可能な限り吸収の予定。
    先ずは以下に気付いたものをリスティング
    して、実装して行く

     * substr(開始位置, 文字数)
     * bit_length
     * cast(<field> as integer) はANSI規格にある関数

     各実装次第では対応がない、または互換性がないものもあると思うが
     MySQL, PostgreSQL, sqlite, Olacle, MS-SQLServer 程度は網羅して
     みたいところだ。
     尚、現在、単純化の為fieldは1つしかとらないが、その中に複数fieldを
     含む式を記述するも可能なので、それで済む分は、手打ちとなる。
     または他にこの関数の様なものを作り対応することも可能だろう。
     変換定義は、先ずは、rdbmsの示す文字をキーとしてrdbms_dicより定義して
     いる辞書のキー見付け、その変換辞書を特定することにする。
     rdbms_dic={'fb':'fb', 'firebird':'fb', 'sql-server':'ms', 'sqlite':'sqlite'}
     conv_func_dic{'fb':{'rtrim':'trim(trailing from ___)'}}
     conv_func_dic[rdbms_dic[rdbms]]

    変更履歴::
      2010-05-08 初版 この関数は将来mire.sqlに集約

    """
    ## RDBMSの辞書名を辞書から拾う ##
    if rdbms in rdbms_dic:      dbs = rdbms_dic[rdbms]
    else:                       dbs = default
    if dbs in conv_func_dic:    cf  = conv_func_dic[dbs] # 関数変換辞書を選定

    ## 指定した関数で括る ##
    for f in funcs:
        if f in cf:    fn = cf[f]                        # 関数変換辞書にあったらそれに置換
        else:          fn = f                            # ないなら、そのまま
        #
        if fn.find('(')>=0:                              # 丸括弧を伴なう
            print 'fn=', fn
            fns = fn.split('___')
            print fns
            field = '%s%s%s' % (fns[0], field, fns[1])
            print field
        else:
            field = '%s(%s)' % (fn, field)
    return field

## FIELD_X LIKE ? をOR条件で作成 ##
def like_values(field='', values=None, and_or='and', negative=0, prefix=1, suffix=1,
                funcs=[], upper=0, trim=0, rl_trim=0, debug=0):
    """
    WHERE文内用に単一列に対する複数のLIKE条件
    をor/andで結合した文字列にして返す関数

    Parameters::

     values   =['値1','値2'] 値はlistで渡すことを基本にするが、単一の値ならば
                             文字列でも受付ける。
                             これは文字列がfor文での文字列スライスによるご条件指定と
                             なることを防ぐ為必要な処理でもある。
     and_or   ='and'         既定値は'or'。
     negative ='not'         規定値は空文字列で、その場合以外は全て'NOT'となる
     prefix   =0 ,'%%_0_'    通常は数字の0なら値の頭に%を付けない。文字列なら自由に変幻自在に
     suffix   =0 ,'_A_%%'    通常は数字の0なら値の頭に%を付けない。文字列なら自由に変幻自在に
     funcs    =['upper',     文字列リストで関数名を渡し、それを適用する。引数を持つものは括弧
                'trim']      書きで中にそれを記述して渡す。その処理はmire.sql.func()で定義。
     ## 以下はfuncsに集約 ##
     upper    =1             1とすると大文字として比較。0(既定)なら大文字小文字の区別をする。
     trim     =1             -1なら前方 1なら後方 2なら前後スペース除外比較。0(既定)は全比較。
     rl_trim  =0             0: trim(leading 'anystring'from <field>) Firebird, MySQL,
                             l: trim,rtrim,trim  PostgreSQL, Oracle, SQL-Server, MDB
                                                 (FirebirdではUDFのib_udfで、1でも対応可能)

    依存関係::

      SQL内の関数等、実装に依存するものは、オープンソース系のfirebirdを既定に先ずは設計するが、
     その他のRDBMSでの対応の余地を残す設計とする。

    変更履歴::

     2010-05-01 index_ddls内の汎用処理を関数化
                カンマ区切の値で複数指定可能としたが、、その様な
                値の文字列を指定したい場合に機能しなので諦め
     2010-05-02 prefix,suffix指定を数字によるON/OFFとする他、文字列では自由設定にした。
                prefix,suffixの指定を'%'と誤った場合に'%%'として補正するようにした。
     2010-05-03 否定を LIKE 個別に記述するのではなく、全体に対する否定として頭に一つ付ける
                形にした。同時に、and_orの規定値を'and'に変更しだ。
                prefix, suffix共に指定がないときには、すっきりと、'=' での比較処理とする
     2010-05-08 SQL内の関数はリストfuncsをmire.sql.put_into_funcs(field, funcs=[], rdbms='fb')に
                渡し処理することに。そちらでRDBMS別の実装差を吸収。
    """
    if field == '': return '' # field指定がないなら処理しないで空文字を返す

    from types import StringType, UnicodeType
    if type(prefix) is StringType or type(prefix) is UnicodeType:
        if prefix=='%':    prefix='%%'
    else:
        if prefix == 0:    prefix = ''
        else:              prefix = '%%'

    if type(suffix) is StringType or type(suffix) is UnicodeType:
        if suffix=='%':    suffix='%%'
    else:
        if suffix == 0:    suffix = ''
        else:              suffix = '%%'

    # %が前後共に付かない時は 'LIKE' でなく '=' で比較処理
    if prefix==''==suffix: like_or_equal = '='
    else:                  like_or_equal ='LIKE'


    if not funcs==[]:
        field = put_into_funcs(field=field, funcs=funcs)


    if type(values) is StringType or type(values) is UnicodeType:    # 文字列指定なら
        values=[values]                                              # 単一TABLE指定として
    v=[]
    for value in values:
        v.append("%s %s \'%s%s%s\'" % (field, like_or_equal, prefix, value, suffix))
    and_or = '\n          %3s ' % (and_or)
    if debug>0:    remark=' /* generate by mire.sql.like_values() */'
    else:          renark=''
    if negative==0: negation=''
    else:           negation='not'

    return '%3s(%s)%s' % (negation, and_or.join(v), remark)

## INDEX DDL##
def index_ddls(indexes=None, tables=None, not_indexes=None, not_tables=None, mode=0,
               prefix='%%', suffix='%%', and_or='and', conn=None, cur=None, dsn=None,ip='127.0.0.1'
               , db_file='', user='SYSDBA', password='masterkey', charset='UTF', debug=0):
    """
    任意のindexやtable名を指定し、その範囲のINDEX定義のSQL文のリストを出力する関数
    DB接続はconnとcurに渡してもuserとpasswordsと共にdsnかipとdb_fileを渡し内部生成しても
    使える。
    【必要な System Table】
     ユーザ作成のINDEXの設定SQL文を作成し出力するには次のシステムテーブル
    「RDB$INDEX_SEGMENTS」より、そのインデックス名「RDB$INDEX_NAME」とその構成する列名
    「RDB$FIELD_NAME」と順番「RDB$FIELD_POSITION」を抽出する必要がある。動作には
    kinterbasdb mire.html, mire.std, mire.fb が必要
     名称は共に31文字の固定で順番はsmall intであるがこのテーブル自体には、列名が
    存在するテーブル名がどこにも出てこない。
     そちらは、システムテーブル「RDB$INDICES」側に記録されている。
    【既 知 の 問 題 点】
     INDEXは有効化無効化が可能。その情報の活用も必要。
     UNIQUEは単純に1のときと理解して処理、実際には他に0とNULL値をとることがある。
    【更  新  履  歴】
    2010-04-30 新規作成 細かい定義に拘らずSystem Tableを眺めて作成。誤りを含むと思う。
               取敢えず動作は確認。
    2010-05-01 WHERE LIKEによる条件定義に変更し部分一致を許容
               indexとtable名に否定条件指定を可能に
               modeに0(既定値)以外をしていすることで、システムテーブルを除外可能に
    """
    from types import StringType, UnicodeType, ListType, TupleType
    from mire.fb import query

    # tableやindex名等の情報をSystem Table「RDB$INDICES」から取得
    sql0="""
    SELECT    RDB$INDEX_NAME        /* index name */
             ,RDB$RELATION_NAME     /* table name */
             ,RDB$INDEX_ID
             ,RDB$UNIQUE_FLAG       /* 1:Unique Others:Nothing  */
             ,RDB$SEGMENT_COUNT     /*
             ,RDB$INDEX_INACTIVE       Not Used in this function.
             ,RDB$INDEX_TYPE
             ,RDB$FOREIGN_KEY
             ,RDB$SYSTEM_FLAG
             ,RDB$EXPRESSION_BLR
             ,RDB$EXPRESSION_SOURCE
             ,RDB$STATISTICS        */

    FROM      RDB$INDICES
    """
    odr="    ORDER BY  RDB$RELATION_NAME, RDB$INDEX_NAME"
    w=[]
    if not tables==None:                                                        # Table名の指定があるなら
        w.append(like_values('TRIM(RDB$RELATION_NAME)', tables,
                             prefix=prefix, suffix=suffix,
                             and_or=and_or, debug=debug))                       # Where条件に追加

    if not indexes==None:                                                       # Index名の指定があるなら
        w.append(like_values('TRIM(RDB$INDEX_NAME)', indexes,
                             prefix=prefix, suffix=suffix,
                             and_or=and_or, debug=debug))                       # Where条件に追加

    if not not_tables == None:                                                  # Table名の指定があるなら
        w.append(like_values('TRIM(RDB$RELATION_NAME)', not_tables, negative='NOT',
                             prefix=prefix, suffix=suffix,
                             and_or=and_or, debug=debug))                       # Where条件に追加

    if not not_indexes == None:                                                 # Index名の指定があるなら
        w.append(like_values('TRIM(RDB$INDEX_NAME)', not_indexes, negative='NOT',
                             prefix=prefix, suffix=suffix,
                             and_or=and_or, debug=debug))                       # Where条件に追加

    if not mode==0:                                                             # mode指定があるなら
        w.append("RDB$INDEX_NAME NOT LIKE \'RDB$%%\'")                          # Where条件に追加

    if not w==[]:                                                               # Where条件が存在したら
        sql="%sWHERE %s\n%s;" % (sql0, '\n  AND '.join(w), odr)                 # そのWHERE文をsqlに追記
    else:
        sql = "%s\n%s" % (sql0, odr)
    if debug>0:
        print '<div style="font-size:9pt; color:#666; background-color:#EEE"',
        print 'width="600px">debug mode infomation in index_ddls()',
        print 'Query to select index and table name.<pre>'
        print sql
        print '</pre></div>'
    datas = query(sql=sql, conn=conn, cur=cur, dsn=dsn,            # sql実行しデータを取得
                  ip=ip, db_file=db_file, user=user, password=password, debug=debug)

    if conn==None or cur==None:                                    # 接続がないなら接続してから
        from kinterbasdb import connect
        from mire.fb import get_dsn
        if dsn==None:
            dsn=get_dsn(ip, db_file)
        conn = connect(dsn=dsn, user=user, password=password, charset=charset)
        cur  = conn.cursor()
        not_connected = 1
    else:
        not_connected = 0

    # indexの指定列を「RDB$INDEX_SEGMENTS」から指定Index情報をその定義順で取得
    sql="""
    SELECT RDB$FIELD_NAME       /* FIELD NAME  */
          ,RDB$FIELD_POSITION   /* FIELD ORDER */
          ,RDB$STATISTICS
    FROM   RDB$INDEX_SEGMENTS
    WHERE  TRIM(RDB$INDEX_NAME)=?   /* INDEX NAME  */
    ORDER BY RDB$FIELD_POSITION;
    """
    if len(datas)>0:
        q =[]
        for data in datas:                                      # Indexを1つずつ取出し
            qs = ['CREATE']                                     # SQL文をリストで調製
            try:
                if data[3]==1:                                  # Uniqueなら
                    qs.append('UNIQUE')                         # 'UNIQUE'を追加
            except: pass
            qs.append('INDEX %s ON %s' % (data[0].rstrip(),
                                          data[1].rstrip()))    # IndexとTable名の入る区を追加
            flds  = query(sql=sql, params=[data[0].rstrip()],
                          conn=conn, cur=cur, dsn=dsn,
                          ip=ip, db_file=db_file, user=user,
                          password=password, debug=debug)       # sql実行しIndex対象の列名を取得
            f_lst = []                                          # 対象列のリストを作り
            for fld in flds:
                f_lst.append(fld[0].rstrip())
            qs.append('(%s);' % (', '.join(f_lst)))
            q.append(' '.join(qs))                              # INDEX生成時のSQL文を生成
        return q                                                # そのリストを返す
    else:
        return []                                               # 該当なしなので空リストを返す

    if not_connected == 1:                                      # 接続を引継いでいなかったら
        try:
            conn.close()                                        # 切断
        except:
            pass

## 文字列リストHTML画面出力 ##
def listing(str_list,title='',h='h4',tags='ol', tree=1,nothing='&#182;',nest=0):
    """
    文字列リストのリスト出力用関数
    str_list  : 基本的に文字列を要素とするリストを指定。
              : 但し、リスト要素には文字列を要素とするリストを入れ子で持つ
              : ことも出来る。
    tags      : 基本的に文字列でリスト表記用のタグ名(ol, ul, div, pre)を指定
              : 但し、入れ子階層別でタグを切替えたい場合にはリストで指定可
    【更  新  履  歴】
    2010-05-01 初版 何れ、mire.htm に移行
    2010-05-09 str_list内要素がリストまたはタプルの場合にはこの関数で再帰処理
               複合構造の箇条書きに対応。
    2010-05-10 各データ型の変換をobj2value_str()に切出し移行
    """
    from types import ListType, TupleType, StringType, UnicodeType, IntType, LongType, FloatType

    ## リスト記載用のタグ ##
    if type(tags) is ListType or type(tags) is TupleType:      # 引数の型がリストorタプルなら
        #print 'len(tags)=', len(tags),
        #print 'tags[nest]=', tags[nest],
        #print 'tags=', tags

        if len(tags)>nest:
            #print nest
            tag_str = tags[nest]        #   階層別のものを設定
        else:                     tag_str = tags[-1]           #   なくなったら、最後のものを設定
    else:                         tag_str = tags               # それ以外なら、そのまま設定

    ## ol,ulタグ時にliタグを付加 ##
    if tag_str=='ol' or tag_str=='ul':  bu='li'
    else:                         bu=''

    ## 表題タイトル ##
    if type(title) is ListType or type(title) is TupleType:    # 引数の型がリストorタプルなら
        if len(title)>nest:    title_str = title[nest]         #   階層別のものを設定
        else:                     title_str = ''               #   なくなったら、最後のものを設定
    else:                         title_str = title            # それ以外なら、そのまま設定

    ## 表題タグ ##
    if type(h) is ListType or type(h) is TupleType:            # 引数の型がリストorタプルなら
        if len(h)>nest:        h_str = h[nest]                 #   階層別のものを設定
        else:                     h_str = h[-1]                #   なくなったら、最後のものを設定
    else:                         h_str = h                    # それ以外なら、そのまま設定

    if not title_str=='':
        print '<%s>%s</%s>' % (h_str, title_str, h_str.split()[0]), # 表題表示
    if not tag_str=='' and tag_str.split()[0].lower() in ['ol', 'ul',
                                                          'div']:   # ol,ulなら入れ子リストとして
        print '<%s>' % (tag_str),
    i = 0
    for str_line in str_list:
        if type(str_line) is ListType or type(str_line) is TupleType: # 引数がLISTかTUPLE型なら
            nest_p = listing(str_list=str_line, title='',
                             tags=tags, nothing=nothing, nest=nest+1) #   入れ子でこの関数を実行
            if tree>0 and not nest_p == 0:
                #print nest_p
                if nothing=='spacer':
                    print '<div style=\"line-height:85%%; padding:2px;',
                    print 'font-size:9pt; width:4pt;\"></div>'
                elif not nothing=='nothing':
                    print '<div style=\"line-height:85%%; padding:2px;',
                    print 'background-color:#E0E0E0; color:#FFFFFF;',
                    print 'font-size:9pt; width:4pt;\">%s</div>' % (nothing)
            i = 0
        else:
            from time import time
            from datetime import datetime
            if not type(str_line) is StringType or type(str_line) is UnicodeType:
                str_line = obj2value_str(str_line)
            if bu == '':
                if tag_str.split()[0][:-1] in ['h']:
                    print '<%s>%s</%s>' % (tag_str, str_line, tag_str.split()[0]),
                else:
                    print '%s' % (str_line)
            else:           print '<%s>%s</%s>' % (bu, str_line, bu)
            i = i + 1

    if not tag_str=='' and tag_str.split()[0].lower() in ['ol', 'ul', 'div']:
        print '</%s>' % (tag_str.split()[0]),
    return i

def obj2value_str(obj):
    """
    オブジェクトの型を調べて値を文字列にして返す関数
    整数型    : Python長整数精度で変換
    浮動小数点: 倍精度で変換
    時間型    : localtime(time.time())の吐くものをISO表記文字列に変換
    datetime型: マイクロ秒単位のISO表記文字列に変換
    辞書型    : dic2valuee_str()で処理
    【更  新  履  歴】
    2010-05-09 初版
    2010-05-10 辞書型の処理をdic2valuee_str()に独立移行
    """
    from types import ListType, TupleType, StringType, UnicodeType
    from types import IntType, LongType, FloatType, DictType
    from time import time
    from datetime import datetime
    t = type(obj)
    if t is IntType or t is LongType:  s = u'%d' % (obj)                        # 整数
    elif t is FloatType:               s = '%16.16f' % (obj)                    # 浮動小数型
    elif t is type(localtime(time())): s = '%4d-%02d-%02d %02d:%02d:%02d' % obj[:6] # 時間型
    elif t is type(datetime.today()):                                           # datetime型
        s = '%4d-%02d-%02d %02d:%02d:%02d.%06d' % (obj.year, obj.month, obj.day,
                                                   obj.hour, obj.minute, obj.second,
                                                   obj.microsecond)
    elif type(obj) is DictType:        s = dic2value_str(obj)                   # 辞書型
    else:                              s = obj                         # 以外はそのまま
    if type(s) is StringType or type(s) is UnicodeType: return s
    else:                                               return obj     # 諦めてそのまま

def unicode_width(s, width={'Na':1, 'W':2, 'F':2, 'H':1, 'A':2, 'N':1}):
    """
    UNICODE文字列の文字幅を返す関数
    既定は東アジア文字セット利用前提としている
    Na: Narrow    1 半角英数
    H : Halfwidth 1 半角カナ
    W : Wide      2 全角文字
    F : Fullwidth 2 全角英数
    N : Neutral   1 アラビア文字
    A : Ambiguous 2 ギリシア文字、キリル文字
    詳細はウィキペディア(Wikipedia): 東アジアの文字幅
    http://ja.wikipedia.org/wiki/%E6%9D%B1%E3%82%A2%E3%82%B8%E3%82%A2%E3%81%AE%E6%96%87%E5%AD%97%E5%B9%85
    Python unicodedata.east_asian_width()
    http://www.python.jp/doc/2.5/lib/module-unicodedata.html
    別解としては、 【UTF8文字幅揃え】unicodedata.east_asian_width() で紹介した
    http://pythonlife.seesaa.net/article/133360506.html?1273463358
    hush_puppyさんの「RubyとPythonで全角文字を半角文字2文字として数える」
    http://d.hatena.ne.jp/hush_puppy/20090226/1235661269
    がある。 恐らく、そちらのほうが処理は早いだろうから、一般にはhush_puppyさん作成分を
    お使いになると良いかと思う。当方のこの関数は、曖昧、中立の文字の扱いを状況次第で
    切替えることが出来たらいいなとの発想と、見た目で仕組みが判り易い方式とした迄である。
    ※ 残念なことにCJK拡張Bの漢字を指定するとeast_asian_width()はTypeErrorを吐く。
      確かにプラットフォーム側の対応も課題なので、それでどうなのって言う感じだが、
      場合によっては例外処理で補正した方が良いのかもしれない。
    """
    from unicodedata import east_asian_width
    n = 0
    for c in s:                                   # 1文字ずつ、字幅辞書にwidthに
        n = n + width[east_asian_width(c)]        # east_asian_width()が返す文字幅種別を入れ
    return n                                      # 幅の累計値を返す

def max_width(str_list):
    maxwidth=0
    for s in str_list:
        maxwidth = max(unicode_width(s),maxwidth)
    return max_wodth

def repeat(n, c=' '):
    """
    文字列リピート関数
    """
    r = ''
    for i in range(n):                            # n回繰返し
        r = r + c                                 # cを追記し文字列rを生成
    return r

def dic2value_str(obj, sort=1, tag='pre'):
    """
    辞書型のデータを一覧表記の文字列に変換
    要素がリスト等obj2value_str()で処理出来ない
    ものは、そのまま文字列扱いで処理される。

    """
    if len(obj)>0:                                  # 辞書要素があるなら
        keys = obj.keys()                           # キーの抽出
        if sort>0:                                  # 並替え指定なら
            keys.sort()                             # キーを並替え

        if not tag=='':                             # タグ指定があるなら
            ss = u'<%s>' % (tag)                    # 開始タグをセット
        else:
            ss = u''
        key_width = 0                               # キーの最大幅用変数初期化
        lst = []                                    # 辞書文字列のリスト変数初期化
        for k in keys:                              # キーを一つずつ処理
            key = u'%s' % (obj2value_str(k))        # キーの文字列生成
            v   = u'%s' % (obj2value_str(obj[k]))   # 値側の文字列生成
            k_w = unicode_width(key)                # キーの文字幅
            key_width=max(k_w, key_width)           # キーの文字幅の最大値
            lst.append([key, v])                    # キーと値のリストを要素とするリストに追加
        for key,v in lst:                           # リストからキーと値を取出し、文字列を生成
            ss = ss + '\n' + key
            ss = ss + repeat(key_width-unicode_width(key))       # Unicode文字幅で空白文字補充
            ss = ss + ': ' + v
        if not tag=='':                                               # タグ指定があるなら
            ss = ss + '</%s>' % (tag.split()[0])                      # 終了タグをセット
        return ss                                                     # 文字列を返す
    else:                                                             # 文字列要素がないので
        return '{要素のない辞書}'                                     # その旨表示する


##########################
# モジュールのテスト実行 #
##########################
if __name__ == '__main__':
    debug = 1
    try:
        from datetime import datetime
        from time import time, localtime
        from mire.htm import html_header, html_footer # CGIとして実行させる場合に必要
        html_header(title='Index Test')
        from mire.std import conf_section_items
        conf = conf_section_items(file_name='/etc/ZipCodeJp/ZipCodeJP.conf',
                                  section='DATABASE', debug=debug)

        # INDEX名指定時
        lst = index_ddls(indexes=['IDX_BY_KANA'], ip=conf['ip'], db_file=conf['db_file'],
                         user=conf['user'], password=conf['password'],
                         charset=conf['charset'], debug=debug)
        listing(lst, 'index=\'IDX_BY_KANA\'と指定場合それのみを表示。indexはDATABASE内共通の為tableの指定は不要')

        # TABLE名指定時
        lst = index_ddls(tables=['TEST', 'ZIPCODE_JP'], and_or='or', ip=conf['ip'],
                         db_file=conf['db_file'], user=conf['user'], password=conf['password'],
                         charset=conf['charset'], debug=debug)
        listing(lst, 'titles=[\'TEST\', \'ZIPCODE_JP\']と指定場合は、設定があればそのprimary keyも含めて(現状)表示される')

        # 何も指定しない時
        lst = index_ddls(ip=conf['ip'], db_file=conf['db_file'], user=conf['user'],
                         password=conf['password'],charset=conf['charset'], debug=debug)
        listing(lst, '何も指定していしない場合は、システムテーブル分も含み全INDEXが表示される')

        # not_tables指定時
        lst = index_ddls(not_tables='RDB$', prefix='', ip=conf['ip'], db_file=conf['db_file'], user=conf['user'],
                         password=conf['password'],charset=conf['charset'], debug=debug)
        listing(lst, 'システムテーブル分に当る\'RDB$\'で始まるものを除外し、その他の全INDEXを表示。')

        # not_tables指定時
        lst = index_ddls(not_indexes='RDB$', prefix='', ip=conf['ip'],
                         db_file=conf['db_file'], user=conf['user'],password=conf['password'],
                         charset=conf['charset'], debug=debug)
        listing(lst, 'システム生成分に当る\'RDB$\'で始まるものを除外し、その他の全INDEXを表示。mode=1でもほぼ同じ解となる')

        # つぶやき
        print """<pre> ※ System Tableとprimary keyのINDEXは「RDB$」で始まる名前となる。"""
        print '</pre>'


        ## listing()関数のテスト(2010-05-10掲載テスト) ##
        TstLst = ['機能',
                  ['python内のLISTまたはTUPLEを引数とし、その入れ子構造通りにリスティング。',
                   'リスト表記時のタグとして、その階層別で次のものを指定することが出来る。',
                   ['ul     : 箇条書き',
                    'ol     : 連番付の箇条書き',
                    'h1〜h8 : 表題',
                    'div    : ブロック定義'],
                   'そのタグには、classやstyle等の修飾要素の指定も可能',
                   '連続したリスト要素間の表現は悩みどころ',
                   ['文字列要素の後に複数リストを連続して伴なう場合',
                    'そのリストどうしの関係をどう扱うかは用途次第。',
                    'ここでは段落記号の表示から控えめなdivタグによる行間開けで対応して試た。',
                    '下は3ブロックに分かれているのが判るかと思う。',
                    ['第一ブロックの1','第一ブロックの2'],
                    ['第二ブロックの1','第二ブロックの2'],
                    ['第三ブロックの1','第三ブロックの2'],
                    '気のせいではない。微妙に行間が空いている。',
                    ],
                   '文字列以外の型への対応',
                   [
                    '整数型',
                    [1234567890123456789],
                    '浮動小数点: 精度はプラットフォーム依存。通常14〜16桁程度で利用',
                    [123.45678901234567],
                    'time型: localtime(time.time())の吐くオブジェクト time.struct_time',
                    [localtime(time())],
                    'datetime型: 精度はプラットフォーム依存。通常0.001秒単位程度迄で利用',
                    [datetime.today()],
                    '辞書型: 辞書要素に日本語のリストが含まれても中身がコード表記に(現時点)',
                    'また、キーにタグ文字を含む様なときは表示幅が上手く取得出来ない(現時点)',
                    [{3.14:'円周率', 2.718:'自然対数', 293:'絶対零度',
                      '呟き(Unicode vs Bitecode)':[u'呟き(Unicode vs Bitecode)',
                                                   '呟き(Unicode vs Bitecode)'],
                      'Pythonスタートブック':[u'辻 真吾', 2604]}]],
                   ],
                  '引数',
                  ['str_list',
                   ['原則文字列のリストを指定'],
                   'title',
                   ['リスト全体のタイトルを文字列で指定。'],
                   'h',
                   ['タイトルの表示タグを文字列で指定'],
                   'tags',
                   ['各階層別のタグ名を指定(ol, ul, div, pre)。',
                    '半角空白文字を挟み、style等の要素を指定可能'],
                   'nothing',
                   ['同列のリストが続くときに表示する文字を指定。規定値:&#182;',
                    '文字を表示させたくない時は\'\'(空文字)を指定',
                    'リスト要素が隣接していても、その系統分けをしたくない時は\'nothing\''],
                   'nest',
                   ['入れ子階層位置を保持するシステム上の引数']
                   ]
                  ]
        title = 'listing()の使い方'
        listing(str_list=TstLst, title=title, h=['h3', 'h4', 'h5'],
                tags=['h4 style="background-color:#FFF; width:50%;"', 'ol','ul', 'ul', 'ol'],
                nothing='spacer', nest=0)

    except:
        from mire.htm import display_err
        display_err(locals(),title='Index Test')
    finally:
        html_footer()
タグ:Python CGI
posted by Mire at 16:11 | Comment(0) | TrackBack(0) | Pythonプログラミング | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
※ブログオーナーが承認したコメントのみ表示されます。
この記事へのトラックバックURL
http://blog.seesaa.jp/tb/149444369
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック
月額見放題1,000円開始キャンペーンバナー(画像ありver)
紺碧の艦隊 ルパン三世 GREAT CHASE クリックプロモーション
<< 2013年01月 >>
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    
カテゴリ
タグクラウド
ファン
利用中のオープンソース
最近のコメント
最近の記事
過去ログ
QRコード
レガシーなアプリはいかが?
Dell 法人のお客様ページ
  • 【法人様向け】デル、お得なキャンペーン情報
  • 法人のお客様向け ストレージソリューション
  • 法人のお客様向け ネットワークソリューション
  • 【SOHO法人様向け】デル・オンライン広告限定ページ
  • デル-個人のお客様ページ
  • 【個人のお客様向け】デル・オンライン広告限定ページ
  • オンライン広告限定キャンペーンページ
  • ソフトウェア&周辺機器 パソコン工房
    ツートップインターネットショップ(twotop.co.jp) マウスコンピューター/G-Tune
  • ×

    この広告は1年以上新しい記事の投稿がないブログに表示されております。