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