-
Notifications
You must be signed in to change notification settings - Fork 3
/
inegi_sql.py
executable file
·199 lines (171 loc) · 6.9 KB
/
inegi_sql.py
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#!/usr/bin/python
"""Parser INEGI
Recibe un TSV de informacion del INEGI y lo transmite hacia
una base de datos PostgreSQL.
http://www3.inegi.org.mx/sistemas/descarga/default.aspx?c=28088
Los nombres de archivos y directorios estan basados en la convencion de nombres
especificada por INEGI
"""
__author__ = "@rafaelcr (Rafael Cardenas)"
import csv
import psycopg2
import subprocess
import sys
class INEGIParser(object):
def __init__(self, database, user, host, directory):
self.path = directory.strip('/')
self.entidad = self.path.split('/')[-1:][0][:-3]
self.dbconfigure(database, user, host)
self.parse()
self.dbclose()
def dbconfigure(self, database, user, host):
self.sqlconn = psycopg2.connect("dbname=%s user=%s host=%s"
% (database, user, host))
self.sqlconn.autocommit = True
self.sql = self.sqlconn.cursor()
self.sql.execute("CREATE TABLE IF NOT EXISTS entidad (\
id varchar(2) PRIMARY KEY,\
nombre varchar);")
self.sql.execute("CREATE TABLE IF NOT EXISTS municipio (\
entidad varchar(2) references entidad(id),\
id varchar(3),\
nombre varchar,\
PRIMARY KEY (entidad, id));")
self.sql.execute("CREATE TABLE IF NOT EXISTS indicador (\
id bigint PRIMARY KEY,\
descripcion varchar,\
notas varchar);")
self.sql.execute("CREATE TABLE IF NOT EXISTS valor (\
indicador bigint references indicador(id),\
municipio varchar(3),\
entidad varchar(2),\
anio integer,\
valor numeric(20,5),\
unidades varchar,\
fuente varchar,\
PRIMARY KEY (indicador, municipio, entidad, anio));")
self.sql.execute("CREATE TABLE IF NOT EXISTS categoria (\
id serial,\
nombre varchar PRIMARY KEY,\
parent integer);")
def dbclose(self):
self.sql.close()
self.sqlconn.close()
def parse(self):
# el csv de nacional no trae las columnas de los anios como las estatales,
# y no son provistos en ningun archivo
anio = [1895,1900,1910,1921,1930,1940,1950,1960,1970,1980,1981,1982,1983,
1984,1985,1986,1987,1988,1989,1990,1991,1992,1993,1994,1995,1996,1997,
1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010]
# determinar encoding de csv. algunos malamente no vienen en utf-8
fenc = subprocess.Popen(["file","--mime-encoding",
"%s/%sValor.tsv" % (self.path, self.entidad)], stdout=subprocess.PIPE)
self.encoding = fenc.stdout.read().split(':')[1].strip()
print 'Parseando archivos para entidad %s...' % (self.entidad)
print "Procesando CSV 1/4..."
with open("%s/%sValor.tsv" % (self.path, self.entidad)) as inegi_tsv:
for l, line in enumerate(csv.reader(inegi_tsv, dialect="excel-tab")):
if l == 0 and (not line[0].isdigit()): # corregir si viene con columnas
continue
self.wentidad(line[0],line[1])
self.wmunicipio(line[0],line[2],line[3])
self.windicador(line[7],line[8])
self.wcategoria(line[4],"")
self.wcategoria(line[5],line[4])
self.wcategoria(line[6],line[5])
for i, a in enumerate(line[9:]):
if a:
self.wvalor(line[7],line[2],line[0],anio[i],a)
print "Procesando CSV 2/4..."
with open("%s/%sNotas.tsv" % (self.path, self.entidad)) as inegi_tsv:
for l, line in enumerate(csv.reader(inegi_tsv, dialect="excel-tab")):
if l == 0: # ignorar nombres de columnas
continue
# TODO: hay lineas que empiezan sin ID, son linebreaks de la anterior.
# ademas hay algunos IDs repetidos que su desc debe ser concatenada
if line[0].isdigit():
self.wnota(line[0],line[2])
print "Procesando CSV 3/4..."
with open("%s/%sUnidadMedida.tsv" % (self.path, self.entidad)) as inegi_tsv:
for l, line in enumerate(csv.reader(inegi_tsv, dialect="excel-tab")):
if l == 0 and (not line[0].isdigit()):
continue
for i, a in enumerate(line[9:]):
if a:
self.wunidades(line[7],line[2],line[0],anio[i],a)
print "Procesando CSV 4/4..."
with open("%s/%sFuente.tsv" % (self.path, self.entidad)) as inegi_tsv:
for l, line in enumerate(csv.reader(inegi_tsv, dialect="excel-tab")):
if l == 0 and (not line[0].isdigit()):
continue
for i, a in enumerate(line[9:]):
if a:
self.wfuente(line[7],line[2],line[0],anio[i],a)
def strdecode(self, s):
if not self.encoding == "utf-8":
return s.decode("latin1")
else:
return s
def wvalor(self, indicador, municipio, entidad, anio, valor):
self.sql.execute("INSERT INTO valor \
(indicador,municipio,entidad,anio,valor) VALUES (%s,%s,%s,%s,%s);",
(int(indicador),municipio,entidad,anio,float(valor)))
def wunidades(self, indicador, municipio, entidad, anio, unidades):
self.sql.execute("UPDATE valor SET unidades=%s WHERE indicador=%s \
AND municipio=%s AND entidad=%s AND anio=%s;",
(self.strdecode(unidades),int(indicador),municipio,entidad,anio))
def wfuente(self, indicador, municipio, entidad, anio, fuente):
self.sql.execute("UPDATE valor SET fuente=%s WHERE indicador=%s \
AND municipio=%s AND entidad=%s AND anio=%s;",
(self.strdecode(fuente).strip(),int(indicador),municipio,entidad,anio))
def wentidad(self, eid, nombre):
try:
self.sql.execute("INSERT INTO entidad (id,nombre) VALUES (%s,%s);",
(eid,self.strdecode(nombre)))
except Exception, e:
pass
def wmunicipio(self, entidad, mid, nombre):
try:
self.sql.execute("INSERT INTO municipio (entidad,id,nombre) \
VALUES (%s,%s,%s);", (entidad,mid,self.strdecode(nombre)))
except Exception, e:
pass
def windicador(self, iid, descripcion):
try:
self.sql.execute("INSERT INTO indicador (id,descripcion) VALUES \
(%s,%s);", (int(iid),self.strdecode(descripcion)))
except Exception, e:
pass
def wnota(self, iid, notas):
self.sql.execute("UPDATE indicador SET notas=%s WHERE id=%s;",
(self.strdecode(notas),int(iid)))
def wcategoria(self, nombre, parent):
if nombre:
self.sql.execute("SELECT * FROM categoria WHERE nombre=%s",
(self.strdecode(nombre),))
if not self.sql.fetchone():
if parent:
self.sql.execute("SELECT * FROM categoria WHERE nombre=%s",
(self.strdecode(parent),))
parent = int(self.sql.fetchone()[0])
else:
parent = 0
self.sql.execute("INSERT INTO categoria (nombre,parent) VALUES \
(%s,%s);", (self.strdecode(nombre),parent))
def main():
args = sys.argv[1:]
usage = "usage: inegi_sql.py -d[database] -u[user] -h[host] [directories...]"
if len(args) < 4:
print usage
sys.exit(1)
database = args[0][2:]
del args[0]
user = args[0][2:]
del args[0]
host = args[0][2:]
del args[0]
print '* Parser INEGI *'
for directory in args:
p = INEGIParser(database, user, host, directory)
if __name__ == '__main__':
main()