commit 5f508c961494cb93003b230621e5a7706f450416
parent 6b416dc25a00a138a8d458d092ec067917d62755
Author: klewer-martin <martin.cachari@gmail.com>
Date: Thu, 29 Jul 2021 16:43:34 -0300
Updated source code and input file generator
Diffstat:
10 files changed, 239 insertions(+), 86 deletions(-)
diff --git a/Makefile b/Makefile
@@ -51,6 +51,9 @@ run5k:
run50k:
./main -fmt csv -out output.csv -in examples/test_file_50k.csv -ti 1320498000 -tf 1420529000
+run100k:
+ ./main -fmt csv -out output.csv -in examples/test_file_100k.csv -ti 1320498000 -tf 1420529000
+
run500k:
./main -fmt csv -out output.csv -in examples/test_file_500k.csv -ti 1320498000 -tf 1420529000
diff --git a/examples/input_gen.py b/examples/input_gen.py
@@ -2,7 +2,7 @@ from string import digits
from time import strftime, gmtime
from random import randint, choice
-LINES = 5
+LINES = 100000
# OUTPUT:
# ID_TRANSACCION, ID_USUARIO, FECHA, MONTO, NUMERO DE TRAJETA, DESCRIPCION
@@ -10,19 +10,99 @@ LINES = 5
descs = [ "Compras supermercado", "Pago tarjeta", "Compras libreria", "Pago Mecanico", "Pago Dentista", "Pago Servicios online", "Compras Ferreteria", "Compras Accesorios Informatica", "Compras farmacia", "Ventas online", "Extraccion cajero" ]
-def card_number_generator():
- return ''.join(choice(digits) for i in range(1, 16))
+def calculate_luhn(cc):
+ num = list(map(int, str(cc)))
+ check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
+ return 0 if check_digit == 10 else check_digit
+
+def generate_card(type):
+ """
+ Prefill some values based on the card type
+ """
+ card_types = ["americanexpress","visa13", "visa16","mastercard","discover"]
+
+ def prefill(t):
+ # typical number of digits in credit card
+ def_length = 16
+
+ """
+ Prefill with initial numbers and return it including the total number of digits
+ remaining to fill
+ """
+ if t == card_types[0]:
+ # american express starts with 3 and is 15 digits long
+ # override the def lengths
+ return [3, randint(4,7)], 13
+
+ elif t == card_types[1] or t == card_types[2]:
+ # visa starts with 4
+ if t.endswith("16"):
+ return [4], def_length - 1
+ else:
+ return [4], 12
+
+ elif t == card_types[3]:
+ # master card start with 5 and is 16 digits long
+ return [5, randint(1,5)], def_length - 2
+
+ elif t == card_types[4]:
+ # discover card starts with 6011 and is 16 digits long
+ return [6, 0, 1, 1], def_length - 4
+
+ else:
+ # this section probably not even needed here
+ return [], def_length
+
+ def finalize(nums):
+ """
+ Make the current generated list pass the Luhn check by checking and adding
+ the last digit appropriately bia calculating the check sum
+ """
+ check_sum = 0
+
+ #is_even = True if (len(nums) + 1 % 2) == 0 else False
+
+ """
+ Reason for this check offset is to figure out whether the final list is going
+ to be even or odd which will affect calculating the check_sum.
+ This is mainly also to avoid reversing the list back and forth which is specified
+ on the Luhn algorithm.
+ """
+ check_offset = (len(nums) + 1) % 2
+
+ for i, n in enumerate(nums):
+ if (i + check_offset) % 2 == 0:
+ n_ = n*2
+ check_sum += n_ -9 if n_ > 9 else n_
+ else:
+ check_sum += n
+ return nums + [10 - (check_sum % 10) ]
+
+ # main body
+ t = type.lower()
+ if t not in card_types:
+ print("Unknown type: '%s'" % type)
+ print("Please pick one of these supported types: %s" % card_types)
+ return
+
+ initial, rem = prefill(t)
+ so_far = initial + [randint(1,9) for x in range(rem - 1)]
+ return ("".join(map(str,finalize(so_far))))
def generate_file(max_lines):
id_transaction_base = 123400
id_user_base = 1
- id_user_max = 20000
+ id_user_max = 30000
amount_base = 1
amount_max = 10000
for i in range(max_lines):
id_transaction = (i + id_transaction_base)
id_user = randint(id_user_base, id_user_max)
- card_nr = card_number_generator()
+
+ card_nr = generate_card("visa16")
+ while calculate_luhn(card_nr) != 0:
+ card_nr = generate_card("visa16")
+
date = strftime("%d.%m.%Y %H:%M:%S", gmtime(1320487200 + i))
j = randint(1, len(descs) - 1)
diff --git a/include/status.h b/include/status.h
@@ -5,10 +5,11 @@
#include <stdlib.h>
#include <stdarg.h>
-#define ERROR_RETRY_MSG "Verifique y vuelva a intentar"
-#define EXIT_SUCCESS_MSG "Ejecución terminada exitosamente"
-#define USERS_REGISTERED_MSG "Usuarios registrados: "
-#define PROCESED_LINES_MSG "Lineas procesadas: "
+#define MSG_ERROR_RETRY "Verifique y vuelva a intentar"
+#define MSG_EXIT_SUCCESS "Ejecución terminada exitosamente"
+#define MSG_USERS_REGISTERED "Usuarios registrados"
+#define MSG_PROCESED_LINES "Lineas procesadas"
+#define STR_INVALID_CARD_NUMBER "Número no válido"
#define STATUS_T_MAX 12
@@ -38,7 +39,6 @@ typedef enum {
} csv_pos_t;
void show_status(status_t st);
-void free_arrays(size_t num,...);
extern const char *status_strings[STATUS_T_MAX];
diff --git a/include/utils.h b/include/utils.h
@@ -4,6 +4,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stdbool.h>
#include "cla.h"
#include "user.h"
@@ -18,6 +19,11 @@
#define IN_FILE_FIELDS 6
#define IN_FILE_FIELDS_MAX_LEN 50
+#define STR_FMT_CSV "csv"
+#define STR_FMT_XML "xml"
+
+#define CARD_NO_VALID_LEN 16
+
#define XML_STR_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
#define XML_STR_ROOT "root"
#define XML_STR_ROW "row"
@@ -29,8 +35,9 @@ status_t get_date(time_t *, char **);
status_t array_destroy(char **, size_t);
status_t string_split(char *, char **, char *);
+bool is_valid_card(char *);
+
void clean_array(char **);
void clean_buffer(char *);
-
#endif
diff --git a/include/vector.h b/include/vector.h
@@ -26,7 +26,7 @@ status_t ADT_Vector_add(ADT_Vector_t **, void *);
status_t ADT_Vector_destroy(ADT_Vector_t **);
status_t ADT_Vector_set(ADT_Vector_t **, void *, size_t);
-status_t ADT_Vector_print(const ADT_Vector_t *, FILE *, printer_t);
+status_t ADT_Vector_print(const ADT_Vector_t *, FILE *);
status_t ADT_Vector_sort(ADT_Vector_t *, comparator_t);
void *ADT_Vector_get_elem(const ADT_Vector_t *v, void *e);
diff --git a/source/cla.c b/source/cla.c
@@ -74,23 +74,34 @@ status_t cla_setup(int argc, char **argv, cla_t cla)
for(f = FLAG_FMT; f < FLAGS_MAX; f++) {
if(!strcmp(available_flags[f], argv[i])) {
switch (f) {
- case FLAG_FMT: strcpy(cla->fmt, argv[i + 1]); break;
- case FLAG_OUT: if((fp = fopen(argv[i + 1], "wt")) == NULL)
- return ERROR_OPENING_FILE;
-
- cla->fo = fp;
- break;
- case FLAG_IN: if((fp = fopen(argv[i + 1], "rt")) == NULL)
- return ERROR_OPENING_FILE;
-
- cla->fi = fp;
- break;
- case FLAG_TI: cla->ti = strtoul(argv[i + 1], &endptr, 10);
- if(*endptr != '\0') return ERROR_WRONG_TIME;
- break;
- case FLAG_TF: cla->tf = strtoul(argv[i + 1], &endptr, 10);
- if(*endptr != '\0') return ERROR_WRONG_TIME;
- break;
+ case FLAG_FMT:
+ strcpy(cla->fmt, argv[i + 1]);
+ break;
+
+ case FLAG_OUT:
+ if((fp = fopen(argv[i + 1], "wt")) == NULL)
+ return ERROR_OPENING_FILE;
+
+ cla->fo = fp;
+ break;
+
+ case FLAG_IN:
+ if((fp = fopen(argv[i + 1], "rt")) == NULL)
+ return ERROR_OPENING_FILE;
+
+ cla->fi = fp;
+ break;
+
+ case FLAG_TI:
+ cla->ti = strtoul(argv[i + 1], &endptr, 10);
+ if(*endptr != '\0') return ERROR_WRONG_TIME;
+ break;
+
+ case FLAG_TF:
+ cla->tf = strtoul(argv[i + 1], &endptr, 10);
+ if(*endptr != '\0') return ERROR_WRONG_TIME;
+ break;
+
default: return ERROR_FLAG_NOT_FOUND;
}
}
@@ -113,6 +124,9 @@ status_t cla_create(cla_t *cla)
return ERROR_MEMORY;
}
+ (*cla)->fi = NULL;
+ (*cla)->fo = NULL;
+
return OK;
}
@@ -120,8 +134,8 @@ status_t cla_destroy(cla_t cla)
{
if(cla == NULL) return ERROR_NULL_POINTER;
- fclose(cla->fo);
- fclose(cla->fi);
+ if(cla->fi != NULL) fclose(cla->fi);
+ if(cla->fo != NULL) fclose(cla->fo);
free(cla->fmt);
free(cla);
diff --git a/source/main.c b/source/main.c
@@ -4,11 +4,6 @@
#include "../include/status.h"
#include "../include/vector.h"
-#define IN_FILE_MAX_LEN 100
-#define IN_FILE_DELIM ","
-#define IN_FILE_FIELDS 6
-#define IN_FILE_FIELDS_MAX_LEN 50
-
int main (int argc, char *argv[])
{
status_t st;
@@ -68,6 +63,26 @@ int main (int argc, char *argv[])
return st;
}
+ /* Setea el impresor a ADT_Vector */
+ if(!strcmp(cla->fmt, STR_FMT_CSV)) {
+ if((st = ADT_Vector_set_printer(v, user_print_as_csv)) != OK) {
+ show_status(st);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
+ return st;
+ }
+ }
+ else if(!strcmp(cla->fmt, STR_FMT_XML)) {
+ if((st = ADT_Vector_set_printer(v, user_print_as_xml)) != OK) {
+ show_status(st);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
+ return st;
+ }
+ }
+
/* Crea un usuario temporal */
if((st = user_create(&user_tmp)) != OK) {
show_status(st);
@@ -81,10 +96,22 @@ int main (int argc, char *argv[])
while(fgets(buffer, IN_FILE_MAX_LEN, cla->fi)) {
/* Separa la linea leida segun un caracter delimitador */
- string_split(buffer, data, IN_FILE_DELIM);
+ if((st = string_split(buffer, data, IN_FILE_DELIM)) != OK) {
+ show_status(st);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
+ return st;
+ }
/* Setea el usuario temporal con los datos obtenidos de la linea */
- user_set_data(user_tmp, data);
+ if((st = user_set_data(user_tmp, data)) != OK) {
+ show_status(st);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
+ return st;
+ }
amount = strtol(data[POS_AMOUNT], &endptr, 10);
if(*endptr != '\0') return ERROR_CORRUPT_DATA;
@@ -96,15 +123,41 @@ int main (int argc, char *argv[])
if(epoch < cla->ti) continue;
else if(epoch > cla->tf) break;
+ /* Solo imprime en el archivo de salida las transacciones realizadas con una tarjeta valida */
+ if(!is_valid_card(data[POS_CARD_NUMBER])) {
+ fprintf(stderr, "%s: %s\n",STR_INVALID_CARD_NUMBER, data[POS_CARD_NUMBER]);
+ continue;
+ }
+
/* Busca el id del usuario en el vector */
if((user = ADT_Vector_get_elem(v, user_tmp)) != NULL) {
/* Si lo encuentra le suma el monto correspondiente */
- user_add_amount(user, amount);
+ if((st = user_add_amount(user, amount)) != OK) {
+ show_status(st);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
+ return st;
+ }
}
+
+ /* Si no lo encuentra crea un usuario nuevo */
else {
- /* Si no lo encuentra crea un usuario nuevo */
- user_create(&user);
- user_set_data(user, data);
+ if((st = user_create(&user)) != OK) {
+ show_status(st);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
+ return st;
+ }
+
+ if((st = user_set_data(user, data))) {
+ show_status(st);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
+ return st;
+ }
/* Y lo agrega al vector */
if((st = ADT_Vector_add(&v, user)) != OK){
@@ -131,25 +184,13 @@ int main (int argc, char *argv[])
}
/* Imprime el vector con los usuarios de acuerdo al argumento recibido */
- if(!strcmp(cla->fmt, "xml")) {
- if((st = ADT_Vector_export_as_xml(v, cla->fo, user_print_as_xml)) != OK) {
- show_status(st);
- free(user_tmp);
- cla_destroy(cla);
- ADT_Vector_destroy(&v);
- array_destroy(data, IN_FILE_FIELDS);
- return st;
- }
- }
- else if(!strcmp(cla->fmt,"csv")) {
- if((st = ADT_Vector_export_as_csv(v, cla->fo, user_print_as_csv)) != OK) {
- show_status(st);
- free(user_tmp);
- cla_destroy(cla);
- ADT_Vector_destroy(&v);
- array_destroy(data, IN_FILE_FIELDS);
- return st;
- }
+ if((st = ADT_Vector_print(v, cla->fo)) != OK) {
+ show_status(st);
+ free(user_tmp);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ array_destroy(data, IN_FILE_FIELDS);
+ return st;
}
free(user_tmp);
diff --git a/source/status.c b/source/status.c
@@ -18,27 +18,4 @@ const char *status_strings[] = {
void show_status(status_t st)
{
fprintf(stderr, "\n%s\n", status_strings[st]);
- fprintf(stderr, "%s\n", ERROR_RETRY_MSG);
-}
-
-void free_arrays(size_t num,...)
-{
- va_list valist;
- size_t i;
-
- va_start(valist, num);
-
- for(i = 0; i < num; i++)
- free(va_arg(valist, char *));
-}
-
-void close_streams(size_t num,...)
-{
- va_list valist;
- size_t i;
-
- va_start(valist, num);
-
- for(i = 0; i < num; i++)
- free(va_arg(valist, FILE *));
}
diff --git a/source/utils.c b/source/utils.c
@@ -5,6 +5,9 @@ status_t string_split(char *s, char **data, char *delim)
char *p, *tmp;
size_t fields = 0;
+ if(s == NULL || data == NULL || delim == NULL)
+ return ERROR_NULL_POINTER;
+
for(p = s; (tmp = strtok(p, delim)); p = NULL)
strcpy(data[fields++], tmp);
@@ -75,3 +78,29 @@ status_t get_date(time_t *e, char **data)
}
return OK;
}
+
+bool is_valid_card(char *card_no)
+{
+ size_t i, j, k;
+ int arr[4][4], sum;
+ char tmp[2];
+
+ if(card_no == NULL) return false;
+ if(strlen(card_no) != CARD_NO_VALID_LEN) return false;
+
+ for(i = 0, k = 0; i < 4; i++) {
+ for(j = 0; j < 4; j++) {
+ arr[i][j] = (card_no[k++] - '0');
+ if(j % 2) {
+ arr[i][j] *= 2;
+ sprintf(tmp, "%d", arr[i][j]);
+ if(!(strlen(tmp) - 1)) arr[i][j] = (tmp[0] - '0');
+ else arr[i][j] = ((tmp[0] - '0') + (tmp[1] - '0'));
+
+ }
+ sum += arr[i][j];
+ }
+ }
+
+ return (sum % 10) ? false : true;
+}
diff --git a/source/vector.c b/source/vector.c
@@ -108,14 +108,16 @@ void *ADT_Vector_get_elem(const ADT_Vector_t *v, void *e)
return NULL;
}
-status_t ADT_Vector_print(const ADT_Vector_t *v, FILE *fp, printer_t pf)
+status_t ADT_Vector_print(const ADT_Vector_t *v, FILE *fp)
{
+ status_t st;
size_t i;
if(v == NULL) return ERROR_NULL_POINTER;
for(i = 0; i < v->size; i++)
- (*pf)(v->a[i], fp);
+ if((st = v->printer(v->a[i], fp)) != OK)
+ return st;
putchar('\n');
return OK;