def test(self): filename = '/tmp/_test_utenabi_AdaptadorMultiprocesoTest.csv' multigen = MultiGenerador() multigen.agregar_generador(GeneradorDeEntero(0, 9999999, seed=0)) multigen.agregar_generador(GeneradorDeEntero(0, 9999999, seed=1)) multigen.agregar_generador(GeneradorDeEntero(0, 9999999, seed=2)) generador_csv = ArchivoCSV(multigen, ("num1", "num2", "num3")) adaptador_multiproceso = AdaptadorMultiproceso( generador_csv, 2 ) adaptador_multiproceso.generar_csv(filename, 1000) adaptador_multiproceso.close() lineas_totales = 0 lineas_iguales = 0 with open(filename, 'r') as thefile: csv_reader = csv.reader(thefile) csv_reader.next() for values in csv_reader: lineas_totales += 1 if values[0] == values[1] and values[1] == values[2]: lineas_iguales += 1 # 100 es 10% de los 1000 que generamos... # Suponemos `falla` si mas del 10% de las lineas generadas # poseen los 3 valores iguales self.assertTrue(lineas_iguales < 100, "{0} de {1} lineas generadas contienene los mismos valores para las distintas columnas".format( lineas_iguales, lineas_totales))
def main(): assert len(sys.argv) == 3, "Debe especificar: la cantidad de personas " \ "a crear y el nombre de archivo destino" obj_count = int(sys.argv[1]) archivo_destino = sys.argv[2] assert not os.path.exists(archivo_destino), \ "El archivo destino ya existe... por las dudas salimos..." logging.info("Lanzando %s procesos concurrentes", multiprocessing.cpu_count()) multigenerador = MultiGenerador(( # tipo de doc -> dni, le, lc GeneradorDeOpcionPreestablecida(opciones=('dni', 'le', 'lc'), seed=0), # numero de documento (UNICO, NO GENERA REPETIDOS) GeneradorDeNroDocumento(seed=0, unique=True), # nombre -> 2 palabras españolas aleatorias GeneradorDePalabrasEspaniol(seed=0, cant_palabras_default=2), # apellido -> 1 palabra español aleatoria GeneradorDePalabrasEspaniol(seed=0, cant_palabras_default=1), # barrio, ciudad, provincia GeneradorDeBarrioCiudadProvincia(), # codigo postal GeneradorDeCP(), # fecha de nacimiento GeneradorDeFecha(seed=0), )) headers_csv = ( "tipo_doc", "nro_doc", "nombre", "apellido", "barrio", "ciudad", "provincia", "cp", "fecha_nacimiento", ) # Siempre necesitamos un "ArchivoCSV", ya sea que usemos multiples procesos o no. # Si usamos multiples procesos, encapsulamos la instancia de Archivo CSV en AdaptadorMultiproceso generador_csv = ArchivoCSV(multigenerador, headers_csv) if multiprocessing.cpu_count() > 1: # # Si el hardware posee mas de 1 nucleo, los aprovechamos usando AdaptadorMultiproceso # logging.info("Iniciando AdaptadorMultiproceso") adaptador_multiproceso = AdaptadorMultiproceso( generador_csv, multiprocessing.cpu_count() ) adaptador_multiproceso.generar_csv(archivo_destino, obj_count) adaptador_multiproceso.close() else: # # Si el hardware posee 1 solo nucleo, utilizamos ArchivoCSV para generar el archivo # logging.info("Iniciando ArchivoCSV") generador_csv.generar_csv(archivo_destino, obj_count) generador_csv.close()
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)