def test(self): generador = GeneradorDeFechaSecuencial(datetime.date(2001, 1, 1), datetime.date(2001, 1, 2)) self.assertEqual(generador.generar(), ("2001-01-01", "2001", "1", "1")) self.assertEqual(generador.generar(), ("2001-01-02", "2001", "1", "2")) self.assertEqual(generador.generar(), ("2001-01-03", "2001", "1", "3")) self.assertEqual(generador.calcular_count(), 2) self.assertRaises(AssertionError, GeneradorDeFechaSecuencial, datetime.date(2001, 1, 1), datetime.date(2000, 1, 1))
def generar_todos(filename_prefix, cant_tarjetas, cant_comercios, cant_cupones): filename_tarjetas_personas = '/tmp/' + filename_prefix + '_tarjeta_con_persona.csv' filename_comercios = '/tmp/' + filename_prefix + '_comercios.csv' filename_fechas = '/tmp/' + filename_prefix + '_fechas.csv' filename_cupones = '/tmp/' + filename_prefix + '_cupones.csv' filename_sql = '/tmp/' + filename_prefix + '.sql' #=========================================================================== # Generamos comercios #=========================================================================== # # Un comercio tiene varios datos: # -id # - codigo de comercio # - razon social # - rubro # # Usamos un `Generador` para generar cada uno de estos datos: Ej: # - GeneradorDeEnteroSecuencial: numero secuencial (para PRIMARY KEY) # - GeneradorDeEntero: generara un entero aleatorio UNICO # - GeneradorDeRazonSocial: generara 2 palabras aleatorios, y le agregara "S.A.", "S.R.L.", etc # - GeneradorDeOpcionPreestablecida: devolvera aleatoriamente alguna de las opciones # pasadas por parametros (en este caso, devolvera aleatoriamente alguno de los rubros preestablecidos) # multigenerador = MultiGenerador(( GeneradorDeEnteroSecuencial(seed=1), GeneradorDeEntero(10000000, 99999999, unique=True), GeneradorDeRazonSocial(), # razon social GeneradorDeOpcionPreestablecida(opciones=RUBROS), )) # El archivo CSV debe tener un encabezado... headers_csv = ( "id", "numero_de_comercio", "razon_social", "rubro", ) # Creamos instancia de `ArchivoCSV`, para luego generar los datos aleatorios archivo_csv = ArchivoCSV(multigenerador, headers_csv) # La cantidad de filas a generar depende de `cant_comercios` logger.info("Iniciando generacion de comercios...") archivo_csv.generar_csv(filename_comercios, cant_comercios) # Liberamos recursos... archivo_csv.close() del multigenerador, headers_csv, archivo_csv #=========================================================================== # Generamos fechas #=========================================================================== logger.info("Iniciando generacion de fechas...") fecha_desde = datetime.date(year=2001, month=1, day=1) fecha_hasta = datetime.date(year=2011, month=12, day=31) # Creamos 'generador' de fechas antes que nada, porque mas adelante # necesitaremos tener una referencia a este objeto... generador_fechas = GeneradorDeFechaSecuencial(fecha_desde, fecha_hasta) # Los elementos del encabezado serán: headers_csv = ( "id", "fecha", # <PK> "anio", "mes", "dia", ) # Cada fila del CSV de fecha debe tener MULTIPLES columnas, por eso usamos `MultiGenerador` # Un `MultiGenerador` es un contenedor de `Generadores` # 1er columna. ID -> lo generamos con `GeneradorDeEnteroSecuencial()` # 2da, 3ra, 4ta, 5ta columna -> fecha, año, mes y dia: lo genera `GeneradorDeFechaSecuencial()` multigenerador = MultiGenerador( ( GeneradorDeEnteroSecuencial(seed=1), generador_fechas, ) ) # Creamos instancia de `ArchivoCSV`, para luego generar los datos aleatorios archivo_csv = ArchivoCSV(multigenerador, headers_csv) # En este caso, no sabemos la cantidad de registros a crear, # sino que quremos crear desde la fecha `fecha_desde` hasta la fecha `fecha_hasta`... # El calculo de cuantas fechas hay en ese rango, usamos `generador_fechas.calcular_count()` archivo_csv.generar_csv(filename_fechas, generador_fechas.calcular_count()) # Liberamos recursos... archivo_csv.close() del multigenerador, headers_csv, archivo_csv, generador_fechas, fecha_desde, fecha_hasta #=========================================================================== # Generamos tarjeta + personas #=========================================================================== logger.info("Iniciando generacion de tarjetas (con infor de personas)...") multigenerador = MultiGenerador(( GeneradorDeEntero(100000000000, 999999999999, unique=True), # numero_de_tarjeta GeneradorDeEntero(1000000, 9999999, unique=True), # numero_de_cuenta GeneradorDePalabrasEspaniol(seed=0, cant_palabras_default=2), # nombre, apellido GeneradorDePalabrasEspaniol(seed=0, cant_palabras_default=1), # barrio GeneradorDeBarrioCiudadProvincia(), # ciudad, provincia )) headers_csv = ( # "id", -> es generado al crear el archivo: parametro `generar_id` de `generar_csv()` "numero_de_tarjeta", # <PK> "numero_de_cuenta", "nombre", "apellido", "barrio", "ciudad", "provincia", ) archivo_csv = ArchivoCSV(multigenerador, headers_csv) # En este caso, queremos aprovechar los multiples procesadores que posee el hardware, # por lo tanto creamos una instancia de `AdaptadorMultiproceso`, para que los datos # del archivo CSV sean generados en paralelo adaptador_multiproceso = AdaptadorMultiproceso(archivo_csv, multiprocessing.cpu_count()) # Como queremos que exista un identificador secuencial, usamos `generar_id`. Esto # hace que se cree una columa `id`, numercia, incremental, que comienza en `1`. adaptador_multiproceso.generar_csv(filename_tarjetas_personas, cant_tarjetas, generar_id=True) adaptador_multiproceso.close() del adaptador_multiproceso, multigenerador, headers_csv, archivo_csv # Ahora cargamos los `ID` de los objetos creados en los pasos anteriores # Para esto utilizamos `GeneradorDeItemDesdeCsv`, que lee los "ID" de cada una de las # filas y los guarda en memoria, para ir usandolos aleatoriamente. # # Solo guardamos el ID, ya que es lo unico que nos hace falta para generar la tabla de hechos # El metodo `callback_xxx()` recibe un array con los elementos de cada fila leida del CSV; # y debe devolver el elemento que queremos utilizar #=========================================================================== # Dict con tarjetas generadas #=========================================================================== def callback_fecha(obj): return obj[0] dict_fechas = GeneradorDeItemDesdeCsv(filename_fechas, callback_fecha) #=========================================================================== # Dict con tarjetas generadas #=========================================================================== def callback_tarjeta(obj): return obj[0] dict_tarjetas = GeneradorDeItemDesdeCsv(filename_tarjetas_personas, callback_tarjeta) #=========================================================================== # Dict con comercios generados #=========================================================================== def callback_comercio(obj): return obj[0] dict_comercios = GeneradorDeItemDesdeCsv(filename_comercios, callback_comercio) #=========================================================================== # Generamos cupones #=========================================================================== logger.info("Iniciando generacion de cupones...") # Generadores de datos a utilizar... generadores = ( dict_fechas, dict_tarjetas, dict_comercios, GeneradorDeFloat(20, 2000, seed=0).set_formateador_2_decimales(), GeneradorDeEntero(0, 9999999), ) # Encabezado del archivo CSV... headers_csv = ( "id_fecha", # <FK> "id_tarjeta", # <FK> "id_comercio", # <FK> "monto", # HECHO "numero_cupon", ) multigenerador = MultiGenerador(generadores) archivo_csv = ArchivoCSV(multigenerador, headers_csv) # Aprovechamos multiples procesadores... generador_cupones = AdaptadorMultiproceso( archivo_csv, multiprocessing.cpu_count(), ) generated_filenames_list = [] # En este caso, usamos el metodo `generar_multiples_csv_concurrentes()` # Este metodo posee una performance mucho mayor. Cada proceso generará su propio archivo CSV # (y por lo tanto, tantos archivos como procesos concurrentes se ejecuten) generador_cupones.generar_multiples_csv_concurrentes( filename_cupones, generated_filenames_list, cant_cupones ) generador_cupones.close() del generador_cupones # ----- FIN ---- # Para facilitar la carga de los datos en PostgreSql, aprovechamos # para generar un script. Pero esto no utiliza nada de la "API" de utenabi... #=========================================================================== # Generamos archivo SQL #=========================================================================== # # Para facilitar la carga de estos datos, aqui mismo generamos un archivo SQL # que incluya la estructura de la BD y haga la carga de los datos teniendo # en cuenta los nombres de los archivos generados. # sql = """ -- # -- # Cambios en configuracion por default: -- # -- # + PERFORMANCE: http://www.postgresql.org/docs/current/static/populate.html -- # + NON-DURABILITY: http://www.postgresql.org/docs/current/static/non-durability.html -- # -- # fsync = off -- # full_page_writes = off -- # checkpoint_segments = 30 -- # autovacuum = off -- # -- # http://www.postgresql.org/docs/current/static/runtime-config-resource.html#GUC-MAINTENANCE-WORK-MEM set maintenance_work_mem = '512MB'; DROP TABLE IF EXISTS th_cupones; DROP TABLE IF EXISTS d_tarjeta; DROP TABLE IF EXISTS d_fecha; DROP TABLE IF EXISTS d_comercio; -- -- FECHA -- CREATE TABLE d_fecha ( id integer PRIMARY KEY, fecha char(10) not null, anio integer not null, mes integer not null, dia integer not null ); select now(); COPY d_fecha from '%(archivo_csv_d_fecha)s' CSV HEADER; select now(); CREATE INDEX ON d_fecha (anio); select now(); CREATE INDEX ON d_fecha (mes); select now(); CREATE INDEX ON d_fecha (dia); select now(); -- -- TARJETAS -- CREATE TABLE d_tarjeta ( id integer PRIMARY KEY, numero_de_tarjeta bigint not null, numero_de_cuenta integer not null, nombre char(40) not null, apellido char(40) not null, barrio char(40) not null, ciudad char(40) not null, provincia char(40) not null ); select now(); COPY d_tarjeta from '%(archivo_csv_d_tarjeta)s' CSV HEADER; select now(); CREATE INDEX ON d_tarjeta (barrio); select now(); CREATE INDEX ON d_tarjeta (ciudad); select now(); CREATE INDEX ON d_tarjeta (provincia); select now(); -- -- COMERCIOS -- CREATE TABLE d_comercio ( id integer PRIMARY KEY, numero_de_comercio integer not null, razon_social char(60) not null, rubro char(40) not null ); select now(); COPY d_comercio from '%(archivo_csv_d_comercio)s' CSV HEADER; select now(); CREATE INDEX ON d_comercio (rubro); select now(); -- -- CUPONES -- CREATE TABLE th_cupones ( id_fecha integer not null, id_tarjeta integer not null, id_comercio integer not null, monto float not null, numero_cupon integer not null ); select now(); """ % { 'archivo_csv_d_fecha': filename_fechas, 'archivo_csv_d_tarjeta': filename_tarjetas_personas, 'archivo_csv_d_comercio': filename_comercios, } for a_filename in generated_filenames_list: sql += """ COPY th_cupones from '{0}' CSV HEADER; select now(); """.format(a_filename) sql += """ CREATE INDEX ON th_cupones (id_fecha); select now(); CREATE INDEX ON th_cupones (id_tarjeta); select now(); CREATE INDEX ON th_cupones (id_comercio); select now(); ALTER TABLE th_cupones ADD CONSTRAINT cupones_fecha_fk FOREIGN KEY (id_fecha) REFERENCES d_fecha(id); select now(); ALTER TABLE th_cupones ADD CONSTRAINT cupones_tarjeta_fk FOREIGN KEY (id_tarjeta) REFERENCES d_tarjeta(id); select now(); ALTER TABLE th_cupones ADD CONSTRAINT cupones_comercio_fk FOREIGN KEY (id_comercio) REFERENCES d_comercio(id); select now(); """ logger.info("Guardando SQL en {0}...".format(filename_sql)) with open(filename_sql, 'w') as sql_f: sql_f.write(sql)