The post Wazuh no muestra información de los equipos de repente first appeared on Dipler.
]]>ELK tiene dos grandes repositorios en función de la licencia del mismo como podemos ver en su web. Es de vital importancia que las versiones de los distintos componentes de nuestra arquitectura sean consistentes entre sí y, sin embargo, es muy común que, si no tenemos cuidado al usar los gestores de paquetes para actualizar este software surjan complicaciones. Por ejemplo, la versión 7.10 de ElasticSearch no es compatible con la 7.13+ de la misma distribución o que, por ejemplo la versión 7.10 de ElasticSearch no es compatible con la versión oss-7.10 de filebeat.
y… ¿Qué tiene que ver todo esto con Wazuh? pues bien, me encuentro en varias ocasiones con situaciones en las que veo que el dashboard deja de mostrar datos de los equipos de repente.
Cuando el dashboard de Wazuh deja de mostrar datos de repente, lo primero que deberemos hacer es entrar a la consola del equipo y ejecutar el comando:
~# filebeat test output
Este comando revisará la configuración y la comunicación entre los componentes. En mi caso obtuve la siguiente salida:
~# filebeat test output
elasticsearch: https://192.168.1.10:9200...
parse url... OK
connection...
parse host... OK
dns lookup... OK
addresses: 192.168.1.10
dial up... OK
TLS...
security: server's certificate chain verification is enabled
handshake... OK
TLS version: TLSv1.2
dial up... OK
talk to server... ERROR Connection marked as failed because the onConnect callback failed: could not connect to a compatible version of Elasticsearch: 400 Bad Request: {"error":{"root_cause":[{"type":"invalid_index_name_exception","reason":"Invalid index name [_license], must not start with '_'.","index":"_license","index_uuid":"_na_"}],"type":"invalid_index_name_exception","reason":"Invalid index name [_license], must not start with '_'.","index":"_license","index_uuid":"_na_"},"status":400}
Llegados a este punto podemos ver en el mensaje que hay problemas de licencia. Es decir, las versiones por «alguna razón» son incompatibles. En este caso se podía observar como la versión de filebeat era una:
~# filebeat version
filebeat version 7.17.17 (amd64), libbeat 7.17.17 [af0928f86fb2f8c2da6e72ad7eef25d67f7815c9 built 2024-01-17 19:05:41 +0000 UTC]
Mientras que la versión de elastic search era otra:
~# curl -XGET -k https://<ELASTICSEARCH_HOST_IP>:9200 -u <ELASTICSEARCH_USER>:<ELASTICSEARCH_USER_PASSWORD>
{
"name" : "node-1",
"cluster_name" : "wazuh-indexer-cluster",
"cluster_uuid" : "DaJoCkJWSA7mSH2yOgnJZw",
"version" : {
"number" : "7.10.2",
"build_type" : "rpm",
"build_hash" : "db90a415ff2fd428b4f7b3f800a51dc229287cb4",
"build_date" : "2023-06-03T06:24:25.112415503Z",
"build_snapshot" : false,
"lucene_version" : "9.6.0",
"minimum_wire_compatibility_version" : "7.10.0",
"minimum_index_compatibility_version" : "7.0.0"
},
"tagline" : "The OpenSearch Project: https://opensearch.org/"
}
Llegados a este punto, todo nos indica que tenemos que bajar la versión de filebeat ya que en la versión 7.13+ se introdujeron cambios importantes.
~# apt install filebeat=7.10.2
Llegados a este punto pueden pasar dos cosas, que las versiones sean las correctas o que falte algo por hacer. En este caso, al ejecutar el comando de test de nuevo, veremos que, al menos en mi caso salía el siguiente error:
~# filebeat test output
elasticsearch: https://192.168.1.10:9200...
parse url... OK
connection...
parse host... OK
dns lookup... OK
addresses: 1.1.1.1
dial up... OK
TLS...
security: server's certificate chain verification is enabled
handshake... OK
TLS version: TLSv1.2
dial up... OK
talk to server... ERROR Connection marked as failed because the onConnect callback failed: Filebeat requires the default distribution of Elasticsearch. Please update to the default distribution of Elasticsearch for full access to all free features, or switch to the OSS distribution of Filebeat.
Ahora el error es mucho más claro. Tenemos ElasticSearch y Filebeat en la misma versión pero en una distribución distinta. Para solucionar esto vamos a cambiar el origen del repositorio de elasticsearch para que concuerde con el instalado por wazuh:
~# nano /etc/apt/sources.list.d/elastic-7.x.list
Y cambiaremos la línea:
deb https://artifacts.elastic.co/packages/7.x/apt stable main
Por
deb https://artifacts.elastic.co/packages/oss-7.x/apt stable main
Ahora actualizamos la lista de repositorios y volvemos a instalar filebeat en la versión deseada pero esta vez en su distribución OSS:
~# apt update
~# apt install filebeat=7.10.2
Si ahora ejecutamos el comando de pruebas, veremos como el resultado es correcto:
~# filebeat test output
elasticsearch: https://192.168.1.10:9200…
parse url… OK
connection…
parse host… OK
dns lookup… OK
addresses: 192.168.1.10
dial up… OK
TLS…
security: server's certificate chain verification is enabled
handshake… OK
TLS version: TLSv1.2
dial up… OK
talk to server… OK
version: 7.10.2
Si ahora volvemos a acceder al dashboard, veremos como empiezan a aparecer los datos y los logs de los equipos.
The post Wazuh no muestra información de los equipos de repente first appeared on Dipler.
]]>The post Cómo calcular diferencias de valor entre dos filas first appeared on Dipler.
]]>De repente, en un proceso de auditoría o, simplemente al comparar los precios de dos exchanges para llevar a cabo un proceso de arbitraje, nos damos cuenta que, de algunos exanges, nos faltan datos. Es decir, no tenemos algunos precios y queremos poner solución a este problema.
La tabla en la que almacenamos los datos la hemos llamado ticks y tiene la siguiente estructura:
Esta tabla almacena todas las velas o klines agrupadas por minuto.
Como nos hemos dado cuenta durante este proceso de auditoría de que nos faltan datos de algunos minutos, queremos hacer una select que itere en los más de 2.000.000 de registros y nos indique qué velas nos faltan para poder poner solución.
Mucha gente abordaría este problema mediante un bucle e iterando en el lenguaje de programación final o bien mediante un procedimiento almacenado. Sin embargo, existe una forma muy interesante de hacer esto con un único comando en PostgreSQL.
Supongamos que tenemos un subconjunto de datos como el siguiente:
Pues bien, si queremos obtener la diferencia entre el valor de una fila y el valor de la anterior deberemos hacer uso de la función LAG(). Esta diferencia de valor entre filas es crucial para poder realizar la tarea que nos proponemos.
LAG(expression [,offset [,default_value]])
OVER (
[PARTITION BY partition_expression, ... ]
ORDER BY sort_expression [ASC | DESC], ...
)
Esta función nos permitirá acceder a un dato de una fila distinta a la actual mediante los siguientes valores:
Expresión que se evalúa en la fila que aparece en la fila indicada por el offset. Puede ser una columna, una expresión o una subquery.
Entero positivo que indica cuántas filas antes tiene que aplicar la expresión. El valor por defecto es 1.
Valor por defecto en caso de que el offset no se pueda aplicar. El valor por defecto es NULL.
Indica las características del conjunto de datos sobre el que iterará.
Permite dividir las filas en particiones sobre las que se aplicará la función LAG() se aplica. Por defecto se usará una única partición.
Permite especificar el orden de las filas sobre las que aplicaremos la función LAG().
Sabiendo la estructura de la expresión, vamos a ir paso a paso:
Primero vamos a obtener todas las entradas para un símbolo en concreto:
SELECT *
FROM ticks
WHERE symbol = 'ETHBTC';
Ahora queremos mostrar además de la fecha de cierre de la fila actual, la de la fila anterior:
select close_time, lag(close_time) over (order by close_time), *
from ticks
where symbol = 'ETHBTC'
Como se puede observar en esta imagen, en la primera fila, la columna lag tiene el valor NULL ya que no existe ninguna fila previa. Sin embargo, el valor de lag de la fila N tendrá el valor de la columna close_time de la fila N-1.
Ahora nos interesa , en lugar de obtener valores anteriores, vamos a a calcular el intervalo entre estas dos filas.
select close_time - lag(close_time) over (order by close_time) as "interval", *
from ticks
where symbol = 'ETHBTC'
Ahora queremos poder filtrar por el nuevo campo. Por desgracia no se puede filtrar por el campo «interval». Por ello, deberemos hacer uso de una subselect para aplicar el filtro que deseemos u ordenar los datos finales en función de ese campo. En este caso, vamos a ver qué velas de 1 minuto nos faltan buscando intervalos superiores a 00:01:00.
select * from (
select close_time - lag(close_time) over (order by close_time) as increase, close_time, lag(close_time) over (order by close_time), *
from ticks
where symbol = 'ETHBTC'
) as tmp where increase > '00:01:00'
Obteniendo el siguiente resultado:
Ni que decir tiene, la función LAG no es gratuita en recursos y por ello hay que ejecutarla con cuidado y sólo cuando sea estrictamente necesario.
The post Cómo calcular diferencias de valor entre dos filas first appeared on Dipler.
]]>The post Pipenv: gestión de dependencias en Python para dormir tranquilos first appeared on Dipler.
]]>Para solucionar esto, cada lenguaje cuenta con distintas soluciones oficiales y no oficiales que ayudan en esta gestión.
En el caso de Python (igual que pasa en PHP con composer y otros sistemas similares) tenemos la posibilidad de gestionar las dependencias de distintas maneras:
En este artículo nos vamos a centrar en abordar el manejo de dependencias con la segunda opción. Ésta variante, a su vez puede llevarse a cabo de distintas formas.
La más directa consiste en trabajar directamente con pip, los ficheros de dependencias requeridas, etc. Sin embargo, un proceso que parece muy sencillo a simple vista, se puede acabar dificultando al trabajar con proyectos grandes o muy grandes o, incluso, multimódulo.
Para evitar que nos suceda esto, hay herramientas basadas en los mismos conceptos (e incluso las mismas herramientas) pero con un flujo de trabajo más controlado y bien definido. Permitiendo controlar mucho mejor las dependencias, aislando correctamente todas aquellas necesarias y excluyendo las innecesarias, etc.
En este artículo vamos a adentrarnos en el mundo de Pipenv.
Pipenv es una herramienta que tiene por objetivo facilitar el manejo de entornos virtuales, así como facilitar el proceso de gestión de dependencias y paquetes. Vamos a hacer un recorrido de la herramienta que nos permita descubrir parte del potencial de la herramienta.
El presente artículo asume que estamos usando Python 3. Si no estamos seguros de la versión que tenemos instalada, podemos ejecutar el siguiente comando:
➜ python --version
Python 3.9.1
Teniendo en cuenta todo esto, vamos a ponernos manos a la obra y a instalar el paquete pipenv. Tenemos varias opciones:
Podemos hacer uso de hombrew para instalar fácilmente pipenv
➜ brew install pipenv
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 4 taps (microsoft/mssql-release, homebrew/core, homebrew/cask and homebrew/services).
==> New Formulae
apt docuum mailcatcher pari-seadata-big
aws-vault eigenpy microsoft/mssql-release/msodbcsql17@17.8.1.1 procps
basis_universal fanyi microsoft/mssql-release/mssql-tools@17.8.1.1 pyoxidizer
bubblewrap firefoxpwa newrelic-infra-agent reproc
bupstash fst notcurses rsc_2fa
cargo-bloat ghostunnel onedrive singularity
cargo-llvm-lines h2c opensearch-dashboards spot
cargo-outdated i2c-tools ots sql-lint
chrpath influxdb-cli pari-elldata stylua
cilium-cli joplin-cli pari-galdata wildmidi
cruft kn pari-galpol xauth
datree libaec pari-seadata xsel
==> Updated Formulae
Updated 2099 formulae.
==> Renamed Formulae
prestosql -> trino
==> Deleted Formulae
giter8 procyon-decompiler terraform-provisioner-ansible tj
==> New Casks
betterdiscord-installer clock-signal gosign leapp mimestream stork trezor-suite wifi-explorer-pro
blackhole-64ch duplicate-file-finder infra maccleaner-pro mweb-pro temurin vsd-viewer wing-personal
blockbench fluent-reader jiohome memory-cleaner open-video-downloader touch-portal vsdx-annotator zebra2
clay foxglove-studio kdocs midi-router-client shottr transfer vym
==> Updated Casks
Updated 890 casks.
==> Deleted Casks
3cxphone elpki lightwright phonetrans superbeam
agfeo-dashboard finisher-fluxx locklizard-safeguard-viewer playnow tbs-studio
anytrans finisher-micro macclean plecs-standalone thetube
anytrans-for-android finisher-neo macintosh-explorer pomolectron trufont
axe-electrum flow-e modulair privatus uberconference
baiducloud fluxcenter mweb pro-fit unity-appletv-support-for-editor
boonzi fm3-edit noraswitch qtum unity-linux-il2cpp-support-for-editor
brooklite imarisviewer obyte qyooo unity-macos-il2cpp-support-for-editor
colormunki-photo imobie-m1-app-checker open-ecard rubitrack-pro utox
deadbeef instant-articles-builder otter-browser s3stat-setup wanna
dnagedcom instasizer pastor scrooo wingpersonal
dragthing jabt-flow phonebrowse simplelink-msp432e4-sdk wolfram-player
dukto jidusm phoneclean spectrum youtube-dl-gui
eaccess lektor phonerescue stageplotpro zbuc-imgur
==> Downloading https://ghcr.io/v2/homebrew/core/pipenv/manifests/2021.5.29
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/pipenv/blobs/sha256:3e01446c114bb70d72f7455b19417e9e3190a95dcd565dc4ce994988b1b49f54
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:3e01446c114bb70d72f7455b19417e9e3190a95dcd565dc4ce994988b1b49f54?se=2021-08-25T22%3A30%3A00Z&sig=RwjOht74WcGb6QyObhAXlWI%2F31JHBb
######################################################################## 100.0%
==> Pouring pipenv--2021.5.29.big_sur.bottle.tar.gz
==> Caveats
zsh completions have been installed to:
/usr/local/share/zsh/site-functions
==> Summary
🍺 /usr/local/Cellar/pipenv/2021.5.29: 1,854 files, 28.2MB
==> `brew cleanup` has not been run in 30 days, running now...
...
Pruned 2 symbolic links and 19 directories from /usr/local
Para dar soporte a cualquier plataforma, ya sea de las anteriormente mencionadas o cualquier otra, podemos hacer uso del paquete pip o pip3. Por ello, lo primero que tenemos que hacer es comprobar que tenemos instalado el gestor de paquetes pip en el equipo, para lo que ejecutaremos el comando pip o pip3:
➜ pip3 --version
pip 20.2.3 from /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/pip (python 3.9)
Para posteriormente instalar el gestor de paquetes.
➜ pip3 install pipenv
Collecting pipenv
Using cached pipenv-2021.5.29-py2.py3-none-any.whl (3.9 MB)
Requirement already satisfied: virtualenv in /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages (from pipenv) (20.7.2)
Requirement already satisfied: virtualenv-clone>=0.2.5 in /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages (from pipenv) (0.5.6)
Requirement already satisfied: setuptools>=36.2.1 in /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages (from pipenv) (49.2.1)
Requirement already satisfied: pip>=18.0 in /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages (from pipenv) (20.2.3)
Requirement already satisfied: certifi in /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages (from pipenv) (2021.5.30)
Requirement already satisfied: distlib<1,>=0.3.1 in /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages (from virtualenv->pipenv) (0.3.2)
Requirement already satisfied: filelock<4,>=3.0.0 in /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages (from virtualenv->pipenv) (3.0.12)
Requirement already satisfied: six<2,>=1.9.0 in /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages (from virtualenv->pipenv) (1.16.0)
Requirement already satisfied: backports.entry-points-selectable>=1.0.4 in /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages (from virtualenv->pipenv) (1.1.0)
Requirement already satisfied: platformdirs<3,>=2 in /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages (from virtualenv->pipenv) (2.2.0)
Installing collected packages: pipenv
Successfully installed pipenv-2021.5.29
WARNING: You are using pip version 20.2.3; however, version 21.2.4 is available.
You should consider upgrading via the '/Library/Frameworks/Python.framework/Versions/3.9/bin/python3.9 -m pip install --upgrade pip' command.
Una vez instalado podremos comprobar la versión ejecutando:
➜ pipenv --version
pipenv, version 2021.5.29
Como hemos dicho anteriormente Pipenv es un gestor de dependencias en entornos virtuales que facilita mucho el proceso de gestión del proyecto y, sobre todo, permite que un equipo trabaje con las mismas versiones de las dependencias y un ecosistema lo más parecido posible aunque se usen distintos equipos y configuraciones base.
Para ello, Pipenv se apoya en una serie de ficheros:
Este fichero contiene información básica del proyecto, así como las dependencias del mismo. Más adelante, veremos un ejemplo
Fichero que complementa a Pipfile. Si bien este último permite identificar versiones genéricas de las librerías y otra información relevante, el fichero Pipfile.lock nos permite describir la configuración exacta del proyecto. Es decir, si en el primero decimos que queremos un paquete concreto en su versión 2, en el fichero Pipfile.lock queda definida la versión exacta de dicho paquete. Esto permite facilitar enormemente el flujo de trabajo de equipos de cualquier tamaño.
Crear. unproyecto con Pipenv es muy sencillo. Para ello ejecutaremos el comando con el argumento –three para python 3 y –two para python 2
➜ pipenv --three
Creating a virtualenv for this project...
Pipfile: /Users/alejandro/workspace/python/Pipfile
Using /opt/local/bin/python3.9 (3.9.5) to create virtualenv...
⠇ Creating virtual environment...created virtual environment CPython3.9.5.final.0-64 in 471ms
creator CPython3Posix(dest=/Users/alejandro/.local/share/virtualenvs/python-8t82TBKP, clear=False, no_vcs_ignore=False, global=False)
seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/Users/alejandro/Library/Application Support/virtualenv)
added seed packages: pip==21.2.3, setuptools==57.4.0, wheel==0.37.0
activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator
✔ Successfully created virtual environment!
Virtualenv location: /Users/alejandro/.local/share/virtualenvs/python-8t82TBKP
Creating a Pipfile for this project...
Este comando ha creado el fichero Pipfile que hemos comentado anteriormente y que tendrá un contenido similar a:
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
[dev-packages]
[requires]
python_version = "3.9"
En este documento tenemos la definición del proyecto, los paquetes y paquetes de desarrollo necesarios para ejecutar nuestro proyecto y los elementos requeridos. De momento el fichero es muy simple. No obstante, poco a poco iremos viendo como crece y varía con los comandos que iremos ejecutando.
Vamos a crear un script que obtenga el precio del bitcoin haciendo uso de la API pública de Coindesk: https://api.coindesk.com/v1/bpi/currentprice.json
Primero vamos a importar la librería necesaria para poder hacer peticiones HTTP:
➜ pipenv install requests
Installing requests...
Adding requests to Pipfile's [packages]...
✔ Installation Succeeded
Pipfile.lock not found, creating...
Locking [dev-packages] dependencies...
Locking [packages] dependencies...
Building requirements...
Resolving dependencies...
✔ Success!
Updated Pipfile.lock (fe5a22)!
Installing dependencies from Pipfile.lock (fe5a22)...
🐍 ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 0/0 — 00:00:00
To activate this project's virtualenv, run pipenv shell.
Alternatively, run a command inside the virtualenv with pipenv run.
Perfecto, ya tenemos instalado el paquete requests. En este punto veremos que el fichero Pipfile tiene ahora el paquete que hemos indicado:
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
requests = "*"
[dev-packages]
[requires]
python_version = "3.9"
Y que ha aparecido un fichero llamado Pipfile.lock que contiene el detalle de las dependencias y las versiones:
{
"_meta": {
"hash": {
"sha256": "b8c2e1580c53e383cfe4254c1f16560b855d984fde8b2beb3bf6ee8fc2fe5a22"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.9"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"certifi": {
"hashes": [
"sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee",
"sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"
],
"version": "==2021.5.30"
},
"charset-normalizer": {
"hashes": [
"sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b",
"sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3"
],
"markers": "python_version >= '3'",
"version": "==2.0.4"
},
"idna": {
"hashes": [
"sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a",
"sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"
],
"markers": "python_version >= '3'",
"version": "==3.2"
},
"requests": {
"hashes": [
"sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
"sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
],
"index": "pypi",
"version": "==2.26.0"
},
"urllib3": {
"hashes": [
"sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4",
"sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
"version": "==1.26.6"
}
},
"develop": {}
}
Como hemos comentado anteriormente, este fichero permitirá asegurarnos de que todos los miembros del equipo contamos con las mismas versiones de los paquetes.
Muy bien, ¿pero dónde está el entorno virtual con las dependencias instaladas? Es posible que hayas usado antes pip o los venv y te sorprenderá no ver la típica carpeta env o venv. Para ver su localización ejecutaremos el siguiente comando:
➜ pipenv --venv
/Users/alejandro/.local/share/virtualenvs/python-8t82TBKP
Esto es así porque no tenemos habilitada la opción de generar la carpeta venv en el proyecto, sino que la crea fuera.
➜ export PIPENV_VENV_IN_PROJECT="enabled"
Ambas opciones son válidas. Si una vez habilitada esta variable de entorno volvemos a ejecutar el comando de vgeneración del proyecto, veremos qyue tenemos una carpeta llamada .venv.
➜ pipenv --three
Creating a virtualenv for this project...
Pipfile: /Users/alejandro/workspace/python/Pipfile
Using /opt/local/bin/python3.9 (3.9.5) to create virtualenv...
⠹ Creating virtual environment...created virtual environment CPython3.9.5.final.0-64 in 730ms
creator CPython3Posix(dest=/Users/alejandro/workspace/python/.venv, clear=False, no_vcs_ignore=False, global=False)
seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/Users/alejandro/Library/Application Support/virtualenv)
added seed packages: pip==21.2.3, setuptools==57.4.0, wheel==0.37.0
activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator
✔ Successfully created virtual environment!
Virtualenv location: /Users/alejandro/workspace/python/.venv
➜ ls -la
total 16
drwxr-xr-x 5 alejandro staff 160 26 ago 01:05 .
drwx------@ 1344 alejandro staff 43008 25 ago 23:56 ..
drwxr-xr-x 7 alejandro staff 224 26 ago 01:05 .venv
-rw-r--r-- 1 alejandro staff 153 26 ago 00:51 Pipfile
-rw-r--r-- 1 alejandro staff 2147 26 ago 00:52 Pipfile.lock
Activar el entorno virutal es tan sencillo como ejecutar el comando:
➜ pipenv shell
Launching subshell in virtual environment...
. /Users/alejandro/.local/share/virtualenvs/crawler-JJA5iozN/bin/activate
➜ . /Users/alejandro/.local/share/virtualenvs/crawler-JJA5iozN/bin/activate
(python) ➜
El hecho de que aparezca un nombre enter paréntesis antes del prompt quiere decir que estamos dentro del entorno virtual.
Si queremos crear el entorno virtual a partir de un proyecto descargado de un repositorio, bastaría con ejecutar el comando:
pip install
import requests
response = requests.get('https://api.coindesk.com/v1/bpi/currentprice.json')
json = response.json()
print(f"{json['bpi']['USD']['rate']} @ {json['time']['updated']}")
Y si ahora lo ejecutamos obtendremos la siguiente salida:
(python) ➜ python test.py
49,033.6041 @ Aug 25, 2021 23:19:00 UTC
Con este pequeño ejemplo hemos hecho un recorrido completo en el que se expone el manejo. deproyectos y dependencias con Pipenv. No obstante, es una herramienta con muchas más posibilidades de las que hemos podido ver en el artículo.
Os dejo un link a una página en la que podéis ver más información: Pipenv: Flujo de trabajo en Python para humanos. — documentación de pipenv – 2018.05.18 (pipenv-es.readthedocs.io).
Otra opción es ejecutar el comando pipenv –man. Este comando mostrará en consola la documentación completa del comando con detalles de su uso y sus opciones.
The post Pipenv: gestión de dependencias en Python para dormir tranquilos first appeared on Dipler.
]]>The post ¡Deja de usar i++ en tus bucles de PHP! first appeared on Dipler.
]]>Sustituir $i++ por ++$i es uno de los más de 20 consejos de optimización de códigos PHP que describimos en 2009. Si bien desde ese año, muchos lenguajes han hecho mejoras al respecto, como Java, PHP no ha tenido la misma suerte.
Con la actualización a PHP 7 se ha optimizado la velocidad de ejecución de los scripts gracias a un motor de ejecución renovado. No obstante, sigue siendo un código interpretado.
Los lenguajes compilados o transpilados, como Java, C o Rust permiten realizar optimizaciones automáticas durante el proceso de conversión a código máquina o bytecode. Sin embargo, en lenguajes de scripting, como PHP, esto no es así. Estos lenguajes no realizan varias pasadas de forma previa a la ejecución que permitan buscar patrones de optimización automática. Al menos no sin afectar gravemente al rendimiento.
Vale, es cierto, existen sistemas que permiten cachea el código, almacenarlo en memoria, etc. Con la finalidad de acelerar significativamente el tiempo de ejecución.
Con la finalidad de entender los resultados, antes vamos a analizar detenidamente lo que sucede con cada una de las posibilidades básicas que tenemos para incrementar en 1 un valor.
Para hacer este análisis vamos a hacer uso de la herramienta phpdbg con el código
~$ phpdbg -p <archivo>
Phpdbg con el parámetro -p nos permite listar los opcodes de un script.
Para el código
<?php
$i = 0;
$i++;
Si analizamos el opcode generado
~$ phpdbg -p 001_additions_i++.php
function name: (null)
L1-3 {main}() ./20210718 PHP optimization/001_additions_i++.php - 0x10967e7e0 + 6 ops
L2 #0 EXT_STMT
L2 #1 ASSIGN $i 0
L3 #2 EXT_STMT
L3 #3 POST_INC $i ~1
L3 #4 FREE ~1
L3 #5 RETURN<-1> 1
[Script ended normally]
Vemos como las líneas marcadas (#2-#4) son las que se corresponden con $i++:
En este caso, el valor de $i++ devolvería, según la iteración:
<?php
$i = 0;
++$i;
De forma análoga al caso anterior, se incrementa el valor de la variable en 1, sin embargo, en este caso se realizan menos operaciones a bajo nivel, realizando el incremento antes de obtener el valor.
~$ phpdbg -p 001_additions_++i.php
function name: (null)
L1-3 {main}() ./20210718 PHP optimization/001_additions_i++.php - 0x10a08c000 + 5 ops
L2 #0 EXT_STMT
L2 #1 ASSIGN $i 0
L3 #2 EXT_STMT
L3 #3 PRE_INC $i
L3 #4 RETURN<-1> 1
[Script ended normally]
De este modo, el código, para cada iteración, la instrucción ++$i devolverá:
Es decir, se ahorran opcodes a bajo nivel con respecto al $i++, es decir, teóricamente debería de ser más rápido.
<?php
$i = 0;
$i = $i + 1;
~$ phpdbg -p 001_additions_i=i+1.php
function name: (null)
L1-3 {main}() ./20210718 PHP optimization/001_additions_i=i+1.php - 0x10528b100 + 6 ops
L2 #0 EXT_STMT
L2 #1 ASSIGN $i 0
L3 #2 EXT_STMT
L3 #3 ADD $i 1 ~1
L3 #4 ASSIGN $i ~1
L3 #5 RETURN<-1> 1
[Script ended normally]
Este bucle, el incremento realiza una suma de un entero, es decir, no es exactamente un incremento aunque el resultado sea el mismo. Pero obtiene un valor, suma un valor constante y, finalmente almacena el valor. En opcodes:
<?php
$i = 0;
$i += 1;
~$ phpdbg -p 001_additions_i+=1.php
function name: (null)
L1-3 {main}() ./20210718 PHP optimization/001_additions_i+=1.php - 0x11227e7e0 + 5 ops
L2 #0 EXT_STMT
L2 #1 ASSIGN $i 0
L3 #2 EXT_STMT
L3 #3 ASSIGN_ADD $i 1
L3 #4 RETURN<-1> 1
[Script ended normally]
De forma similar al caso anterior se realiza una suma pero sin devolver el valor $i inicialmente (realiza una operación menos, reduciendo en varios opcodes cada iteración), por lo que debería de ser algo más rápido que la suma tradicional y que el post-incremento.
Vamos a analizarlo:
Según los opcodes analizados anteriormente, podríamos intentar definir las formas más y menos costosas de realizar estas operaciones. En este caso, según los opcode, claramente el ++$i sería la opción más rápida, mientras que $i = $i + 1 sería la más lenta.
Entre $i++ y $i+=1, podríamos decantarnos por que, a pesar de que añadir es más costoso que incrementar, nos ahorramos la tarea de liberar. Por ello el orden teórico según los opcode sería:
Para ilustrar y analizar el caso del pre-incremento, post-incremento y dos tipos de operación suma, vamos a hacer un pequeño test que evalúe el rendimiento en PHP.
El script que vamos a utilizar para realizar las pruebas de rendimiento es el siguiente:
<?php
define('ITERATIONS', 1000000000);
define('TIMES', 100);
$result_array = [
'$i++' => array(),
'++$i' => array(),
'$i+=1' => array(),
'$i=$i+1' => array(),
];
for ($j = 0; $j < TIMES; $j++) {
/**
* i++ enlapsed time
*/
$start = microtime(true);
for ($i = 0; $i < ITERATIONS; $i++);
$time_elapsed_secs = microtime(true) - $start;
echo '$i++ (', ITERATIONS,' times) took ', $time_elapsed_secs, "s\n";
$result_array['$i++'][] = $time_elapsed_secs;
/**
* ++i enlapsed time
*/
$start = microtime(true);
for ($i = 0; $i < ITERATIONS; ++$i);
$time_elapsed_secs = microtime(true) - $start;
echo '++$i (', ITERATIONS,' times) took ', $time_elapsed_secs, "s\n";
$result_array['++$i'][] = $time_elapsed_secs;
/**
* $i+=1 enlapsed time
*/
$start = microtime(true);
for ($i = 0; $i < ITERATIONS; $i+=1);
$time_elapsed_secs = microtime(true) - $start;
echo '$i += 1 (', ITERATIONS,' times) took ', $time_elapsed_secs, "s\n";
$result_array['$i+=1'][] = $time_elapsed_secs;
/**
* $i= $i+1 enlapsed time
*/
$start = microtime(true);
for ($i = 0; $i < ITERATIONS; $i=$i + 1);
$time_elapsed_secs = microtime(true) - $start;
echo '$i = $i + 1 (', ITERATIONS,' times) took ', $time_elapsed_secs, "s\n";
$result_array['$i=$i+1'][] = $time_elapsed_secs;
}
$fp = fopen('001_additions.csv', 'w');
foreach ($result_array as $key => $row) {
array_unshift($row, $key);
fputcsv($fp, $row);
}
fclose($fp);
El código, si se analiza en detalle, veremos cómo ejecuta 100 veces cada una de las cuatro pruebas. En cada prueba ejecutará 1.000.000.000 (mil millones de veces) la operación suma (además de comparaciones y moverá el puntero de ejecución). Además, para cada una de las 100 iteraciones, ejecutaremos cuatro tipos de incremento distintas. El resultado de cada prueba se almacenará en una variable que se utilizará para generar finalmente un CSV que podremos analizar.
La ejecución de una única iteración nos muestra los siguientes resultados:
$i++ (1000000000 times) took 13.006237030029s
++$i (1000000000 times) took 9.5504179000854s
$i += 1 (1000000000 times) took 11.834012031555s
$i = $i + 1 (1000000000 times) took 14.221917152405s
Estos resultados podrían verse afectados por las tareas que se están realizando en paralelo en el equipo, por lo que, para mejorar el análisis, se ejecutan 100 veces cada una de los bucles de forma intercalada, permitiendo reducir la variabilidad en los tiempos asociada a la carga del equipo en otras tareas.
La ejecución se realiza en un Macbook Pro de 15″ con 16GB de RAM y PHP 7.3.
Tras ejecutar 100 veces el test completo obtenemos los siguientes resultados:
CSV php increment times (263 descargas )A menor valor, menor tiempo de ejecución y por lo tanto es una aproximación de ejecución más rápida.
Como puede observarse, los datos ejecutados 100 veces son consistentes con los de la primera ejecución que hemos mostrado más arriba y, si somos algo curiosos, podemos observar cómo ha ido variando la carga del equipo durante las horas que duró la prueba.
Además de ver una clara diferencia que es consistente, podemos ver como hay unas variaciones de hasta el 50% en el tiempo de ejecución haciendo uso de las diferentes sintaxis, siendo el pre-incremento la opción claramente ganadora y el incremento en formato suma tradicional el más costoso.
Cada forma de incrementar un valor tiene sus ventajas e inconvenientes como hemos visto en este artículo porque cubren necesidades diferentes. No obstante, para bucles for hay un claro ganador: el pre-incremento ++$i.
En la inmensa mayoría de los casos, no notaremos mejoras de rendimiento significativas. Esto es así porque normalmente la carga de la ejecución de un bucle viene dada por el contenido del mismo. Es decir, el número de instrucciones a bajo nivel ejecutadas en el cuerpo del bucle suele ser muy superior al del cálculo del propio bucle.
En cualquier caso, esta optimización debería preocuparnos únicamente en el caso de ejecutar grandes tareas por lotes o contar con una gran cantidad de visitas en paralelo.
No obstante, y aunque sobre el código final a ejecutar pueda no tener un gran impacto de tiempo, todo suma; sobre todo si es cambiar una costumbre y poner el símbolo de suma antes de la variable en lugar de después.
The post ¡Deja de usar i++ en tus bucles de PHP! first appeared on Dipler.
]]>The post Instalando y utilizando Debugbar en Lumen first appeared on Dipler.
]]>Lumen tiene una gran cantidad de ventajas con respecto a su hermano mayor si vamos a definir una API. Pero entre las desventajas que tiene es que no siempre se encuentra tanta documentación ni librerías adaptadas.
En este artículo vamos a describir paso a paso cómo crear un proyecto basado en Lumen 8, instalar el plugin de debugbar y configurarlo sin romper la API.
Creación del proyecto
El primer paso es crear el proyecto Lumen. Si ya tienes un proyecto sobre el que desees usar el paquete, puedes pasar al siguiente punto.
~$ composer create-project --prefer-dist laravel/lumen debugbar_lumen
Creating a "laravel/lumen" project at "./debugbar_lumen"
Installing laravel/lumen (v8.1.1)
- Installing laravel/lumen (v8.1.1): Extracting archive
Created project in ./20210712 Debugbar laravel/debugbar_lumen
> @php -r "file_exists('.env') || copy('.env.example', '.env');"
Loading composer repositories with package information
Updating dependencies
Lock file operations: 106 installs, 0 updates, 0 removals
- Locking brick/math (0.9.2)
- Locking doctrine/inflector (2.0.3)
- Locking doctrine/instantiator (1.4.0)
- Locking doctrine/lexer (1.2.1)
- Locking dragonmantank/cron-expression (v3.1.0)
- Locking egulias/email-validator (2.1.25)
- Locking fakerphp/faker (v1.15.0)
- Locking graham-campbell/result-type (v1.0.1)
- Locking hamcrest/hamcrest-php (v2.0.1)
- Locking illuminate/auth (v8.49.2)
- Locking illuminate/broadcasting (v8.49.2)
- Locking illuminate/bus (v8.49.2)
- Locking illuminate/cache (v8.49.2)
- Locking illuminate/collections (v8.49.2)
- Locking illuminate/config (v8.49.2)
- Locking illuminate/console (v8.49.2)
- Locking illuminate/container (v8.49.2)
- Locking illuminate/contracts (v8.49.2)
- Locking illuminate/database (v8.49.2)
- Locking illuminate/encryption (v8.49.2)
- Locking illuminate/events (v8.49.2)
- Locking illuminate/filesystem (v8.49.2)
- Locking illuminate/hashing (v8.49.2)
- Locking illuminate/http (v8.49.2)
- Locking illuminate/log (v8.49.2)
- Locking illuminate/macroable (v8.49.2)
- Locking illuminate/pagination (v8.49.2)
- Locking illuminate/pipeline (v8.49.2)
- Locking illuminate/queue (v8.49.2)
- Locking illuminate/session (v8.49.2)
- Locking illuminate/support (v8.49.2)
- Locking illuminate/testing (v8.49.2)
- Locking illuminate/translation (v8.49.2)
- Locking illuminate/validation (v8.49.2)
- Locking illuminate/view (v8.49.2)
- Locking laravel/lumen-framework (v8.2.4)
- Locking mockery/mockery (1.4.3)
- Locking monolog/monolog (2.3.0)
- Locking myclabs/deep-copy (1.10.2)
- Locking nesbot/carbon (2.50.0)
- Locking nikic/fast-route (v1.3.0)
- Locking nikic/php-parser (v4.11.0)
- Locking opis/closure (3.6.2)
- Locking phar-io/manifest (2.0.1)
- Locking phar-io/version (3.1.0)
- Locking phpdocumentor/reflection-common (2.2.0)
- Locking phpdocumentor/reflection-docblock (5.2.2)
- Locking phpdocumentor/type-resolver (1.4.0)
- Locking phpoption/phpoption (1.7.5)
- Locking phpspec/prophecy (1.13.0)
- Locking phpunit/php-code-coverage (9.2.6)
- Locking phpunit/php-file-iterator (3.0.5)
- Locking phpunit/php-invoker (3.1.1)
- Locking phpunit/php-text-template (2.0.4)
- Locking phpunit/php-timer (5.0.3)
- Locking phpunit/phpunit (9.5.6)
- Locking psr/container (1.1.1)
- Locking psr/event-dispatcher (1.0.0)
- Locking psr/log (1.1.4)
- Locking psr/simple-cache (1.0.1)
- Locking ramsey/collection (1.1.3)
- Locking ramsey/uuid (4.1.1)
- Locking sebastian/cli-parser (1.0.1)
- Locking sebastian/code-unit (1.0.8)
- Locking sebastian/code-unit-reverse-lookup (2.0.3)
- Locking sebastian/comparator (4.0.6)
- Locking sebastian/complexity (2.0.2)
- Locking sebastian/diff (4.0.4)
- Locking sebastian/environment (5.1.3)
- Locking sebastian/exporter (4.0.3)
- Locking sebastian/global-state (5.0.3)
- Locking sebastian/lines-of-code (1.0.3)
- Locking sebastian/object-enumerator (4.0.4)
- Locking sebastian/object-reflector (2.0.4)
- Locking sebastian/recursion-context (4.0.4)
- Locking sebastian/resource-operations (3.0.3)
- Locking sebastian/type (2.3.4)
- Locking sebastian/version (3.0.2)
- Locking symfony/console (v5.3.2)
- Locking symfony/deprecation-contracts (v2.4.0)
- Locking symfony/error-handler (v5.3.3)
- Locking symfony/event-dispatcher (v5.3.0)
- Locking symfony/event-dispatcher-contracts (v2.4.0)
- Locking symfony/finder (v5.3.0)
- Locking symfony/http-client-contracts (v2.4.0)
- Locking symfony/http-foundation (v5.3.3)
- Locking symfony/http-kernel (v5.3.3)
- Locking symfony/mime (v5.3.2)
- Locking symfony/polyfill-ctype (v1.23.0)
- Locking symfony/polyfill-intl-grapheme (v1.23.0)
- Locking symfony/polyfill-intl-idn (v1.23.0)
- Locking symfony/polyfill-intl-normalizer (v1.23.0)
- Locking symfony/polyfill-mbstring (v1.23.0)
- Locking symfony/polyfill-php72 (v1.23.0)
- Locking symfony/polyfill-php73 (v1.23.0)
- Locking symfony/polyfill-php80 (v1.23.0)
- Locking symfony/process (v5.3.2)
- Locking symfony/service-contracts (v2.4.0)
- Locking symfony/string (v5.3.3)
- Locking symfony/translation (v5.3.3)
- Locking symfony/translation-contracts (v2.4.0)
- Locking symfony/var-dumper (v5.3.3)
- Locking theseer/tokenizer (1.2.0)
- Locking vlucas/phpdotenv (v5.3.0)
- Locking voku/portable-ascii (1.5.6)
- Locking webmozart/assert (1.10.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 106 installs, 0 updates, 0 removals
- Installing doctrine/inflector (2.0.3): Extracting archive
- Installing symfony/polyfill-php72 (v1.23.0): Extracting archive
- Installing symfony/polyfill-intl-normalizer (v1.23.0): Extracting archive
- Installing symfony/polyfill-intl-idn (v1.23.0): Extracting archive
- Installing doctrine/lexer (1.2.1): Extracting archive
- Installing egulias/email-validator (2.1.25): Extracting archive
- Installing symfony/deprecation-contracts (v2.4.0): Extracting archive
- Installing psr/container (1.1.1): Extracting archive
- Installing fakerphp/faker (v1.15.0): Extracting archive
- Installing symfony/polyfill-php80 (v1.23.0): Extracting archive
- Installing symfony/polyfill-mbstring (v1.23.0): Extracting archive
- Installing symfony/http-foundation (v5.3.3): Extracting archive
- Installing symfony/finder (v5.3.0): Extracting archive
- Installing voku/portable-ascii (1.5.6): Extracting archive
- Installing symfony/translation-contracts (v2.4.0): Extracting archive
- Installing symfony/translation (v5.3.3): Extracting archive
- Installing nesbot/carbon (2.50.0): Extracting archive
- Installing illuminate/macroable (v8.49.2): Extracting archive
- Installing psr/simple-cache (1.0.1): Extracting archive
- Installing illuminate/contracts (v8.49.2): Extracting archive
- Installing illuminate/collections (v8.49.2): Extracting archive
- Installing illuminate/support (v8.49.2): Extracting archive
- Installing illuminate/filesystem (v8.49.2): Extracting archive
- Installing illuminate/session (v8.49.2): Extracting archive
- Installing symfony/polyfill-ctype (v1.23.0): Extracting archive
- Installing phpoption/phpoption (1.7.5): Extracting archive
- Installing graham-campbell/result-type (v1.0.1): Extracting archive
- Installing vlucas/phpdotenv (v5.3.0): Extracting archive
- Installing symfony/var-dumper (v5.3.3): Extracting archive
- Installing symfony/mime (v5.3.2): Extracting archive
- Installing symfony/polyfill-php73 (v1.23.0): Extracting archive
- Installing symfony/http-client-contracts (v2.4.0): Extracting archive
- Installing psr/event-dispatcher (1.0.0): Extracting archive
- Installing symfony/event-dispatcher-contracts (v2.4.0): Extracting archive
- Installing symfony/event-dispatcher (v5.3.0): Extracting archive
- Installing psr/log (1.1.4): Extracting archive
- Installing symfony/error-handler (v5.3.3): Extracting archive
- Installing symfony/http-kernel (v5.3.3): Extracting archive
- Installing symfony/polyfill-intl-grapheme (v1.23.0): Extracting archive
- Installing symfony/string (v5.3.3): Extracting archive
- Installing symfony/service-contracts (v2.4.0): Extracting archive
- Installing symfony/console (v5.3.2): Extracting archive
- Installing nikic/fast-route (v1.3.0): Extracting archive
- Installing illuminate/container (v8.49.2): Extracting archive
- Installing illuminate/pipeline (v8.49.2): Extracting archive
- Installing illuminate/bus (v8.49.2): Extracting archive
- Installing illuminate/events (v8.49.2): Extracting archive
- Installing illuminate/view (v8.49.2): Extracting archive
- Installing illuminate/translation (v8.49.2): Extracting archive
- Installing illuminate/validation (v8.49.2): Extracting archive
- Installing illuminate/testing (v8.49.2): Extracting archive
- Installing symfony/process (v5.3.2): Extracting archive
- Installing ramsey/collection (1.1.3): Extracting archive
- Installing brick/math (0.9.2): Extracting archive
- Installing ramsey/uuid (4.1.1): Extracting archive
- Installing opis/closure (3.6.2): Extracting archive
- Installing illuminate/database (v8.49.2): Extracting archive
- Installing illuminate/console (v8.49.2): Extracting archive
- Installing illuminate/queue (v8.49.2): Extracting archive
- Installing illuminate/pagination (v8.49.2): Extracting archive
- Installing monolog/monolog (2.3.0): Extracting archive
- Installing illuminate/log (v8.49.2): Extracting archive
- Installing illuminate/http (v8.49.2): Extracting archive
- Installing illuminate/hashing (v8.49.2): Extracting archive
- Installing illuminate/encryption (v8.49.2): Extracting archive
- Installing illuminate/config (v8.49.2): Extracting archive
- Installing illuminate/cache (v8.49.2): Extracting archive
- Installing illuminate/broadcasting (v8.49.2): Extracting archive
- Installing illuminate/auth (v8.49.2): Extracting archive
- Installing webmozart/assert (1.10.0): Extracting archive
- Installing dragonmantank/cron-expression (v3.1.0): Extracting archive
- Installing laravel/lumen-framework (v8.2.4): Extracting archive
- Installing hamcrest/hamcrest-php (v2.0.1): Extracting archive
- Installing mockery/mockery (1.4.3): Extracting archive
- Installing phpdocumentor/reflection-common (2.2.0): Extracting archive
- Installing phpdocumentor/type-resolver (1.4.0): Extracting archive
- Installing phpdocumentor/reflection-docblock (5.2.2): Extracting archive
- Installing sebastian/version (3.0.2): Extracting archive
- Installing sebastian/type (2.3.4): Extracting archive
- Installing sebastian/resource-operations (3.0.3): Extracting archive
- Installing sebastian/recursion-context (4.0.4): Extracting archive
- Installing sebastian/object-reflector (2.0.4): Extracting archive
- Installing sebastian/object-enumerator (4.0.4): Extracting archive
- Installing sebastian/global-state (5.0.3): Extracting archive
- Installing sebastian/exporter (4.0.3): Extracting archive
- Installing sebastian/environment (5.1.3): Extracting archive
- Installing sebastian/diff (4.0.4): Extracting archive
- Installing sebastian/comparator (4.0.6): Extracting archive
- Installing sebastian/code-unit (1.0.8): Extracting archive
- Installing sebastian/cli-parser (1.0.1): Extracting archive
- Installing phpunit/php-timer (5.0.3): Extracting archive
- Installing phpunit/php-text-template (2.0.4): Extracting archive
- Installing phpunit/php-invoker (3.1.1): Extracting archive
- Installing phpunit/php-file-iterator (3.0.5): Extracting archive
- Installing theseer/tokenizer (1.2.0): Extracting archive
- Installing nikic/php-parser (v4.11.0): Extracting archive
- Installing sebastian/lines-of-code (1.0.3): Extracting archive
- Installing sebastian/complexity (2.0.2): Extracting archive
- Installing sebastian/code-unit-reverse-lookup (2.0.3): Extracting archive
- Installing phpunit/php-code-coverage (9.2.6): Extracting archive
- Installing doctrine/instantiator (1.4.0): Extracting archive
- Installing phpspec/prophecy (1.13.0): Extracting archive
- Installing phar-io/version (3.1.0): Extracting archive
- Installing phar-io/manifest (2.0.1): Extracting archive
- Installing myclabs/deep-copy (1.10.2): Extracting archive
- Installing phpunit/phpunit (9.5.6): Extracting archive
46 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating optimized autoload files
62 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
Ua vez tenemos instalado correctamente Lumen, vamos a comprobar que el proyecto se ha generado correctamente:
~$ cd debugbar_lumen
~$ php -S localhost:8000 -t public
PHP 7.3.27 Development Server started at Mon Jul 12 22:36:12 2021
Listening on http://localhost:8000
Document root is /.../debugbar_lumen/public
Press Ctrl-C to quit.
Usando Composer instalamos la dependencia de DebugBar
~$ composer require barryvdh/laravel-debugbar --dev
Using version ^3.6 for barryvdh/laravel-debugbar
./composer.json has been updated
Running composer update barryvdh/laravel-debugbar
Loading composer repositories with package information
Updating dependencies
Lock file operations: 5 installs, 0 updates, 0 removals
- Locking barryvdh/laravel-debugbar (v3.6.2)
- Locking illuminate/routing (v8.49.2)
- Locking maximebf/debugbar (v1.16.5)
- Locking symfony/debug (v4.4.25)
- Locking symfony/routing (v5.3.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 5 installs, 0 updates, 0 removals
- Installing symfony/debug (v4.4.25): Extracting archive
- Installing maximebf/debugbar (v1.16.5): Extracting archive
- Installing symfony/routing (v5.3.0): Extracting archive
- Installing illuminate/routing (v8.49.2): Extracting archive
- Installing barryvdh/laravel-debugbar (v3.6.2): Extracting archive
7 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating optimized autoload files
65 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
Ahora tenemos por objetivo poder indicar cuándo queremos que se muestren los datos del Debugbar en nuestra API. Para ello modificaremos el fichero .env y el fichero .env.example (para que si alguien más instala nuestra aplicación tenga una referencia) y pondremos la variable APP_DEBUG a true y una nueva variable llamada DEBUGBAR_ENABLED que permitirá habilitar y deshabilitar la barra de debug. La barra de debug se mostrará únicamente cuando este valor sea verdadero.
Abrimos el fichero bootstrap/app.php y habilitamos, si APP_DEBUG=true y la petición no se está ejecutando desde consola la inyección del provider:
if (!$app->runningInConsole() && config('APP_DEBUG')) {
$app->register(Barryvdh\Debugbar\LumenServiceProvider::class);
}
Esta comprobación de que ejecutamos desde consola nos permite bypasear determinados errores que puede dar debugbar en Lumen, como:
Argument 2 passed to Barryvdh\Debugbar\ServiceProvider::Barryvdh\Debugbar\{closure}() must be an instance of Illuminate\Foundation\Application, instance of Laravel\Lumen\Application given, called in ./vendor/illuminate/container/Container.php on line 763 {«exception»:»[object] (TypeError(code: 0): Argument 2 passed to Barryvdh\\Debugbar\\ServiceProvider::Barryvdh\\Debugbar\\{closure}() must be an instance of Illuminate\\Foundation\\Application, instance of Laravel\\Lumen\\Application given, called in ./vendor/illuminate/container/Container.php on line 763 at ./vendor/barryvdh/laravel-debugbar/src/ServiceProvider.php:59)
Finalmente copiaremos el fichero de configuración en la carpeta config:
~$ mkdir config
~$ cp vendor/barryvdh/laravel-debugbar/config/debugbar.php config
Y, de nuevo, en el fichero bootstrap/app.php, realizaremos un cambio para activar este fichero de configuración. Para ello añadiremos la siguiente línea:
$app->configure('debugbar');
Ha llegado el momento de comprobar que no hemos cometido ningún error. Para ello vamos a volver a levantar el servidor:
~$ php -S localhost:8000 -t public
PHP 7.3.27 Development Server started at Mon Jul 12 22:36:12 2021
Listening on http://localhost:8000
Document root is /.../debugbar_lumen/public
Press Ctrl-C to quit.
Y con curl o con un programa similar a Postman vamos a realizar una petición al servidor:
El problema es que estamos definiendo una API y queremos que devuelva, en este caso, los datos en JSON. Por ello vamos a hacer un pequeño cambio en el fichero routes/web.php, convirtiendo:
$router->get('/', function () use ($router) {
return $router->app->version();
});
en
$router->get('/', function () use ($router) {
return response()->json(['version' => $router->app->version()]);
});
De este modo, repitiendo la misma llamada que antes, obtendremos el siguiente valor por respuesta:
Si ahora habilitásemos la Debugbar, estaríamos generando una respuesta JSON inválida, así que vamos a incluir el resultado en json de respuesta. Para ello vamos a crear el fichero app/Http/Middleware/JsonDebugbar.php con el siguiente código extraído de un ticket cerrado del propio proyecto:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\JsonResponse;
class JsonDebugbar
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$response = $next($request);
if (
$response instanceof JsonResponse &&
app()->bound('debugbar') &&
app('debugbar')->isEnabled() &&
is_object($response->getData())
) {
$response->setData($response->getData(true) + [
'_debugbar' => app('debugbar')->getData(),
]);
}
return $response;
}
}
Vamos a registrar el nuevo middleware a nivel de ruta. Para hacer esto tenemos dos opciones: en el fichero bootstrap/app.php añadiendo la siguiente línea:
$app->routeMiddleware([
'jsonDebugbar' => App\Http\Middleware\JsonDebugbar::class,
// more middlewares to be declared
]);
Si ahora realizamos otra petición a través de postman, veremos que aun no se aplican los cambios, porque la ruta en cuestión necesita invocar al middleware.
Para hacer esto modificaremos el fichero routes/web.php e introduciremos el grupo que invoque al middleware:
$router->group(['middleware' => 'jsonDebugbar'], function () use ($router) {
$router->get('/', function () use ($router) {
return response()->json(['version' => $router->app->version()]);
});
});
Para ello modificaremos el fichero bootstrap/app.php y añadiremos el middleware:
$app->middleware([
App\Http\Middleware\JsonDebugbar::class
]);
Si una vez invocado el middleware realizamos una petición a través de postman, podremos observar como somos capaces de ver el detalle de la petición con:
The post Instalando y utilizando Debugbar en Lumen first appeared on Dipler.
]]>The post Dipler vuelve después de 10 años first appeared on Dipler.
]]>Estos años han sido años de grandes cambios y muy interesantes que. Espero que los próximos 5 sean como mínimo tan interesantes. No obstante considero que, de no haberme centrado en ellas, probablemente no hubiesen sucedido. Como dicen, «la suerte existe, pero tiene que pillarte trabajando».
En este tiempo he trabajado como:
Esta especialización en el ámbito sanitario (a sabiendas de que aun me queda mucho por aprender en el sector), ha sido una de las mejores decisiones que he tomado en este periodo de tiempo. Ésta ha venido impulsada especialmente por dos proyectos muy interesantes y de los que espero ir contando novedades próximamente:
In3ator es un proyecto nacido a raíz del FabAcademy2015 (USP CEU) y el Master de Ingeniería Biomédica que cursé en 2014-2015.
El proyecto consiste en diseñar y fabricar una incubadora neonatal de bajo coste fiable, accesible, reparable y replicable en cualquier lugar del mundo. Os dejo una pequeña presentación por si queréis echarle un vistazo al proyecto.
Este proyecto que comenzó como un mero proyecto académico evolucionó en la creación de una asociación llamada Medical Open World. Permitiendo al proyecto obtener premios:
y ser invitado a dar charlas y congresos:
No obstante, lo más bonito y destacable del proyecto es aportar un granito de arena para ayudar a aquellos que lo necesiten.
Este proyecto nació en 2015 como un proyecto humilde y con pocas pretensiones y, a día de hoy, 5 años después, puedo decir que, ninguno de los voluntarios involucrados en el mismo nos pudimos imaginar al punto al que hemos llegado.
El otro gran proyecto en el que me embarqué hace ya 5 años es el de Madrija, una empresa toledana de recién creación que aspiraba a buscarse un hueco en el sector salud. Unos años después ya somos más de 30 personas. Y, no sin una gran cantidad de esfuerzo entre medias, se puede decir que estamos consiguiendo posicionarnos en el sector. Principalmente en el ámbito de la cardiología y del I+D+i.
The post Dipler vuelve después de 10 años first appeared on Dipler.
]]>The post Ordenar tablas sin usar macros [Excel] first appeared on Dipler.
]]>El secreto de la productividad utilizando en esta herramienta no es otra que conseguir maximizar la automatización de tareas. Por ello voy a intentar describir paso a paso como ordenar una lista sin tener que hacer ni un solo clic.
Si bien es cierto que esto se puede hacer “fácilmente” haciendo uso de una macro, vamos a ver los pasos a seguir para hacer lo mismo mezclando dos funciones de Excel: RANK y VLOOKUP.
En primer lugar vamos a crear una tabla con información, por ejemplo los valores de la prima de riesgo de distintos países frente a Alemania (datos del 19 de mayo de 2014):
País | Prima de Riesgo |
España | 164,00 |
Reino Unido | 123,00 |
Francia | 46,00 |
Italia | 175,00 |
Portugal | 238,00 |
Estados Unidos | 119,00 |
Suiza | – 59,00 |
Japón | – 74,00 |
Australia | 237,00 |
Austria | 23,00 |
Finlandia | 31,00 |
Grecia | 538,00 |
Holanda | 31,00 |
India | 751,00 |
Irlanda | 132,00 |
Nueva Zelanda | 297,00 |
Suecia | 49,00 |
Canadá | 93,00 |
Ahora vamos a establecer el ranking de cada país teniendo en cuenta su prima de riesgo. Para ello, en la columna de la izquierda (es importante que sea en la de la izquierda, ya que posteriormente utilizaremos la función VLOOKUP) de la primera fila escribiremos la siguiente fórmula:
=RANK.EQ(G7;$G$7:$G$24;1)
RANK.EQ es una función que nos dará el orden de un valor dentro de un listado de valores. La coletilla EQ hará que en caso de empate se muestre el mismo número dos veces; en este caso Finlandia y Holanda tienen la misma Prima de Riesgo y están en cuarto lugar, por lo que asignará a ambas el valor de empate a 4 (En caso de querer ver la media de los valores de la posición, en este caso 4,5 en lugar de 4, deberemos hacer uso de RANK.AVG). Este empate más adelante tendremos que resolverlo para poder ordenar correctamente la lista.
Para la función RANK.EQ:
Finalmente, para obtener las posiciones asociadas a todos los valores aplicamos la fórmula a cada fila.
Tras completar estos pasos, nuestra tabla debería ser parecida a la siguiente:
Ranking | País | Prima de Riesgo |
12 | España | 164,00 |
10 | Reino Unido | 123,00 |
6 | Francia | 46,00 |
13 | Italia | 175,00 |
15 | Portugal | 238,00 |
9 | Estados Unidos | 119,00 |
2 | Suiza | – 59,00 |
1 | Japón | – 74,00 |
14 | Australia | 237,00 |
3 | Austria | 23,00 |
4 | Finlandia | 31,00 |
17 | Grecia | 538,00 |
4 | Holanda | 31,00 |
18 | India | 751,00 |
11 | Irlanda | 132,00 |
16 | Nueva Zelanda | 297,00 |
7 | Suecia | 49,00 |
8 | Canadá | 93,00 |
Ahora sólo nos queda ordenar la tabla. Para lo que haremos uso de la función VLOOKUP.
Para ello, lo primero que vamos a hacer es escribir en la columna de la izquierda de nuestra nueva tabla el orden en el que deseamos que aparezca nuestra tabla. En nuestro caso vamos a ordenarlo en orden ascendente, por lo que en la primera columna deberemos escribir una enumeración de 1 a 18, marcando así cada una de las filas.
Para cada fila escribiremos en la columna contigua a la enumeración:
=VLOOKUP($I7;$E$7:$G$24;2;FALSE)
De manera que:
Repetimos el proceso con la tercera columna para tener el valor de la prima de riesgo asociada al país y listo. Ya tenemos nuestra lista ordenada.
Si no tenemos valores repetidos, ya hemos terminado, pero este no es nuestro caso, nos vamos a encontrar con errores al aplicarle a la tabla la función VLOOKUP porque tenemos dos países con la misma prima de Riesgo.
Ranking | País | Prima de Riesgo |
1 | Japón | – 74,00 |
2 | Suiza | – 59,00 |
3 | Austria | 23,00 |
4 | Finlandia | 31,00 |
5 | #N/A | #N/A |
6 | Francia | 46,00 |
7 | Suecia | 49,00 |
8 | Canadá | 93,00 |
9 | Estados Unidos | 119,00 |
10 | Reino Unido | 123,00 |
11 | Irlanda | 132,00 |
12 | España | 164,00 |
13 | Italia | 175,00 |
14 | Australia | 237,00 |
15 | Portugal | 238,00 |
16 | Nueva Zelanda | 297,00 |
17 | Grecia | 538,00 |
18 | India | 751,00 |
Llegados a este punto tenemos varias opciones:
Vamos a proceder a explicar el segundo método aun a sabiendas de que no siempre será posible seguir estos pasos antes de aplicar la función RANK.EQ.
*NOTA: lo que voy a contar a continuación podría calificarse de chapuza (útil y resolutiva, eso sí). Es posible que existan otras formas mejores de hacerlo sin usar macros.
=C7+0,0001*E7
De este modo, deberíamos obtener un resultado final satisfactorio como el que se puede observar en la siguiente tabla:
Ranking | País | Prima de Riesgo |
1 | Japón | – 74,00 |
2 | Suiza | – 59,00 |
3 | Austria | 23,00 |
4 | Finlandia | 31,00 |
5 | Holanda | 31,00 |
6 | Francia | 46,00 |
7 | Suecia | 49,00 |
8 | Canadá | 93,00 |
9 | Estados Unidos | 119,00 |
10 | Reino Unido | 123,00 |
11 | Irlanda | 132,00 |
12 | España | 164,00 |
13 | Italia | 175,00 |
14 | Australia | 237,00 |
15 | Portugal | 238,00 |
16 | Nueva Zelanda | 297,00 |
17 | Grecia | 538,00 |
18 | India | 751,00 |
Finalmente, podéis descargar el archivo Excel que se ha utilizado para realizar este artículo.
Ordenado automático de registros.xslx (844 descargas )The post Ordenar tablas sin usar macros [Excel] first appeared on Dipler.
]]>The post [PHP & MySQL] Tratar correctamente los acentos y demás first appeared on Dipler.
]]>Pero antes de proponer cada una de las soluciones por separado, vamos a ver un ejemplo de lo que estamos hablando: Supongamos que en nuestra página web hay un campo de entrada de datos, y en ella el usuario escribe:
Café Roma
Acto seguido pulsa el botón «Aceptar» para guardar ese texto en la base de datos. Todo parece ir bien, pero al recuperarlo de la base de datos y mostrarlo por pantalla vemos que escribe algo parecido a lo siguiente:
Café Roma
O, incluso
Caf� Roma
A grosso modo, esto ocurre porque estamos mezclando codificaciones uno o más sitios (navegador del usuario, servidor Apache con PHP instalado y la base de datos MySQL). Esto es un problema muy común y del que, mucha gente pregunta en foros todos los días y, por ello vamos a proponer soluciones para, en un principio mostrarlo en un navegador de forma correcta.
HTML tiene una serie de caracteres especiales y reservados que tienen una función especial, un claro ejemplo son los caracteres mayor > y menor <, ya que, por ejemplo, estos caracteres se utilizan para delimitar las etiquetas XML en las que está basada la especificación HTML. Por ello, HTML especifica una serie de códigos llamados «Entidades HTML» que nos permiten codificar estos caracteres especiales. Por ello si queremos poner el símbolo <, realmente deberíamos escribir < o bien <. En el primer caso estaríamos indicando ese carácter usando un código alfanumérico y, en el segundo, estamos indicando el valor de dicho carácter en ASCII.
Como hacer esta conversión manualmente no siempre es posible y, en el caso de que si que lo sea, es bastante tedioso escribir usando esos códigos, PHP nos brinda una función que hará el trabajo sucio por nosotros: htmlentities; de manera que con invocar la función:
$str = htmlentities($str);
De manera que ahora en nuestra variable $str en lugar de tener, por ejemplo, «Camión», que podría darnos problemas al guardarlo en una base de datos si el sistema está mal configurado (más adelante veremos como configurarlo bien), tendríamos una variable que contiene «Camión», es decir, un string compuesto únicamente por caracteres ASCII que no debería darnos problema a la hora de persistirlo en cualquier base de datos; tenga el cotejamiento que tenga.
Finalmente, si vamos a mostrar el código por pantalla, tenemos dos opciones:
Lo que hemos visto en el apartado anterior, no deja de ser una chapuza (para la mayor parte de los casos) y que se puede solucionar de una forma bastante sencilla y, es que, si configuramos bien nuestro servidor Apache con PHP, nuestra base de datos MySQL y la conexión entre ambas, podemos ahorrarnos todo este tratamiento de datos; y no solo eso, sino que además, nos permitirá consultar cómodamente los datos almacenados desde cualquier otro sistema sin tener que perocuparnos de deshacer la codificación de caracteres extraños.
Vamos a ver los pasos para realizar esta configuración directamente:
SET NAMES 'utf8'&nbsp;
&nbsp;&lt;meta http-equiv="Content-type" content="text/html; charset=utf-8" /&gt;&nbsp;
The post [PHP & MySQL] Tratar correctamente los acentos y demás first appeared on Dipler.
]]>The post Importar los datos de un archivo CSV – Matlab first appeared on Dipler.
]]>MATLAB es una herramienta estupenda para tratar con grandes cantidades de datos de manera sencilla y, por eso mismo, es utilizado para desarrollar los algoritmos deseados (como por ejemplo uno de detección de pasos) antes de implementarlos en el dispositivo final. De esta manera eliminamos el factor humano al implementar algunas funciones que vienen de serie en MATLAB como, por ejemplo, una FFT (Fast Fourier Transform).
El primer paso para hacer esto es importar los datos en RAW (en crudo). Estos datos estarán formateados de una u otra manera que puede depender de nosotros o no. En mi caso, estoy trabajando con un formato CSV que separa cada uno de las «columnas» con un punto y coma (semicolon) en lugar de una coma; esto hace que no pueda hacer uso fácilmente de la función por defecto de MATLAB para abrir este tipo de archivos.
Por ello, vamos a aprender como importar datos en un formato cualquiera dado siempre que este simule a una tabla:
path = './pruebas/Sensor_record_20120731_214243_AndroSensor.csv'; formats = '%f%f%f%f%f%f%f%f%f%f'; headerLines = 1; delimiter = ';'; [M{1:10}] = textread(path, formats,'headerlines', headerLines, 'delimiter', delimiter);
En la primera línea indicamos la ruta en la que almacenamos nuestros datos, en la segunda indicamos el formato de cada uno de los campo; en este caso estamos indicando que tenemos 10 campos y que todos ellos son un número decimal. En la tercera línea indicamos que tenemos una línea de cabecera y que ha de saltarse, esto es especialmente interesante cuando las primeras líneas no siguen el formato del resto del fichero CSV o si, simplemente, queremos obviar esas lineas y no importarlas. En la cuarta indicamos nuestro delimitados. Y, finalmente, en la sexta, le decimos que nos meta en un CellArray de 10 posiciones los datos de dicho archivo., de manera que trabajar con los datos de la primera columna es algo tan sencillo como:
plot(M{1});
The post Importar los datos de un archivo CSV – Matlab first appeared on Dipler.
]]>The post Cuando dejas de ser amigo de Google first appeared on Dipler.
]]>El título puede llevar a malentendidos. No me he «peleado» con Google, él conmigo tampoco; pero el momento que sospechaba que llegaría en un momento u otro, finalmente lo hizo. El castigo ha llegado en forma de un descenso del 90% de las visitas del blog.
Este bajón de visitas no ha venido dado porque el ranking de la web haya descendido, tampoco por entrar en las blacklist de los buscadores; ha sido culpa del hackeo que sufrió el blog en los últimos 15 días. Como ya os comenté, el blog fue reinstalado desde 0 con la intención de cargarlo con los mismos datos. La forma de hacer el importado y exportado de datos (con el fin de eliminar los problemas que pudiese haber en la base datos) fue mediante las utilidades que vienen por defecto en WordPress y que utilizan XML para mover los datos. Esto hizo que las direcciones url, entre otras cosas, cambiasen, de modo que, por ejemplo: https://www.dipler.org/2009/07/%C2%BFcomo-funciona-facebook/ pasase a tener un enlace como https://www.dipler.org/2009/07/como-funciona-facebook/ . Objetivamente, podemos decir que, ahora, tenemos urls más limpias pero, por otro lado, ha aumentado muy significativamente el número de errores 404 de la web.
En definitiva, hemos pasado del rango de 300-500 visitas diarias al de 20-50 visitas diarias, es decir, la web ha sufrido un descenso de al más del 90% en lo que a número de visitas se refiere. Esto había que arreglarlo como sea, y no valía dejar que Google reindexase todas las nuevas direcciones, ya que el blog estaba enlazado por varias webs, entre ellas por la wikipedia y varias universidades.
Analizando el listado de errores 404, la mayor parte de ellos venían dados por los caracteres ¿ y ¡, así que jugando un poco con los ficheros .htaccess, he hecho que ese problema desaparezca.
RewriteRule ^(.*)¿+(.*)$ $1$2 [R=301,L]
RewriteRule ^(.*)¡+(.*)$ $1$2 [R=301,L]
Finalmente y, con la intención de eliminar los errores 404 de rastreo introducidos por la famosa puerta de atrás que ha causado todos estos problemas, me dispongo a redirigir todo el tráfico a a la página principal.
RewriteRule ^(.*)wpbuzz(.*)$ $1 [R=301,L]
Ahora le toca «mover ficha» a Google y volver a mostrarme una media de 10.000 veces diarias en sus resultados y no las 1.000 que hace ahora (y bajando)
The post Cuando dejas de ser amigo de Google first appeared on Dipler.
]]>