diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..73f69e0
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..a55e7a1
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/contabilidad.iml b/.idea/contabilidad.iml
new file mode 100644
index 0000000..8558fe5
--- /dev/null
+++ b/.idea/contabilidad.iml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..2081fb2
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/php.xml b/.idea/php.xml
new file mode 100644
index 0000000..e783343
--- /dev/null
+++ b/.idea/php.xml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..58d225f
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,11 @@
+# Contabilidad
+
+1. Obtener pdf de cartolas desde email.
+ 1. Conectar a Email por IMAP.
+ 1. Buscar emails con cartolas.
+ 1. Descargar cartolas.
+ 1. Guardar de forma ordenada.
+1. Extraer información e ingresar a base de datos a traves de API.
+ 1. Abrir archivos y leer.
+ 1. Formatear datos.
+ 1. Mandar a API.
diff --git a/api/Dockerfile b/api/Dockerfile
index 1a55712..fc22ef5 100644
--- a/api/Dockerfile
+++ b/api/Dockerfile
@@ -1,5 +1,9 @@
FROM php:8-fpm
+RUN apt-get update -y && apt-get install -y git
+
RUN docker-php-ext-install pdo pdo_mysql
+COPY --from=composer /usr/bin/composer /usr/bin/composer
+
WORKDIR /app
diff --git a/api/common/Controller/Import.php b/api/common/Controller/Import.php
new file mode 100644
index 0000000..2af3056
--- /dev/null
+++ b/api/common/Controller/Import.php
@@ -0,0 +1,21 @@
+getParsedBody();
+ return $this->withJson($response, $post);
+ }
+ public function uploads(Request $request, Response $response, PdfHandler $handler): Response {
+ $output = $handler->load();
+ return $this->withJson($response, $output);
+ }
+}
diff --git a/api/common/Service/PdfHandler.php b/api/common/Service/PdfHandler.php
new file mode 100644
index 0000000..58f7e39
--- /dev/null
+++ b/api/common/Service/PdfHandler.php
@@ -0,0 +1,30 @@
+client = $client;
+ $this->folder = $pdf_folder;
+ $this->url = $url;
+ }
+
+ public function load(): ?array {
+ $folder = $this->folder;
+ $files = new \DirectoryIterator($folder);
+ $output = [];
+ foreach ($files as $file) {
+ if ($file->isDir() or $file->getExtension() != 'pdf') {
+ continue;
+ }
+ $output []= ['filename' => $file->getBasename()];
+ }
+ $response = $this->client->post($this->url, ['json' => ['files' => $output]]);
+ !d(json_decode($response->getBody()));
+ return $output;
+ }
+}
\ No newline at end of file
diff --git a/api/composer.json b/api/composer.json
index c065ec0..312f622 100644
--- a/api/composer.json
+++ b/api/composer.json
@@ -10,7 +10,11 @@
"zeuxisoo/slim-whoops": "^0.7.3",
"provm/controller": "^1.0",
"provm/models": "^1.0.0-rc3",
- "nesbot/carbon": "^2.50"
+ "nesbot/carbon": "^2.50",
+ "robmorgan/phinx": "^0.12.9",
+ "odan/phinx-migrations-generator": "^5.4",
+ "martin-mikac/csv-to-phinx-seeder": "^1.6",
+ "guzzlehttp/guzzle": "^7.4"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
diff --git a/api/db/migrations/20211029150551_tipo_categoria.php b/api/db/migrations/20211029150551_tipo_categoria.php
new file mode 100644
index 0000000..7ca9068
--- /dev/null
+++ b/api/db/migrations/20211029150551_tipo_categoria.php
@@ -0,0 +1,26 @@
+table('tipos_categoria')
+ ->addColumn('descripcion', 'string')
+ ->addColumn('activo', 'boolean')
+ ->create();
+ }
+}
diff --git a/api/db/migrations/20211029150601_tipo_estado_coneccion.php b/api/db/migrations/20211029150601_tipo_estado_coneccion.php
new file mode 100644
index 0000000..c99e71f
--- /dev/null
+++ b/api/db/migrations/20211029150601_tipo_estado_coneccion.php
@@ -0,0 +1,25 @@
+table('tipos_estado_coneccion')
+ ->addColumn('descripcion', 'string')
+ ->create();
+ }
+}
diff --git a/api/db/migrations/20211029150754_tipo_cuenta.php b/api/db/migrations/20211029150754_tipo_cuenta.php
new file mode 100644
index 0000000..814ccd9
--- /dev/null
+++ b/api/db/migrations/20211029150754_tipo_cuenta.php
@@ -0,0 +1,25 @@
+table('tipos_cuenta')
+ ->addColumn('descripcion', 'string')
+ ->create();
+ }
+}
diff --git a/api/db/migrations/20211029152716_categoria.php b/api/db/migrations/20211029152716_categoria.php
new file mode 100644
index 0000000..5fd364c
--- /dev/null
+++ b/api/db/migrations/20211029152716_categoria.php
@@ -0,0 +1,27 @@
+table('categorias')
+ ->addColumn('nombre', 'string')
+ ->addColumn('tipo_id', 'integer')
+ ->addForeignKey('tipo_id', 'tipos_categoria')
+ ->create();
+ }
+}
diff --git a/api/db/migrations/20211029152729_coneccion.php b/api/db/migrations/20211029152729_coneccion.php
new file mode 100644
index 0000000..3df0b87
--- /dev/null
+++ b/api/db/migrations/20211029152729_coneccion.php
@@ -0,0 +1,25 @@
+table('conecciones')
+ ->addColumn('key', 'string')
+ ->create();
+ }
+}
diff --git a/api/db/migrations/20211029152732_cuenta.php b/api/db/migrations/20211029152732_cuenta.php
new file mode 100644
index 0000000..a214923
--- /dev/null
+++ b/api/db/migrations/20211029152732_cuenta.php
@@ -0,0 +1,29 @@
+table('cuentas')
+ ->addColumn('nombre', 'string')
+ ->addColumn('categoria_id', 'integer')
+ ->addForeignKey('categoria_id', 'categorias')
+ ->addColumn('tipo_id', 'integer')
+ ->addForeignKey('tipo_id', 'tipos_cuenta')
+ ->create();
+ }
+}
diff --git a/api/db/migrations/20211029152738_estado_coneccion.php b/api/db/migrations/20211029152738_estado_coneccion.php
new file mode 100644
index 0000000..27c7cd3
--- /dev/null
+++ b/api/db/migrations/20211029152738_estado_coneccion.php
@@ -0,0 +1,29 @@
+table('estados_coneccion')
+ ->addColumn('coneccion_id', 'integer')
+ ->addForeignKey('coneccion_id', 'conecciones')
+ ->addColumn('fecha', 'date')
+ ->addColumn('tipo_id', 'integer')
+ ->addForeignKey('tipo_id', 'tipos_estado_coneccion')
+ ->create();
+ }
+}
diff --git a/api/db/migrations/20211029152752_transaccion.php b/api/db/migrations/20211029152752_transaccion.php
new file mode 100644
index 0000000..143dacb
--- /dev/null
+++ b/api/db/migrations/20211029152752_transaccion.php
@@ -0,0 +1,32 @@
+table('transacciones')
+ ->addColumn('debito_id', 'integer')
+ ->addForeignKey('debito_id', 'cuentas')
+ ->addColumn('credito_id', 'integer')
+ ->addForeignKey('credito_id', 'cuentas')
+ ->addColumn('fecha', 'datetime')
+ ->addColumn('glosa', 'string')
+ ->addColumn('detalle', 'text')
+ ->addColumn('valor', 'double')
+ ->create();
+ }
+}
diff --git a/api/db/seeds/TipoCuenta.php b/api/db/seeds/TipoCuenta.php
new file mode 100644
index 0000000..5356de6
--- /dev/null
+++ b/api/db/seeds/TipoCuenta.php
@@ -0,0 +1,36 @@
+ 'Ganancia'
+ ],
+ [
+ 'descripcion' => 'Activo'
+ ],
+ [
+ 'descripcion' => 'Pasivo'
+ ],
+ [
+ 'descripcion' => 'Perdida'
+ ]
+ ];
+ $this->table('tipos_cuenta')
+ ->insert($data)
+ ->saveData();
+ }
+}
diff --git a/api/db/seeds/TipoEstadoConeccion.php b/api/db/seeds/TipoEstadoConeccion.php
new file mode 100644
index 0000000..68e4c55
--- /dev/null
+++ b/api/db/seeds/TipoEstadoConeccion.php
@@ -0,0 +1,30 @@
+ 'Activa'
+ ],
+ [
+ 'descripcion' => 'Inactiva'
+ ]
+ ];
+ $this->table('tipos_estado_coneccion')
+ ->insert($data)
+ ->saveData();
+ }
+}
diff --git a/api/phinx.php b/api/phinx.php
new file mode 100644
index 0000000..56047ef
--- /dev/null
+++ b/api/phinx.php
@@ -0,0 +1,41 @@
+ [
+ 'migrations' => '%%PHINX_CONFIG_DIR%%/db/migrations',
+ 'seeds' => '%%PHINX_CONFIG_DIR%%/db/seeds'
+ ],
+ 'environments' => [
+ 'default_migration_table' => 'phinxlog',
+ 'default_environment' => 'development',
+ 'production' => [
+ 'adapter' => 'mysql',
+ 'host' => 'db',
+ 'name' => $_ENV['MYSQL_DATABASE'],
+ 'user' => $_ENV['MYSQL_USER'],
+ 'pass' => $_ENV['MYSQL_PASSWORD'],
+ 'port' => '3306',
+ 'charset' => 'utf8',
+ ],
+ 'development' => [
+ 'adapter' => 'mysql',
+ 'host' => 'db',
+ 'name' => $_ENV['MYSQL_DATABASE'],
+ 'user' => $_ENV['MYSQL_USER'],
+ 'pass' => $_ENV['MYSQL_PASSWORD'],
+ 'port' => '3306',
+ 'charset' => 'utf8',
+ ],
+ 'testing' => [
+ 'adapter' => 'mysql',
+ 'host' => 'db',
+ 'name' => $_ENV['MYSQL_DATABASE'],
+ 'user' => $_ENV['MYSQL_USER'],
+ 'pass' => $_ENV['MYSQL_PASSWORD'],
+ 'port' => '3306',
+ 'charset' => 'utf8',
+ ]
+ ],
+ 'version_order' => 'creation'
+];
diff --git a/api/php.ini b/api/php.ini
new file mode 100644
index 0000000..e145929
--- /dev/null
+++ b/api/php.ini
@@ -0,0 +1,2 @@
+log_errors = true
+error_log = /var/log/php/error.log
\ No newline at end of file
diff --git a/api/public/uploads/pdfs/BICE-CC-2021-06.pdf b/api/public/uploads/pdfs/BICE-CC-2021-06.pdf
new file mode 100644
index 0000000..3542b89
Binary files /dev/null and b/api/public/uploads/pdfs/BICE-CC-2021-06.pdf differ
diff --git a/api/public/uploads/pdfs/BICE-CC-2021-07.pdf b/api/public/uploads/pdfs/BICE-CC-2021-07.pdf
new file mode 100644
index 0000000..5864d8b
Binary files /dev/null and b/api/public/uploads/pdfs/BICE-CC-2021-07.pdf differ
diff --git a/api/public/uploads/pdfs/BICE-CC-2021-09.pdf b/api/public/uploads/pdfs/BICE-CC-2021-09.pdf
new file mode 100644
index 0000000..d521655
Binary files /dev/null and b/api/public/uploads/pdfs/BICE-CC-2021-09.pdf differ
diff --git a/api/public/uploads/pdfs/Scotiabank-CC-2021-07.pdf b/api/public/uploads/pdfs/Scotiabank-CC-2021-07.pdf
new file mode 100644
index 0000000..42fbeee
Binary files /dev/null and b/api/public/uploads/pdfs/Scotiabank-CC-2021-07.pdf differ
diff --git a/api/public/uploads/pdfs/Scotiabank-CC-2021-08.pdf b/api/public/uploads/pdfs/Scotiabank-CC-2021-08.pdf
new file mode 100644
index 0000000..ac7de93
Binary files /dev/null and b/api/public/uploads/pdfs/Scotiabank-CC-2021-08.pdf differ
diff --git a/api/public/uploads/pdfs/Scotiabank-CC-2021-09.pdf b/api/public/uploads/pdfs/Scotiabank-CC-2021-09.pdf
new file mode 100644
index 0000000..b2ba2fb
Binary files /dev/null and b/api/public/uploads/pdfs/Scotiabank-CC-2021-09.pdf differ
diff --git a/api/public/uploads/pdfs/Scotiabank-CC-2021-10.pdf b/api/public/uploads/pdfs/Scotiabank-CC-2021-10.pdf
new file mode 100644
index 0000000..8fef2f3
Binary files /dev/null and b/api/public/uploads/pdfs/Scotiabank-CC-2021-10.pdf differ
diff --git a/api/resources/routes/import.php b/api/resources/routes/import.php
new file mode 100644
index 0000000..0a6bc5b
--- /dev/null
+++ b/api/resources/routes/import.php
@@ -0,0 +1,5 @@
+post('/import', Import::class);
+$app->get('/import/uploads', [Import::class, 'uploads']);
\ No newline at end of file
diff --git a/api/setup/settings/02_common.php b/api/setup/settings/02_common.php
index 8ef013b..1687cc3 100644
--- a/api/setup/settings/02_common.php
+++ b/api/setup/settings/02_common.php
@@ -14,6 +14,24 @@ return [
$arr['resources'],
'routes'
]);
+ $arr['public'] = implode(DIRECTORY_SEPARATOR, [
+ $arr['base'],
+ 'public'
+ ]);
+ $arr['uploads'] = implode(DIRECTORY_SEPARATOR, [
+ $arr['public'],
+ 'uploads'
+ ]);
+ $arr['pdfs'] = implode(DIRECTORY_SEPARATOR, [
+ $arr['uploads'],
+ 'pdfs'
+ ]);
+ return (object) $arr;
+ },
+ 'urls' => function(Container $c) {
+ $arr = [
+ 'python' => 'http://python:5000'
+ ];
return (object) $arr;
}
];
diff --git a/api/setup/setups/02_common.php b/api/setup/setups/02_common.php
new file mode 100644
index 0000000..5e7229e
--- /dev/null
+++ b/api/setup/setups/02_common.php
@@ -0,0 +1,15 @@
+ function(Container $c) {
+ return new GuzzleHttp\Client();
+ },
+ Contabilidad\Common\Service\PdfHandler::class => function(Container $c) {
+ return new Contabilidad\Common\Service\PdfHandler($c->get(GuzzleHttp\Client::class), $c->get('folders')->pdfs, implode('/', [
+ $c->get('urls')->python,
+ 'pdf',
+ 'parse'
+ ]));
+ }
+];
diff --git a/api/src/Categoria.php b/api/src/Categoria.php
index a44574e..d2da085 100644
--- a/api/src/Categoria.php
+++ b/api/src/Categoria.php
@@ -6,6 +6,7 @@ use ProVM\Common\Alias\Model;
/**
* @property int $id
* @property string $nombre
+ * @property TipoCategoria $tipo_id
*/
class Categoria extends Model {
public static $_table = 'categorias';
@@ -18,6 +19,13 @@ class Categoria extends Model {
}
return $this->cuentas;
}
+ protected $tipo;
+ public function tipo() {
+ if ($this->tipo === null) {
+ $this->tipo = $this->childOf(TipoCategoria::class, [Model::SELF_KEY => 'tipo_id']);
+ }
+ return $this->tipo;
+ }
protected $saldo;
public function saldo() {
@@ -34,6 +42,7 @@ class Categoria extends Model {
public function toArray(): array {
$arr = parent::toArray();
+ $arr['tipo'] = $this->tipo()->toArray();
$arr['saldo'] = $this->saldo();
$arr['saldoFormateado'] = '$' . number_format($this->saldo(), 0, ',', '.');
return $arr;
diff --git a/api/src/Coneccion.php b/api/src/Coneccion.php
new file mode 100644
index 0000000..9f9aee2
--- /dev/null
+++ b/api/src/Coneccion.php
@@ -0,0 +1,21 @@
+estados === null) {
+ $this->estados = $this->parentOf(TipoEstadoConeccion::class, [Model::CHILD_KEY => 'coneccion_id']);
+ }
+ return $this->estados;
+ }
+}
diff --git a/api/src/Cuenta.php b/api/src/Cuenta.php
index f49de81..2d98485 100644
--- a/api/src/Cuenta.php
+++ b/api/src/Cuenta.php
@@ -7,10 +7,11 @@ use ProVM\Common\Alias\Model;
* @property int $id
* @property string $nombre
* @property Categoria $categoria_id
+ * @property TipoCuenta $tipo_id
*/
class Cuenta extends Model {
public static $_table = 'cuentas';
- protected static $fields = ['nombre', 'categoria_id'];
+ protected static $fields = ['nombre', 'categoria_id', 'tipo_id'];
protected $categoria;
public function categoria() {
@@ -19,13 +20,12 @@ class Cuenta extends Model {
}
return $this->categoria;
}
-
- protected $entradas;
- public function entradas() {
- if ($this->entradas === null) {
- $this->entradas = $this->parentOf(Entrada::class, [Model::CHILD_KEY => 'cuenta_id']);
+ protected $cuenta;
+ public function cuenta() {
+ if ($this->cuenta === null) {
+ $this->cuenta = $this->childOf(TipoCuenta::class, [Model::SELF_KEY => 'tipo_id']);
}
- return $this->entradas;
+ return $this->cuenta;
}
protected $cargos;
diff --git a/api/src/EstadoConeccion.php b/api/src/EstadoConeccion.php
new file mode 100644
index 0000000..bc18db4
--- /dev/null
+++ b/api/src/EstadoConeccion.php
@@ -0,0 +1,30 @@
+coneccion === null) {
+ $this->coneccion = $this->childOf(Coneccion::class, [Model::SELF_KEY => 'coneccion_id']);
+ }
+ return $this->coneccion;
+ }
+ protected $tipo;
+ public function tipo() {
+ if ($this->tipo === null) {
+ $this->tipo = $this->childOf(TipoEstadoConeccion::class, [Model::SELF_KEY => 'tipo_id']);
+ }
+ return $this->tipo;
+ }
+}
diff --git a/api/src/TipoCategoria.php b/api/src/TipoCategoria.php
new file mode 100644
index 0000000..a226d90
--- /dev/null
+++ b/api/src/TipoCategoria.php
@@ -0,0 +1,14 @@
+desde === null) {
- $this->desde = $this->childOf(Cuenta::class, [Model::SELF_KEY => 'desde_id']);
+ protected $debito;
+ public function debito() {
+ if ($this->debito === null) {
+ $this->debito = $this->childOf(Cuenta::class, [Model::SELF_KEY => 'debito_id']);
}
- return $this->desde;
+ return $this->debito;
}
- protected $hasta;
- public function hasta() {
- if ($this->hasta === null) {
- $this->hasta = $this->childOf(Cuenta::class, [Model::SELF_KEY => 'hasta_id']);
+ protected $credito;
+ public function credito() {
+ if ($this->credito === null) {
+ $this->credito = $this->childOf(Cuenta::class, [Model::SELF_KEY => 'credito_id']);
}
- return $this->hasta;
+ return $this->credito;
}
public function fecha(\DateTime $fecha = null) {
if ($fecha === null) {
@@ -40,8 +40,8 @@ class Transaccion extends Model {
public function toArray(): array {
$arr = parent::toArray();
- $arr['desde'] = $this->desde()->toArray();
- $arr['hasta'] = $this->hasta()->toArray();
+ $arr['debito'] = $this->debito()->toArray();
+ $arr['credito'] = $this->credito()->toArray();
$arr['fechaFormateada'] = $this->fecha()->format('d-m-Y');
$arr['valorFormateado'] = '$' . number_format($this->valor, 0, ',', '.');
return $arr;
diff --git a/docker-compose.yml b/docker-compose.yml
index 3a9904d..1d921a2 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -2,44 +2,62 @@ version: '3'
services:
api:
+ restart: unless-stopped
image: php
build:
context: api
env_file: .env
volumes:
- ./api/:/app/
+ - ./api/php.ini:/usr/local/etc/php/conf.d/php.ini
+ - ./logs/api/php/:/var/log/php/
api-proxy:
+ restart: unless-stopped
image: nginx
ports:
- - 9001:80
+ - "9001:80"
volumes:
- ./api/nginx.conf:/etc/nginx/conf.d/default.conf
- ./logs/api/:/var/log/nginx/
- ./api/:/app/
db:
+ restart: unless-stopped
image: mariadb
env_file: .env
volumes:
- contabilidad_data:/var/lib/mysql
adminer:
+ restart: unless-stopped
image: adminer
ports:
- - 9002:8080
+ - "9002:8080"
ui:
+ restart: unless-stopped
image: php-ui
build:
context: ui
volumes:
- ./ui/:/app/
ui-proxy:
+ restart: unless-stopped
image: nginx
ports:
- - 9000:80
+ - "9000:80"
volumes:
- ./ui/nginx.conf:/etc/nginx/conf.d/default.conf
- ./logs/ui/:/var/log/nginx/
- ./ui/:/app/
+ python:
+ restart: unless-stopped
+ build:
+ context: ./python
+ volumes:
+ - ./python/src/:/app/src/
+ - ./python/config/:/app/config/
+ - ./api/public/uploads/pdfs/:/app/data/
+ - ./logs/python/:/var/log/python/
+
volumes:
contabilidad_data:
diff --git a/python/.idea/.gitignore b/python/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/python/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/python/.idea/inspectionProfiles/profiles_settings.xml b/python/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/python/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/.idea/misc.xml b/python/.idea/misc.xml
new file mode 100644
index 0000000..dd81986
--- /dev/null
+++ b/python/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/.idea/modules.xml b/python/.idea/modules.xml
new file mode 100644
index 0000000..614b3c1
--- /dev/null
+++ b/python/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/.idea/python.iml b/python/.idea/python.iml
new file mode 100644
index 0000000..eed5550
--- /dev/null
+++ b/python/.idea/python.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/.idea/vcs.xml b/python/.idea/vcs.xml
new file mode 100644
index 0000000..6c0b863
--- /dev/null
+++ b/python/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/Dockerfile b/python/Dockerfile
new file mode 100644
index 0000000..a65d664
--- /dev/null
+++ b/python/Dockerfile
@@ -0,0 +1,18 @@
+FROM python
+
+RUN apt-get update -y && apt-get install -y default-jre
+
+RUN pip install flask tabula-py pyyaml pypdf4 gunicorn
+
+WORKDIR /app
+
+COPY ./src/ /app/src/
+
+#ENTRYPOINT ["/bin/bash"]
+
+EXPOSE 5000
+
+WORKDIR /app/src
+
+CMD ["python", "app.py"]
+#CMD ["gunicorn", "-b 0.0.0.0:5000", "app:app"]
diff --git a/python/config/.passwords.yml b/python/config/.passwords.yml
new file mode 100644
index 0000000..f44e275
--- /dev/null
+++ b/python/config/.passwords.yml
@@ -0,0 +1,3 @@
+passwords:
+ - 0839
+ - 159608395
diff --git a/python/data/BICE-CC-2021-09.pdf b/python/data/BICE-CC-2021-09.pdf
new file mode 100644
index 0000000..d521655
Binary files /dev/null and b/python/data/BICE-CC-2021-09.pdf differ
diff --git a/python/src/.idea/.gitignore b/python/src/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/python/src/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/python/src/.idea/inspectionProfiles/Project_Default.xml b/python/src/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..e2ab1e8
--- /dev/null
+++ b/python/src/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/src/.idea/inspectionProfiles/profiles_settings.xml b/python/src/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/python/src/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/src/.idea/misc.xml b/python/src/.idea/misc.xml
new file mode 100644
index 0000000..8961d95
--- /dev/null
+++ b/python/src/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/python/src/.idea/modules.xml b/python/src/.idea/modules.xml
new file mode 100644
index 0000000..f669a0e
--- /dev/null
+++ b/python/src/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/src/.idea/src.iml b/python/src/.idea/src.iml
new file mode 100644
index 0000000..aa55312
--- /dev/null
+++ b/python/src/.idea/src.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/src/.idea/vcs.xml b/python/src/.idea/vcs.xml
new file mode 100644
index 0000000..b2bdec2
--- /dev/null
+++ b/python/src/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/src/__pycache__/app.cpython-310.pyc b/python/src/__pycache__/app.cpython-310.pyc
new file mode 100644
index 0000000..7e4e9dc
Binary files /dev/null and b/python/src/__pycache__/app.cpython-310.pyc differ
diff --git a/python/src/app.py b/python/src/app.py
new file mode 100644
index 0000000..3f03046
--- /dev/null
+++ b/python/src/app.py
@@ -0,0 +1,34 @@
+import json
+import os
+from flask import Flask, request
+
+import contabilidad.pdf as pdf
+import contabilidad.passwords as passwords
+import contabilidad.log as log
+
+
+app = Flask(__name__)
+log.logging['filename'] = '/var/log/python/contabilidad.log'
+
+
+@app.route('/pdf/parse', methods=['POST'])
+def pdf_parse():
+ data = request.get_json()
+ if not isinstance(data['files'], list):
+ data['files'] = [data['files']]
+ password_file = '/app/config/.passwords.yml'
+ pwds = passwords.get_passwords(password_file)
+ texts = []
+ for file in data['files']:
+ filename = os.path.realpath(os.path.join('/app/data', file['filename']))
+ for p in pwds:
+ obj = pdf.get_text(filename, p)
+ if obj is None:
+ continue
+ print(obj)
+ texts.append(json.dumps(obj))
+ return json.dumps(texts)
+
+
+if __name__ == '__main__':
+ app.run(host='0.0.0.0')
diff --git a/python/src/contabilidad/__pycache__/log.cpython-310.pyc b/python/src/contabilidad/__pycache__/log.cpython-310.pyc
new file mode 100644
index 0000000..67fb583
Binary files /dev/null and b/python/src/contabilidad/__pycache__/log.cpython-310.pyc differ
diff --git a/python/src/contabilidad/__pycache__/log.cpython-39.pyc b/python/src/contabilidad/__pycache__/log.cpython-39.pyc
new file mode 100644
index 0000000..36d64f1
Binary files /dev/null and b/python/src/contabilidad/__pycache__/log.cpython-39.pyc differ
diff --git a/python/src/contabilidad/__pycache__/passwords.cpython-310.pyc b/python/src/contabilidad/__pycache__/passwords.cpython-310.pyc
new file mode 100644
index 0000000..756ef7d
Binary files /dev/null and b/python/src/contabilidad/__pycache__/passwords.cpython-310.pyc differ
diff --git a/python/src/contabilidad/__pycache__/passwords.cpython-39.pyc b/python/src/contabilidad/__pycache__/passwords.cpython-39.pyc
new file mode 100644
index 0000000..7fa6d54
Binary files /dev/null and b/python/src/contabilidad/__pycache__/passwords.cpython-39.pyc differ
diff --git a/python/src/contabilidad/__pycache__/pdf.cpython-310.pyc b/python/src/contabilidad/__pycache__/pdf.cpython-310.pyc
new file mode 100644
index 0000000..a530395
Binary files /dev/null and b/python/src/contabilidad/__pycache__/pdf.cpython-310.pyc differ
diff --git a/python/src/contabilidad/__pycache__/pdf.cpython-39.pyc b/python/src/contabilidad/__pycache__/pdf.cpython-39.pyc
new file mode 100644
index 0000000..e9b6584
Binary files /dev/null and b/python/src/contabilidad/__pycache__/pdf.cpython-39.pyc differ
diff --git a/python/src/contabilidad/log.py b/python/src/contabilidad/log.py
new file mode 100644
index 0000000..c16024d
--- /dev/null
+++ b/python/src/contabilidad/log.py
@@ -0,0 +1,19 @@
+import time
+
+
+logging = {
+ 'filename': '/var/log/python/error.log'
+}
+
+
+class LOG_LEVEL:
+ INFO = 'INFO'
+ WARNING = 'WARNING'
+ DEBUG = 'DEBUG'
+ ERROR = 'ERROR'
+
+
+def log(message, level=LOG_LEVEL.INFO):
+ filename = logging['filename']
+ with open(filename, 'a') as f:
+ f.write(time.strftime('[%Y-%m-%d %H:%M:%S] ') + ' - ' + level + ': ' + message)
diff --git a/python/src/contabilidad/passwords.py b/python/src/contabilidad/passwords.py
new file mode 100644
index 0000000..afbea08
--- /dev/null
+++ b/python/src/contabilidad/passwords.py
@@ -0,0 +1,6 @@
+import yaml
+
+
+def get_passwords(filename):
+ with open(filename, 'r') as f:
+ return yaml.load(f, Loader=yaml.Loader)['passwords']
diff --git a/python/src/contabilidad/pdf.py b/python/src/contabilidad/pdf.py
new file mode 100644
index 0000000..de9f7c2
--- /dev/null
+++ b/python/src/contabilidad/pdf.py
@@ -0,0 +1,31 @@
+import PyPDF4
+import tabula
+
+
+def get_pdf(file, password=''):
+ reader = PyPDF4.PdfFileReader(file)
+ if reader.getIsEncrypted() and password != '':
+ status = reader.decrypt(password=password)
+ if status == 0:
+ return None
+ return reader
+
+
+def get_text(filename, password=''):
+ with open(filename, 'rb') as f:
+ reader = get_pdf(f, password)
+ if reader is None:
+ return None
+ print(reader.getPage(0).extractText())
+ texts = []
+ for p in range(0, reader.getNumPages()):
+ print(p)
+ texts.append(reader.getPage(p).extractText())
+ return "\n".join(texts)
+
+
+def get_data(filename, password=''):
+ if password == '':
+ return tabula.read_pdf(filename, pages='all', output_format='json')
+ else:
+ return tabula.read_pdf(filename, password=password, pages='all', output_format='json')
diff --git a/python/src/contabilidad/send_pdf.py b/python/src/contabilidad/send_pdf.py
new file mode 100644
index 0000000..d0bc5c8
--- /dev/null
+++ b/python/src/contabilidad/send_pdf.py
@@ -0,0 +1,54 @@
+import argparse
+import yaml
+import PyPDF4
+import httpx
+
+
+def get_pdf(file, password=''):
+ reader = PyPDF4.PdfFileReader(file)
+ if password != '':
+ status = reader.decrypt(password=password)
+ if status == 0:
+ print('Not decrypted')
+ return reader
+
+
+def send_to_parser(url, text):
+ res = httpx.post(url, data={'to_parse': text})
+ return {'status': res.status_code, 'text': res.json()}
+
+
+def get_text(filename, password=''):
+ with open(filename, 'rb') as f:
+ reader = get_pdf(f, password)
+ texts = []
+ for p in range(0, reader.getNumPages()):
+ texts.append(reader.getPage(p).extractText())
+ return "\n".join(texts)
+
+
+def get_config(filename):
+ with open(filename, 'r') as f:
+ return yaml.load(f, Loader=yaml.Loader)
+
+
+def main(args):
+ password = ''
+ if args.config_file is not None:
+ config = get_config(args.config_file)
+ password = config['password']
+ if args.password is not None:
+ password = args.password
+ text = get_text(args.filename, password)
+ res = send_to_parser(args.url, text)
+ print(res)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-f', '--filename', type=str)
+ parser.add_argument('-p', '--password', type=str)
+ parser.add_argument('-c', '--config_file', type=str)
+ parser.add_argument('-u', '--url', type=str)
+ _args = parser.parse_args()
+ main(_args)
diff --git a/python/src/contabilidad/text_handler.py b/python/src/contabilidad/text_handler.py
new file mode 100644
index 0000000..193f2b8
--- /dev/null
+++ b/python/src/contabilidad/text_handler.py
@@ -0,0 +1,3 @@
+def text_cleanup(text):
+ lines = text.split("\n")
+ print(lines)
diff --git a/python/src/main.py b/python/src/main.py
new file mode 100644
index 0000000..85e7729
--- /dev/null
+++ b/python/src/main.py
@@ -0,0 +1,18 @@
+import argparse
+import os
+
+import contabilidad.pdf as pdf
+
+
+def main(args):
+ filename = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'data', args.filename))
+ obj = pdf.get_text(filename, args.password)
+ print(obj)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-f', '--filename', type=str)
+ parser.add_argument('-p', '--password', type=str, default='')
+ _args = parser.parse_args()
+ main(_args)
diff --git a/python/src/tests/Testing.md b/python/src/tests/Testing.md
new file mode 100644
index 0000000..19087f2
--- /dev/null
+++ b/python/src/tests/Testing.md
@@ -0,0 +1,35 @@
+# Tests
+
+### 1. Conductor
++ Set start event
++ Get ready events
++ Set first step event
++ Get first step ready
++ Set second step event
++ Get second step ready
+
+### 2. Email
++ Connect to IMAP
+ + Wrong data
+ + Wrong configuration
++ Get mailboxes
++ Get mail ids with search
++ Get mails by id
++ Get mail by id
++ Get attachment
++ Close connection
+
+### 3. API Sender
++ Get attachments
++ Process
++ Send to API
+
+
+## Steps
+1. Start
+ + Connect
+ + Standby
+2. Find emails, get attachments
+3. Process attachments
+4. Send to API
+5. Close
diff --git a/worker/main.py b/worker/main.py
new file mode 100644
index 0000000..c7a9f7e
--- /dev/null
+++ b/worker/main.py
@@ -0,0 +1,12 @@
+from threading import Thread
+import httpx
+
+
+class Worker(Thread):
+ def __init__(self, settings):
+ self.settings = settings
+
+ def run():
+ while True:
+ if self.stop_event.isSet():
+ break