#!/usr/bin/python
#encoding: utf8
import psycopg2
import yandex.maps.opster3 as opster


DEFAULT_CONNSTR='password=mapspro user=mapspro host=pg93mpro.maps.dev.yandex.net dbname=mapspro_prod_29_May_2015'

def parse_hstore(hstore):
    hstore = hstore[1:-1]
    dic = {}
    for kv in hstore.split("\", \""):
        k,v = kv.split("\"=>\"")
        dic[k]=v
    return dic

def create_hstore(dic):
    return ",".join( map(lambda item: "\"%s\"=>\"%s\"" % (item[0],item[1]), dic.iteritems() ))


def next_attr_id(conn):
    cur = conn.cursor()
    cur.execute( "SELECT nextval('revision.attributes_id_seq')" );
    return cur.fetchone()[0]

def update_attrs(conn,attrs_to_update):
    for id, contents in  attrs_to_update.iteritems():
        cur = conn.cursor()
        cur.execute( "UPDATE revision.attributes SET contents = %s::hstore WHERE id = %s;",
            (create_hstore(contents), id))

def create_attrs(conn,attrs_to_create):
    for old_id, new_attr in  attrs_to_create.iteritems():
        cur = conn.cursor()
        new_id, contents = new_attr
        cur.execute( "INSERT INTO  revision.attributes(id, contents) VALUES(%s, %s::hstore);",
            (new_id, create_hstore(contents)))

def init_local_object_revision_table(local_table_name, local_cursor, remote_cursor):
    local_cursor.execute("""DROP TABLE IF EXISTS %s;"""%local_table_name)
    local_cursor.execute("""CREATE UNLOGGED TABLE %s (object_id bigint, commit_id bigint, attrs hstore); """%local_table_name)
    counter = 0
    batch_size = 1000
    while(1):
        object_revisions = remote_cursor.fetchmany(batch_size);
        if(not object_revisions):
            break
        query = """ INSERT INTO %s VALUES """ % local_table_name
        query = query + ",".join( map (lambda record: """(%d, %d, '%s')""" % (record[0],record[1],record[2]), object_revisions))
        query = query + """;"""
        local_cursor.execute(query)
        counter = counter + len(object_revisions)
        print ("""%s: %d records inserted""") % (local_table_name, counter)

    local_cursor.execute("""CREATE INDEX ON %s (object_id, commit_id); """ % local_table_name)
    print """%s: index created""" % local_table_name


@opster.command()
def migrate_hydro_revision(
    debug=('d', False, 'dry run mode (transaction will be rolled back)'),
    connstr=('c', DEFAULT_CONNSTR, 'connection string')
):
    '''
    migrate hydro in revision scope
    '''
    conn = psycopg2.connect(connstr)

    #Step 1. Find revisions.attributes of relations which references to 'hydro' and create 'hydro_ln' versions
    cur = conn.cursor()
    cur.execute( "SELECT id, contents FROM  revision.attributes WHERE contents->'rel:master'= 'hydro'");

    rel_attrs_to_update = {}
    rel_attrs_to_create = {}
    for record in cur:
        attr_id = record[0]
        contents = parse_hstore(record[1])
        if contents['rel:role'] == 'fc_part':
            continue
        elif contents['rel:role'] == 'ln_part':
            contents['rel:master'] = 'hydro_ln'
            rel_attrs_to_update[attr_id] = contents
        else:
            contents['rel:master'] = 'hydro_ln'
            rel_attrs_to_create[attr_id] = (next_attr_id(conn), contents)
    update_attrs(conn, rel_attrs_to_update)
    create_attrs(conn, rel_attrs_to_create)

    #Step 2. Find revisions.attributes of objects which references to 'hydro' and create 'hydro_ln' versions
    cur = conn.cursor()
    cur.execute( "SELECT id, contents FROM  revision.attributes WHERE contents?'cat:hydro'")

    attrs_to_create = {}
    for record in cur:
        attr_id = record[0]
        contents = parse_hstore(record[1])
        contents['cat:hydro_ln']="1"
        contents['hydro_ln:ft_type_id'] = contents['hydro:ft_type_id']
        contents['hydro_ln:disp_class'] = contents['hydro:disp_class']
        del contents['cat:hydro']
        del contents['hydro:ft_type_id']
        del contents['hydro:disp_class']
        attrs_to_create[attr_id] = (next_attr_id(conn), contents)

    create_attrs(conn, attrs_to_create)

    #Step 3. Find hydro_ln objects and update their attributes_id
    cur =  conn.cursor()
    for k,v in attrs_to_create.iteritems():
        cur_attr_id = k
        new_attr_id = v[0]
        cur.execute(
            """ UPDATE revision.object_revision_without_geometry SET attributes_id  = %s
                WHERE attributes_id = %s AND
                      object_id IN( SELECT distinct(master_object_id)
                                    FROM revision.object_revision_relation r, revision.attributes a
                                    WHERE r.attributes_id = a.id AND
                                          a.contents->'rel:role' = 'ln_part');""",
                (new_attr_id, cur_attr_id))

    #Step 4. Update relation attributes
    for k,v in rel_attrs_to_create.iteritems():
        cur_attr_id = k
        new_attr_id = v[0]
        cur.execute(
            """ UPDATE revision.object_revision_relation SET attributes_id  = %s
                WHERE attributes_id = %s AND
                      master_object_id IN (SELECT distinct(object_id)
                                           FROM revision.object_revision_without_geometry r, revision.attributes a
                                           WHERE r.attributes_id = a.id AND a.contents?'cat:hydro_ln');""",
            (new_attr_id, cur_attr_id))

    #Step 5. Fix ft_type_id for hydro_ln and hydro
    cur.execute(
        """ UPDATE revision.attributes SET contents = contents || hstore('hydro_ln:ft_type_id','554')
            WHERE contents ? 'cat:hydro_ln' AND
                  contents -> 'hydro_ln:ft_type_id'
                  NOT IN ('552', '553', '554', '555', '556', '557', '558', '559'); """)
    cur.execute(
        """ UPDATE revision.attributes SET contents = contents || hstore('hydro:ft_type_id','505')
            WHERE contents ? 'cat:hydro' AND
                  contents -> 'hydro:ft_type_id'
                  NOT IN ('501', '502', '503', '504', '505', '506', '507', '508', '509', '510', '517'); """)
    if not debug:
        conn.commit()

@opster.command()
def migrate_hydro_view(
    debug=('d', False, 'dry run mode (transaction will be rolled back)'),
    connstr_r=('r', DEFAULT_CONNSTR, 'connection string to revision database'),
    connstr_t=('t', DEFAULT_CONNSTR, 'connection string to view_trunk'),
    connstr_s=('s', DEFAULT_CONNSTR, 'connection string to view_stable'),
):

    '''
    migrate hydro in view  scope
    '''
    conn_r = psycopg2.connect(connstr_r)
    conn_t = psycopg2.connect(connstr_t)
    conn_s = psycopg2.connect(connstr_s)

    cur_object_revision_hydro = conn_r.cursor("object_revision_hydro")        
    cur_object_revision_hydro.execute(""" SELECT object_id, commit_id, a.contents
                   FROM revision.object_revision_without_geometry r, revision.attributes a
                   WHERE r.attributes_id = a.id AND
                         (a.contents ? 'cat:hydro' OR
                          a.contents ? 'cat:hydro_ln'); """)


    cur_object_revision_relation_hydro = conn_r.cursor("object_revision_relation_hydro")
    cur_object_revision_relation_hydro.execute(""" SELECT object_id, commit_id, a.contents
                   FROM revision.object_revision_relation r, revision.attributes a
                   WHERE r.attributes_id = a.id AND
                         (a.contents -> 'rel:master' = 'hydro' OR
                          a.contents -> 'rel:master' = 'hydro_ln'); """)

    conns_v = [conn_t]
    if connstr_t != connstr_s:
        conns_v.append(conn_s)

    for conn_v in conns_v:
        cur_object_revision_hydro.scroll(0, mode = 'absolute')
        init_local_object_revision_table('tmp_hydro_object_revision', conn_v.cursor(), cur_object_revision_hydro)
        cur_object_revision_relation_hydro.scroll(0, mode = 'absolute')
        init_local_object_revision_table('tmp_hydro_object_revision_relations', conn_v.cursor(), cur_object_revision_relation_hydro)
        cursor = conn_v.cursor()
        cursor.execute( """SELECT nspname FROM pg_namespace WHERE nspname like 'vrevisions_%';""" )
        for schema in cursor:
            print schema[0]
            cur_v = conn_v.cursor()
            cur_v.execute(
                """ UPDATE %s.objects_c o_c SET domain_attrs = r.attrs
                FROM tmp_hydro_object_revision r
                WHERE o_c.domain_attrs ? 'cat:hydro' AND
                o_c.id = r.object_id AND
                o_c.commit_id = r.commit_id;""" % schema[0])
            print cur_v.query
            cur_v.execute(
                """ UPDATE %s.objects_r o_r SET domain_attrs = r.attrs
                FROM tmp_hydro_object_revision_relations r
                WHERE o_r.domain_attrs -> 'rel:master'='hydro' AND
                o_r.id = r.object_id AND
                o_r.commit_id = r.commit_id; """ % schema[0])
            print cur_v.query
            cur_v.execute(
                """ UPDATE %s.suggest_data s_d SET categories = delete(categories, 'cat:hydro')||hstore('cat:hydro_ln','1')
                FROM tmp_hydro_object_revision  r
                WHERE s_d.categories ? 'cat:hydro' AND
                s_d.object_id = r.object_id AND
                s_d.commit_id = r.commit_id AND
                r.attrs ? 'cat:hydro_ln'; """ % schema[0])
            print cur_v.query
    if not debug:
        for conn_v in conns_v:
            print  "Commiting..."
            conn_v.commit()

@opster.command()
def migrate_rd(
    debug=('d', False, 'dry run mode (transaction will be rolled back)'),
    noview=('n', False, 'skip view migration'),
    attrs=('a', "ferry,fow,stairs,access", 'List of attributes to be migrated'),
    connstr_r=('r', DEFAULT_CONNSTR, 'connection string to revision'),
    connstr_t=('t', DEFAULT_CONNSTR, 'connection string to view_trunk'),
    connstr_s=('s', DEFAULT_CONNSTR, 'connection string to view_stable'),
):
    '''
    migrate rd_el in revision scope
    '''
    def migrate_ferry_domain_attr(cursor, table_name, attrs_column):
        cursor.execute(
            (""" UPDATE %s SET %s = delete(%s, 'rd_el:ferry')
            WHERE %s ? 'cat:rd_el' AND
                  %s -> 'rd_el:ferry' = '0'; """) %
            (table_name, attrs_column, attrs_column, attrs_column, attrs_column))
        cursor.execute(
            (""" UPDATE %s SET %s = delete(%s, 'rd_el:ferry') || hstore('rd_el:struct_type', CAST( CAST(%s->'rd_el:ferry' AS INTEGER) + 2 AS VARCHAR))
            WHERE %s ? 'cat:rd_el' AND
                  %s -> 'rd_el:ferry' in  ('1','2'); """) %
            (table_name, attrs_column, attrs_column, attrs_column, attrs_column, attrs_column))

    def migrate_fow_domain_attr(cursor, table_name, attrs_column):
        cursor.execute(
            (""" UPDATE %s SET %s = CASE WHEN %s->'rd_el:fow' = '16' THEN %s || hstore('rd_el:fow','10') ELSE %s || hstore('rd_el:fow','0') END
            WHERE %s ? 'cat:rd_el' AND
                  %s -> 'rd_el:fow' IN ('1', '3', '12', '13', '14', '16'); """) %
            (table_name, attrs_column, attrs_column, attrs_column, attrs_column, attrs_column, attrs_column))

    def migrate_fow_service_attr(cursor, table_name):
        cursor.execute(
            (""" UPDATE %s SET service_attrs = service_attrs ||
                                       hstore('srv:hotspot_label',
                                               regexp_replace(service_attrs->'srv:hotspot_label',
                                                              'fow__[0-9]*',
                                                              concat('fow__',  domain_attrs->'rd_el:fow') ))
                WHERE domain_attrs ? 'cat:rd_el' AND
                      domain_attrs -> 'rd_el:fow' IN ('10', '16') """) % (table_name))

    def migrate_stairs_domain_attr(cursor, table_name, attrs_column):
        cursor.execute(
            (""" UPDATE %s SET %s = delete(%s, 'rd_el:stairs') || hstore('rd_el:struct_type', '5')
            WHERE %s ? 'cat:rd_el' AND
                  %s ? 'rd_el:stairs'; """) %
            (table_name, attrs_column, attrs_column, attrs_column, attrs_column))

    def migrate_access_domain_attr(cursor, table_name, attrs_column):
        cursor.execute(
            (""" UPDATE %s SET %s = %s || hstore('rd_el:access_id', CAST( (CAST( %s -> 'rd_el:access_id' AS INTEGER)::bit(5) | B'10000')::integer AS VARCHAR))
            WHERE %s ? 'cat:rd_el' AND
                  %s -> 'rd_el:fc' IN ('3', '4', '5', '6', '7', '8', '9', '10'); """) %
            (table_name, attrs_column, attrs_column, attrs_column, attrs_column, attrs_column))

    conn_r = psycopg2.connect(connstr_r)
    conn_t = psycopg2.connect(connstr_t)
    conn_s = psycopg2.connect(connstr_s)
    attrs_to_convert = attrs.split(',')

    if 'fow' in attrs_to_convert:
        migrate_fow_domain_attr(conn_r.cursor(), 'revision.attributes', 'contents')
    if 'ferry' in attrs_to_convert:
        migrate_ferry_domain_attr(conn_r.cursor(), 'revision.attributes', 'contents')
    if 'stairs' in attrs_to_convert:
        migrate_stairs_domain_attr(conn_r.cursor(), 'revision.attributes', 'contents')
    if 'access' in attrs_to_convert:
        migrate_access_domain_attr(conn_r.cursor(), 'revision.attributes', 'contents')

    conns_v=[]
    if not noview:
        conns_v = [conn_t]
        if connstr_t != connstr_s:
            conns_v.append(conn_s)
    for conn_v in conns_v:
        cursor = conn_v.cursor()
        cursor.execute( """SELECT nspname FROM pg_namespace WHERE nspname like 'vrevisions_%';""" )
        for schema in cursor:
            print schema[0]
            if 'fow' in attrs_to_convert:
                migrate_fow_domain_attr(conn_v.cursor(), schema[0]+'.objects_l', 'domain_attrs')
                migrate_fow_service_attr(conn_v.cursor(), schema[0]+'.objects_l')
            if 'ferry' in attrs_to_convert:
                migrate_ferry_domain_attr(conn_v.cursor(), schema[0]+'.objects_l', 'domain_attrs')
            if 'stairs' in attrs_to_convert:
                migrate_stairs_domain_attr(conn_v.cursor(), schema[0]+'.objects_l', 'domain_attrs')
            if 'access' in attrs_to_convert:
                migrate_access_domain_attr(conn_v.cursor(), schema[0]+'.objects_l', 'domain_attrs')

    if not debug:
        for conn in [conn_r] + conns_v:
            conn.commit()

@opster.command()
def migrate_poi(
    debug=('d', False, 'dry run mode (transaction will be rolled back)'),
    connstr_r=('r', DEFAULT_CONNSTR, 'connection string to revision database'),
    connstr_t=('t', DEFAULT_CONNSTR, 'connection string to view_trunk'),
    connstr_s=('s', DEFAULT_CONNSTR, 'connection string to view_stable'),
):
    '''
    migrate poi in revision scope
    '''
    poi_migration_map ={
    'poi_religion': ('110', '111', '112', '113', '114'),
    'poi_edu': ('167', '168', '169', '182'),
    'poi_medicine': ('172', '181'),
    'poi_shopping': ('173', '174', '175'),
    'poi_food': ('176', '177', '178', '179', '180'),
    'poi_goverment': ('183', '184'),
    'poi_service': ('186'),
    'poi_leisure': ('223', '228', '229', '230', '232', '231'),
    'poi_auto': ('253', '254')
    }

    def migrate_domain_attr(cursor, table_name, attrs_column):
        for poi_cat, ft_type_ids in poi_migration_map.items():
            cursor.execute(
                (""" UPDATE %s SET
                %s = (%s - ARRAY['cat:poi','poi:ft_type_id','poi:disp_class'])||
                      hstore(ARRAY['%s','1','%s',%s -> 'poi:ft_type_id','%s',%s -> 'poi:disp_class'])
                WHERE %s ? 'cat:poi' AND
                      %s -> 'poi:ft_type_id' IN (%s); """) %
                (table_name, attrs_column, attrs_column,
                 'cat:'+poi_cat, poi_cat+':ft_type_id', attrs_column, poi_cat+':disp_class', attrs_column,
                 attrs_column, attrs_column, ",".join( map(lambda item: """'"""+item+"""'""", ft_type_ids)))
                 )

    conn_r = psycopg2.connect(connstr_r)
    conn_t = psycopg2.connect(connstr_t)
    conn_s = psycopg2.connect(connstr_s)

    migrate_domain_attr(conn_r.cursor(), 'revision.attributes', 'contents')

    conns_v = [conn_t]
    if connstr_t != connstr_s:
        conns_v.append(conn_s)

    for conn_v in conns_v:
        cursor = conn_v.cursor()
        cursor.execute( """SELECT nspname FROM pg_namespace WHERE nspname like 'vrevisions_%';""" )
        for schema in cursor:
            print schema[0]
            migrate_domain_attr(conn_v.cursor(), schema[0]+'.objects_p', 'domain_attrs')

    if not debug:
        for conn in [conn_r] + conns_v:
            conn.commit()

@opster.command()
def migrate_srv_ra(
    debug=('d', False, 'dry run mode (transaction will be rolled back)'),
    connstr_r=('r', DEFAULT_CONNSTR, 'connection string to revision database'),
    connstr_t=('t', DEFAULT_CONNSTR, 'connection string to view_trunk'),
    connstr_s=('s', DEFAULT_CONNSTR, 'connection string to view_stable'),
):
    '''
    remove rd_el:srv_ra attribute
    '''

    def migrate_domain_attr(cursor, table_name, attrs_column):
        cursor.execute(
            (""" UPDATE %s SET %s = %s - 'rd_el:srv_ra'::text  WHERE %s ? 'rd_el:srv_ra'""") %
            (table_name, attrs_column, attrs_column, attrs_column))

    conn_r = psycopg2.connect(connstr_r)
    conn_t = psycopg2.connect(connstr_t)
    conn_s = psycopg2.connect(connstr_s)

    migrate_domain_attr(conn_r.cursor(), 'revision.attributes', 'contents')

    conns_v = [conn_t]
    if connstr_t != connstr_s:
        conns_v.append(conn_s)

    for conn_v in conns_v:
        cursor = conn_v.cursor()
        cursor.execute( """SELECT nspname FROM pg_namespace WHERE nspname like 'vrevisions_%';""" )
        for schema in cursor:
            print schema[0]
            migrate_domain_attr(conn_v.cursor(), schema[0]+'.objects_l', 'domain_attrs')

    if not debug:
        for conn in [conn_r] + conns_v:
            conn.commit()

@opster.command()
def migrate_local_flag(
    debug=('d', False, 'dry run mode (transaction will be rolled back)'),
    verbose=('v', False, 'verbose mode'),
    connstr=('c', DEFAULT_CONNSTR, 'connection string')
):
    '''
    clear *_nm:is_local flag for {ru} names of objects with local {be,kk,ab,os,ky} names
    '''

    def update_objects_attr(conn, objects_to_update):
        for object_id, attr in  objects_to_update.iteritems():
            new_attr_id, old_attr_id = attr
            cur = conn.cursor()
            cur.execute(
                """UPDATE revision.object_revision_without_geometry
                SET attributes_id={new_attr}
                WHERE object_id = {object} AND attributes_id={old_attr}
                ;""".format(new_attr=new_attr_id, old_attr=old_attr_id, object=object_id))

    def select_local_names(cursor, category, language):
        cursor.execute(
            """SELECT DISTINCT object_id, attributes.id, attributes.contents,
                    (SELECT count(distinct object_id) FROM revision.object_revision_without_geometry
                    WHERE attributes_id=attributes.id)
                FROM revision.object_revision_without_geometry, revision.attributes
                WHERE deleted=false AND next_commit_id=0
                AND attributes_id=id
                AND contents?'{cat}:is_local'
                AND contents->'{cat}:lang' = 'ru'
                AND object_id IN (
                    SELECT distinct slave_object_id /*all names(and other slaves) of lang_LOCAL objects*/
                    FROM revision.object_revision_relation
                    WHERE deleted=false AND next_commit_id=0
                    AND master_object_id IN (
                        SELECT distinct master_object_id /*lang_LOCAL objects*/
                        FROM revision.object_revision_relation
                        WHERE deleted=false AND next_commit_id=0
                            AND slave_object_id IN (
                            SELECT object_id /*lang_LOCAL names*/
                            FROM revision.object_revision_without_geometry
                            WHERE deleted=false AND next_commit_id=0
                            AND attributes_id IN (
                                SELECT id /*lang_LOCAL attributes*/
                                FROM revision.attributes
                                WHERE contents?'{cat}:is_local'
                                AND contents->'{cat}:lang' = '{lang}'
            ))));""".format(cat=category, lang=language))

    def fix_local_flag(conn, category, language, debug):
        cursor = conn.cursor()
        select_local_names(cursor, category, language)

        attrs_to_fix = {}  #attr_id->(object_id, new_contents, count)
        attrs_count = {}  #attr_id->n
        for record in cursor:
            object_id = record[0]
            attr_id = record[1]
            contents = parse_hstore(record[2])
            count = record[3]
            del contents['%s:is_local' % category]
            if attr_id in attrs_count:
                attrs_count[attr_id] += 1
            else:
                attrs_count[attr_id] = 1
            attrs_to_fix[object_id] = (attr_id, contents, count)

        nm_attrs_to_create = {} #attr_id->new_attr_id, contents
        nm_attrs_to_update = {} #attr_id->contents
        nm_objects_to_update = {} #object_id->new_attr_id, old_attr_id
        for object_id, attr in attrs_to_fix.iteritems():
            attr_id, contents, count = attr
            if count > attrs_count[attr_id]:
                if not attr_id in nm_attrs_to_create:
                    new_attr_id = next_attr_id(conn)
                    nm_attrs_to_create[attr_id] = (new_attr_id, contents)
                    if verbose:
                        print 'new attr: (%s, %s, %s)' %(attr_id, new_attr_id, contents)
                new_attr_id, contents = nm_attrs_to_create[attr_id]
                nm_objects_to_update[object_id] = (new_attr_id, attr_id)
                if verbose:
                    print 'fix object: (%s, %s, %s)', object_id, new_attr_id, contents
            else:
                nm_attrs_to_update[attr_id] = contents
                if verbose:
                    print 'fix attr: (%s, %s)'%(attr_id, contents)

        create_attrs(conn, nm_attrs_to_create)
        update_attrs(conn, nm_attrs_to_update)
        update_objects_attr(conn, nm_objects_to_update)


    languages = ['os', 'ab', 'ky', 'kk', 'be']
    categories = [
        'ad_nm', 'addr_nm', 'rd_nm', 'hydro_nm', 'relief_nm', 'vegetation_nm', 'poi_nm',
        'urban_nm', 'urban_roadnet_nm', 'transport_airport_nm', 'transport_metro_nm',
        'transport_railway_nm', 'transport_nm', 'transport_waterway_nm']

    for language in languages:
        for category in categories:
            conn = psycopg2.connect(connstr)
            fix_local_flag(conn, category, language, debug)
            if not debug:
                conn.commit()

@opster.command()
def migrate_restore_local_flag(
    debug=('d', False, 'dry run mode (transaction will be rolled back)'),
    verbose=('v', False, 'verbose mode'),
    connstr=('c', DEFAULT_CONNSTR, 'connection string')
):
    '''
    restore *_nm:is_local flag for {ru} names shared with objects with local {be,kk,ab,os,ky} names
    '''

    def select_restoring_name_attrs(cursor, category, language):
        query= """SELECT DISTINCT attributes.id, attributes.contents
                FROM revision.object_revision_without_geometry, revision.attributes
                WHERE attributes_id=id
                AND NOT contents?'{cat}:is_local'
                AND contents->'{cat}:lang' = 'ru'
                AND object_id IN (
                    SELECT distinct slave_object_id /*all names(and other slaves) of lang_LOCAL objects*/
                    FROM revision.object_revision_relation
                    WHERE master_object_id IN (
                        SELECT distinct master_object_id /*lang_LOCAL objects*/
                        FROM revision.object_revision_relation
                        WHERE slave_object_id IN (
                            SELECT object_id /*lang_LOCAL names*/
                            FROM revision.object_revision_without_geometry
                            WHERE attributes_id IN (
                                SELECT id /*lang_LOCAL attributes*/
                                FROM revision.attributes
                                WHERE contents?'{cat}:is_local'
                                AND contents->'{cat}:lang' = '{lang}'
            ))))
            UNION
            SELECT DISTINCT attributes.id, attributes.contents
            FROM revision.object_revision_without_geometry, revision.attributes
            WHERE attributes_id=id
            AND NOT contents?'{cat}:is_local'
            AND contents->'{cat}:lang' = 'ru'
            AND object_id IN (
                SELECT distinct slave_object_id /*all names(and other slaves) of lang_LOCAL objects*/
                FROM revision.object_revision_relation
                WHERE master_object_id IN (
                    SELECT distinct master_object_id /*lang_LOCAL objects*/
                    FROM revision.object_revision_relation
                    WHERE slave_object_id IN (
                        SELECT object_id /*lang_LOCAL names*/
                        FROM revision.object_revision_without_geometry
                        WHERE (deleted=true OR next_commit_id>0)
                        AND attributes_id IN (
                            SELECT id /*lang_LOCAL attributes*/
                            FROM revision.attributes
                            WHERE contents?'{cat}:is_local'
                            AND contents->'{cat}:lang' = '{lang}'
                )))
                AND master_object_id NOT IN (
                    SELECT distinct master_object_id /*lang_LOCAL objects*/
                    FROM revision.object_revision_relation
                    WHERE slave_object_id IN (
                        SELECT object_id /*lang_LOCAL names*/
                        FROM revision.object_revision_without_geometry
                        WHERE deleted=false AND next_commit_id=0
                        AND attributes_id IN (
                            SELECT id /*lang_LOCAL attributes*/
                            FROM revision.attributes
                            WHERE contents?'{cat}:is_local'
                            AND contents->'{cat}:lang' = '{lang}'
            ))));""".format(cat=category, lang=language)
        cursor.execute(query)
        if verbose:
            print 'found in lang=%s, cat=%s: %s'%(language, category, cursor.rowcount)

    def restore_local_flag(conn, category, language, debug):
        cursor = conn.cursor()
        select_restoring_name_attrs(cursor, category, language)

        nm_attrs_to_update = {} #attr_id->contents
        for record in cursor:
            attr_id = record[0]
            contents = parse_hstore(record[1])
            contents['%s:is_local' % category] = '1'
            nm_attrs_to_update[attr_id] = contents
            if verbose:
                print 'fix attr: (%s, %s)'%(attr_id, contents)

        update_attrs(conn, nm_attrs_to_update)


    languages = ['os', 'ab', 'ky', 'kk', 'be']
    categories = [
        'ad_nm', 'addr_nm', 'rd_nm', 'hydro_nm', 'relief_nm', 'vegetation_nm', 'poi_nm',
        'urban_nm', 'urban_roadnet_nm', 'transport_airport_nm', 'transport_metro_nm',
        'transport_railway_nm', 'transport_nm', 'transport_waterway_nm']

    for language in languages:
        for category in categories:
            conn = psycopg2.connect(connstr)
            restore_local_flag(conn, category, language, debug)
            if not debug:
                conn.commit()

@opster.command()
def migrate_speed_cat(
    debug=('d', False, 'dry run mode (transaction will be rolled back)'),
    connstr_r=('r', DEFAULT_CONNSTR, 'connection string to revision database'),
    connstr_t=('t', DEFAULT_CONNSTR, 'connection string to view_trunk'),
    connstr_s=('s', DEFAULT_CONNSTR, 'connection string to view_stable'),
):
    '''
    reset rd_el:speed_cat to default value (undefined)
    '''

    def migrate_domain_attr(cursor, table_name, attrs_column):
        cursor.execute(
            (""" UPDATE %s SET %s = %s || hstore('rd_el:speed_cat','0') WHERE %s -> 'rd_el:fc'='8'""") %
            (table_name, attrs_column, attrs_column, attrs_column))

    conn_r = psycopg2.connect(connstr_r)
    conn_t = psycopg2.connect(connstr_t)
    conn_s = psycopg2.connect(connstr_s)

    migrate_domain_attr(conn_r.cursor(), 'revision.attributes', 'contents')

    conns_v = [conn_t]
    if connstr_t != connstr_s:
        conns_v.append(conn_s)

    for conn_v in conns_v:
        cursor = conn_v.cursor()
        cursor.execute( """SELECT nspname FROM pg_namespace WHERE nspname like 'vrevisions_%';""" )
        for schema in cursor:
            print schema[0]
            migrate_domain_attr(conn_v.cursor(), schema[0]+'.objects_l', 'domain_attrs')

    if not debug:
        for conn in [conn_r] + conns_v:
            conn.commit()


if __name__ == '__main__':
    exit(opster.dispatch())
