Blackhold https://blackhold.nusepas.com $this->brain_dump(); Tue, 30 Jan 2024 16:20:53 +0000 ca hourly 1 https://wordpress.org/?v=6.4.3 9799399 Script para reiniciar nginx cuando han caducado los certificados de letsencrypt https://blackhold.nusepas.com/2024/01/30/script-para-reiniciar-nginx-cuando-han-caducado-los-certificados-de-letsencrypt/ https://blackhold.nusepas.com/2024/01/30/script-para-reiniciar-nginx-cuando-han-caducado-los-certificados-de-letsencrypt/#comments Tue, 30 Jan 2024 16:20:53 +0000 https://blackhold.nusepas.com/?p=6255 Últimamente estoy de scripts para automatizar mi vida de sysadmin ;) aquí os dejo otro que comprueba si se han renovado los certificados y reinicia nginx en caso de que si haya ocurrido

# vi /root/scripts/check_certificates2.sh

#!/bin/bash

ADMIN_MAIL=""
DOMAINS=($(nginx -T |grep server_name |grep ";" |grep -v "#" |awk '{print $2}' |sed 's/;//' |grep -v "_" | sort -u))
CHECK_DIR="/root/scripts/check_domains"
REBOOT_NGINX=0

mkdir -p ${CHECK_DIR}

for i in "${DOMAINS[@]}"
do
   if [[ -f ${CHECK_DIR}/$i ]]; then
       OLD=`cat ${CHECK_DIR}/$i |awk '{print $1}'`
       CURRENT=`sha1sum /etc/letsencrypt/live/${i}/cert.pem |awk '{print $1}'`
       if [[ ${OLD} != ${CURRENT} ]]; then
           echo "hashes don't match for domain ${i}, please restart nginx"
           REBOOT_NGINX=1
           sha1sum /etc/letsencrypt/live/${i}/cert.pem ${CHECK_DIR}/$i
       fi
   else
       if [[ -f /etc/letsencrypt/live/${i}/cert.pem ]]; then
           sha1sum /etc/letsencrypt/live/${i}/cert.pem ${CHECK_DIR}/$i
       else
           echo "Domain ${i} has no certificate file"
       fi
   fi
done

if [[ ${REBOOT_NGINX} == "1" ]]; then
    echo "I reboot nginx due there are changes on certificates"
    service nginx restart
fi

Luego hago que se ejecute cada 10 minutos y listos

# vi /etc/crontab
*/10 *  * * *   root    /root/scripts/check_certificates2.sh
…]]>
Últimamente estoy de scripts para automatizar mi vida de sysadmin ;) aquí os dejo otro que comprueba si se han renovado los certificados y reinicia nginx en caso de que si haya ocurrido

# vi /root/scripts/check_certificates2.sh

#!/bin/bash

ADMIN_MAIL=""
DOMAINS=($(nginx -T |grep server_name |grep ";" |grep -v "#" |awk '{print $2}' |sed 's/;//' |grep -v "_" | sort -u))
CHECK_DIR="/root/scripts/check_domains"
REBOOT_NGINX=0

mkdir -p ${CHECK_DIR}

for i in "${DOMAINS[@]}"
do
   if [[ -f ${CHECK_DIR}/$i ]]; then
       OLD=`cat ${CHECK_DIR}/$i |awk '{print $1}'`
       CURRENT=`sha1sum /etc/letsencrypt/live/${i}/cert.pem |awk '{print $1}'`
       if [[ ${OLD} != ${CURRENT} ]]; then
           echo "hashes don't match for domain ${i}, please restart nginx"
           REBOOT_NGINX=1
           sha1sum /etc/letsencrypt/live/${i}/cert.pem > ${CHECK_DIR}/$i
       fi
   else
       if [[ -f /etc/letsencrypt/live/${i}/cert.pem ]]; then
           sha1sum /etc/letsencrypt/live/${i}/cert.pem > ${CHECK_DIR}/$i
       else
           echo "Domain ${i} has no certificate file"
       fi
   fi
done

if [[ ${REBOOT_NGINX} == "1" ]]; then
    echo "I reboot nginx due there are changes on certificates"
    service nginx restart
fi

Luego hago que se ejecute cada 10 minutos y listos

# vi /etc/crontab
*/10 *  * * *   root    /root/scripts/check_certificates2.sh
]]>
https://blackhold.nusepas.com/2024/01/30/script-para-reiniciar-nginx-cuando-han-caducado-los-certificados-de-letsencrypt/feed/ 1 6255
Script para realizar un sha1sum recursivo de los ficheros un directorio https://blackhold.nusepas.com/2024/01/23/script-para-realizar-un-sha1sum-recursivo-de-los-ficheros-un-directorio/ https://blackhold.nusepas.com/2024/01/23/script-para-realizar-un-sha1sum-recursivo-de-los-ficheros-un-directorio/#comments Tue, 23 Jan 2024 18:44:52 +0000 https://blackhold.nusepas.com/?p=6251 Volvemos a la carga con mas scripts de bash. Esta vez necesito realizar un sha1sum para obtener el hash sha1 de unos ficheros que están dentro de muchos directorios. Como sha1sum no soporta la opción recursiva (en mi opinión un fallo), no queda otra que recurrir a nuestro amado bash.

Aquí el script para la ocasión

#!/bin/bash

DIRECTORY="material"

find "$DIRECTORY" -type d -print0 -maxdepth 4 | while read -r -d '' d; do
  echo "#### SHA1SUM Directorio: $d #####"
  sha1sum $d/*
done
…]]>
Volvemos a la carga con mas scripts de bash. Esta vez necesito realizar un sha1sum para obtener el hash sha1 de unos ficheros que están dentro de muchos directorios. Como sha1sum no soporta la opción recursiva (en mi opinión un fallo), no queda otra que recurrir a nuestro amado bash.

Aquí el script para la ocasión

#!/bin/bash

DIRECTORY="material"

find "$DIRECTORY" -type d -print0 -maxdepth 4 | while read -r -d '' d; do
  echo "#### SHA1SUM Directorio: $d #####"
  sha1sum $d/*
done
]]>
https://blackhold.nusepas.com/2024/01/23/script-para-realizar-un-sha1sum-recursivo-de-los-ficheros-un-directorio/feed/ 2 6251
script para dividir pdf en pdf mas pequeños con pdftk o pdfjam https://blackhold.nusepas.com/2024/01/05/script-para-dividir-pdf-en-pdf-mas-pequenos-con-pdftk-o-pdfjam/ https://blackhold.nusepas.com/2024/01/05/script-para-dividir-pdf-en-pdf-mas-pequenos-con-pdftk-o-pdfjam/#respond Fri, 05 Jan 2024 18:09:27 +0000 https://blackhold.nusepas.com/?p=6237 Mi buen amigo Bruno, hace unos días me pasó una extracción de un teléfono móvil con la friolera de más de 51.000 páginas. Mi pobre y humilde portátil sacaba humo sólo al abrirlo así que he tenido que ingeniármelas para dividir el fichero en ficheros más pequeños y poder gestionar la información cómodamente. La solución, un script.

El script tiene dos opciones, usar pdftk o pdfjam. Deberemos comentar y descomentar la línea que nos interese.

Si queremos usar pdftk será necesario instalarlo

# apt install pdftk

Para usar pdfjam, que me ha procesado la información más rápido que con pdftk, será necesario instalar texlive-extra-utils

# apt install texlive-extra-utils

Otra herramienta que será necesario instalar es pdfinfo que se encuentra dentro del paquete poppler-utils

# apt install poppler-utils

A continuación el script

#!/bin/bash

INPUT_FILE=Redmi7-ASPERTIC.pdf
PAGES=`pdfinfo ${INPUT_FILE} |grep Pages |awk -F '           ' '{print $2}'`
FILENAME=`basename ${INPUT_FILE} |awk -F '.' '{print $1}'`
SPLIT=500
START=1
echo "Total Pages: ${PAGES}"
echo 

COUNTER=$START
ITER=1

while [ "$COUNTER" -le "$PAGES" ]
do
    START=$(($COUNTER))
    END=$(($START + $SPLIT - 1))
    echo 
    echo "#################"
    echo "start ${START} end ${END} iter ${ITER}"
    if [[ "$PAGES" -le "$END" ]]
    then
       END=$PAGES
       echo "last end is ${END} of total pages ${PAGES}"
    fi
    #pdftk ${INPUT_FILE} cat ${START}-${END} output ${FILENAME}_${ITER}.pdf
    pdfjam ${INPUT_FILE} ${START}-${END} -o ${FILENAME}_${ITER}.pdf
    COUNTER=$(($COUNTER + $SPLIT))
    ITER=$(($ITER + 1))
done

Y listos, lo mismo de siempre, crear el fichero, darle permisos de ejecución y ejecutarlo!

Aquí una captura del final de la ejecución del script sobre el susodicho fichero

Una utilidad …

]]>
Mi buen amigo Bruno, hace unos días me pasó una extracción de un teléfono móvil con la friolera de más de 51.000 páginas. Mi pobre y humilde portátil sacaba humo sólo al abrirlo así que he tenido que ingeniármelas para dividir el fichero en ficheros más pequeños y poder gestionar la información cómodamente. La solución, un script.

El script tiene dos opciones, usar pdftk o pdfjam. Deberemos comentar y descomentar la línea que nos interese.

Si queremos usar pdftk será necesario instalarlo

# apt install pdftk

Para usar pdfjam, que me ha procesado la información más rápido que con pdftk, será necesario instalar texlive-extra-utils

# apt install texlive-extra-utils

Otra herramienta que será necesario instalar es pdfinfo que se encuentra dentro del paquete poppler-utils

# apt install poppler-utils

A continuación el script

#!/bin/bash

INPUT_FILE=Redmi7-ASPERTIC.pdf
PAGES=`pdfinfo ${INPUT_FILE} |grep Pages |awk -F '           ' '{print $2}'`
FILENAME=`basename ${INPUT_FILE} |awk -F '.' '{print $1}'`
SPLIT=500
START=1
echo "Total Pages: ${PAGES}"
echo 

COUNTER=$START
ITER=1

while [ "$COUNTER" -le "$PAGES" ]
do
    START=$(($COUNTER))
    END=$(($START + $SPLIT - 1))
    echo 
    echo "#################"
    echo "start ${START} end ${END} iter ${ITER}"
    if [[ "$PAGES" -le "$END" ]]
    then
       END=$PAGES
       echo "last end is ${END} of total pages ${PAGES}"
    fi
    #pdftk ${INPUT_FILE} cat ${START}-${END} output ${FILENAME}_${ITER}.pdf
    pdfjam ${INPUT_FILE} ${START}-${END} -o ${FILENAME}_${ITER}.pdf
    COUNTER=$(($COUNTER + $SPLIT))
    ITER=$(($ITER + 1))
done

Y listos, lo mismo de siempre, crear el fichero, darle permisos de ejecución y ejecutarlo!

Aquí una captura del final de la ejecución del script sobre el susodicho fichero

Una utilidad muy interesante después para buscar dentro de los pdf es usar pdfgrep

$ pdfgrep -Ri 666666666 *
]]>
https://blackhold.nusepas.com/2024/01/05/script-para-dividir-pdf-en-pdf-mas-pequenos-con-pdftk-o-pdfjam/feed/ 0 6237
Instalar dudle con debian 12 https://blackhold.nusepas.com/2023/12/14/instalar-dudle-con-debian-12/ https://blackhold.nusepas.com/2023/12/14/instalar-dudle-con-debian-12/#respond Thu, 14 Dec 2023 17:42:45 +0000 https://blackhold.nusepas.com/?p=6219 Por fin otra tarea menos! Esta ya daba vergüenzilla y todo al tener la instalación anterior de dudle aún con Debian 6!!! Así que directamente lo que he planteado es hacer una instalación limpia y los datos que puedan estar ahí, que pasen a mejor vida! Aunque creo que los datos aún serían compatibles, parece que tienen el mismo formato.

Vamos a ver como se hace ahora la instalación. Para ello he seguido el repositorio de github del proyecto que omite la información de la instalación y configuración de apache.

Primero vamos a instalar las dependencias:

# apt -y install apache2 git ruby-dev libxml2-dev zlib1g-dev
# gem install ratom

A continuación clonamos el repositorio en algún sitio del servidor, en mi caso lo he dejado en /var/www/dudle

# cd /var/www
# git clone https://github.com/kellerben/dudle.git

Ahora vamos a descargar los ficheros .mo correspondientes a los ficheros de idioma compilados. Vamos a crear un script, le damos permisos de ejecución, lo ejecutamos y finalmente borramos el fichero.

# cd dudle/locale

# vi download_mo.sh
for i in ??; do
	wget -O $i/dudle.mo https://dudle.inf.tu-dresden.de/locale/`basename $i`/dudle.mo
done

# chmod +x download_mo.sh

# ./download_mo.sh

# rm download_mo.sh

Ahora configuramos apache:

# cd /etc/apache2/sites-available

# vi dudle.conf
<VirtualHost *:80>
        ServerName      dudle.marsupi.org
        ServerAlias     *.dudle.marsupi.org dudle.lamardebits.org *.dudle.lamardebits.org dudle.lamardebits.cat *.dudle.lamardebits.cat
        ServerAdmin     xxx@lamardebits.org
        DocumentRoot    /var/www/dudle/

        ErrorLog        /var/log/apache2/dudle.error.log
        CustomLog       /var/log/apache2/dudle.access.log combined

	<Directory "/var/www/dudle">
		AllowOverride All

    		SetEnv RUBYLIB /var/www/dudle/
    		SetEnv RUBYOPT "-E UTF-8:UTF-8"
    		SetEnv GIT_AUTHOR_NAME="Blackhold"
    		SetEnv GIT_AUTHOR_EMAIL=xxx@domain.com
    		SetEnv GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
    		SetEnv GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"

		Order allow,deny
		Allow from all
	</Directory>
</VirtualHost>

# cd /etc/apache2/sites-enabled/; rm 
…]]>
Por fin otra tarea menos! Esta ya daba vergüenzilla y todo al tener la instalación anterior de dudle aún con Debian 6!!! Así que directamente lo que he planteado es hacer una instalación limpia y los datos que puedan estar ahí, que pasen a mejor vida! Aunque creo que los datos aún serían compatibles, parece que tienen el mismo formato.

Vamos a ver como se hace ahora la instalación. Para ello he seguido el repositorio de github del proyecto que omite la información de la instalación y configuración de apache.

Primero vamos a instalar las dependencias:

# apt -y install apache2 git ruby-dev libxml2-dev zlib1g-dev
# gem install ratom

A continuación clonamos el repositorio en algún sitio del servidor, en mi caso lo he dejado en /var/www/dudle

# cd /var/www
# git clone https://github.com/kellerben/dudle.git

Ahora vamos a descargar los ficheros .mo correspondientes a los ficheros de idioma compilados. Vamos a crear un script, le damos permisos de ejecución, lo ejecutamos y finalmente borramos el fichero.

# cd dudle/locale

# vi download_mo.sh
for i in ??; do
	wget -O $i/dudle.mo https://dudle.inf.tu-dresden.de/locale/`basename $i`/dudle.mo
done

# chmod +x download_mo.sh

# ./download_mo.sh

# rm download_mo.sh

Ahora configuramos apache:

# cd /etc/apache2/sites-available

# vi dudle.conf
<VirtualHost *:80>
        ServerName      dudle.marsupi.org
        ServerAlias     *.dudle.marsupi.org dudle.lamardebits.org *.dudle.lamardebits.org dudle.lamardebits.cat *.dudle.lamardebits.cat
        ServerAdmin     xxx@lamardebits.org
        DocumentRoot    /var/www/dudle/

        ErrorLog        /var/log/apache2/dudle.error.log
        CustomLog       /var/log/apache2/dudle.access.log combined

	<Directory "/var/www/dudle">
		AllowOverride All

    		SetEnv RUBYLIB /var/www/dudle/
    		SetEnv RUBYOPT "-E UTF-8:UTF-8"
    		SetEnv GIT_AUTHOR_NAME="Blackhold"
    		SetEnv GIT_AUTHOR_EMAIL=xxx@domain.com
    		SetEnv GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
    		SetEnv GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"

		Order allow,deny
		Allow from all
	</Directory>
</VirtualHost>

# cd /etc/apache2/sites-enabled/; rm *; ln -s ../sites-available/dudle.conf .

Habilitamos los módulos de apache que necesitamos, comprobamos que no falla nada y reiniciamos apache

# a2enmod rewrite cgid
# apache2ctl configtest
# service apache2 restart

Una recomendación que nos hacen a la documentación de instalación es de configurar un script en el cron

# vi /etc/crontab
# clean old dudle polls
0 0	* * *	www-data /var/www/dudle/dudle_cleanup.sh

A continuación volvemos al directorio de dudle y copiamos el fichero de configuración config_sample.rb de ejemplo a config.rb, en mi caso para poner mi propia hoja de estilos

# cd /var/www/dudle
# cp config_sambple.rb config.rb
# vi config.rb
$conf.default_css = "css/marsupi.css"
# mkdir css
# vi css/marsupi.css
/***************************************************************************
* Copyright 2011 Benjamin Kellermann                                       *
*                                                                          *
* This file is part of dudle.                                              *
*                                                                          *
* Dudle is free software: you can redistribute it and/or modify it under   *
* the terms of the GNU Affero General Public License as published by       *
* the Free Software Foundation, either version 3 of the License, or        *
* (at your option) any later version.                                      *
*                                                                          *
* Dudle is distributed in the hope that it will be useful, but WITHOUT ANY *
* WARRANTY; without even the implied warranty of MERCHANTABILITY or        *
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public     *
* License for more details.                                                *
*                                                                          *
* You should have received a copy of the GNU Affero General Public License *
* along with dudle.  If not, see .           *
***************************************************************************/

/* head */

body {
        padding: 0px;
        color: black;
        font: 70% Verdana,Helvetica,Arial,sans-serif;
        background-color: #CCC;
        margin: .5em .5em 1em;
        border: white 1px solid;
}

#header1 {
        background-color:#AA0000;
        width:20px;
        height:72px;
        float:left;
        margin: 0;
}
#header2{
        background-color:#AA0000;
        padding: 20px 0 5px 300px;
        background-image:url(https://capa8.net/wp-content/uploads/2020/03/petit.png);
        background-position:left 20px;
        background-repeat:no-repeat;
        height:65px;
        float:left;
/*  margin: 0 1.275em 0 0;*/
}
#header3 {
        background-color:#AA0000;
        height:92px;
        padding: 0;
        margin: 0;
}

#breadcrumbs {
        min-height:1.37em;
        padding: .4em 0 .4em 68px;
        background-color: #000;
        color: #C6D3DF;
}
#breadcrumbs a {
        color: #C6D3DF;
}

/* main content */


#main{
        background-color: white;
        border: 1px solid #EEE;
}

label { cursor: pointer; }

#tabs{
        float:left;
        padding-top: 1.5625em;
        width: 15em;
}
#tabs ul{
        list-style:none;
        margin: 0;
        margin-top:.5em;
        padding: 0;
}

#tabs li.nonactive_tab , #tabs li.active_tab{
        background-color: #FFF;
        background-image: url(tud/big_emptynode_close.gif);
        background-repeat: no-repeat;
        background-position: left;
        color: #333;
        display: block;
        text-decoration: none;
        margin: 0;
        padding: .3em .3em .4em 0;
        padding-left: 28px;
}

#tabs li.active_tab, #tabs li.nonactive_tab:hover {
        background-color: #CCC;
}
#tabs li.active_tab{
        background-image: url(tud/big_emptynode_open.gif);
}

#tabs li.nonactive_tab a {
        border-bottom: none;
        color: #333;
}
#tabs li.separator_tab {
        border-top: 1px solid #ccc;
}

#content{
        margin-top:0;
        margin-left: 15em;
        padding-top: 1.5625em;
        padding-left: 1em;
        padding-bottom: 1.25em;
        line-height: 1.5em;
        min-height: 20em;
        border-left: 1px solid #ccc;
}

#tabs:before {
        content:"dudle";
        margin: 0 0 0 1em;
        padding: 0 8em .375em 0;
        min-width: 15em;
}



/* content */

.invisible {
        border:none;
        visibility: hidden;
}

.textcolumn{
        text-align: left;
        max-width: 45em;
}

.shorttextcolumn{
        text-align: left;
        max-width: 20em;
}

h1, h2, h3, #tabs:before {
        font-size: 1em;
        font-weight: 700;
        letter-spacing: .1em;
        text-transform: uppercase;
}

h1, #tabs:before {
        border-bottom: dotted 1px #333;
        color: #333;
        line-height: normal;
}
h1 {
        margin: 0 0 1.875em -.5625em;
        padding: 0 .5625em .375em;
}

h2, h3{
        color: #666;
        font-style: normal;
        margin-bottom: .5em;
        margin-top: .5em;
}

h3 {
        font-style: italic;
        font-weight: 700;
        letter-spacing: .05em;
        text-transform: none;
}

a, a:active {
        color: #40779E;
        text-decoration: none;
}

a:hover {
        text-decoration: underline;
}

td {
        vertical-align:middle;
        text-align:center;
}
th, td.navigation, td.sum {
        padding: 0.25em 0.5em 0.25em 0.5em;
        background-color: #CCC;
/*  border: #999 1px solid;*/
        border: none;
}
th a {
        border: none;
}
td.sum{ color: #FFF}
td.match_100{background-color: #860000; }
td.match_90{ background-color: #1D355B; }
td.match_80{ background-color: #2F4067; }
td.match_70{ background-color: #3F4F74; }
td.match_60{ background-color: #515D80; }
td.match_50{ background-color: #636C8E; }
td.match_40{ background-color: #777F9E; }
td.match_30{ background-color: #8B94B1; }
td.match_20{ background-color: #A8AFC7; }
td.match_10{ background-color: #CDD4E2; }
td.match_0{  background-color: #D9DCE2; }


#active, #active input.delete {
        background-color:#89b6df;
}
pre#configwarning {
        font-family: "Courier New",Courier,monospace;
        letter-spacing:0;
        margin-top: -12ex;
        line-height:95%;
        margin-left: -2ex;
}

.warning, .error{
        color: #E87B14;
}

td.separator_top{
        border-top: dotted 1px #333;
        padding-top:0.7ex;
}

th.weekday{
        width: 2.5em;
        padding: 0.25em 0.5em 0.25em 0.5em;
}

input.navigation, input.disabled, input.chosen, input.notchosen, input.delete, input.toggle {
        border: none;
        padding: 0px;
        margin: 0px;
        cursor: pointer;
        min-height: 1.5em;
}
input.navigation, input.disabled, input.chosen, input.notchosen, input.toggle {
        width: 100%;
}
input.toggle {
        padding:0 0.5em 0 0.5em;
}
.navigation, input.delete, input.toggle {
        background-color: #CCC;
} 
a.editcolumn {
        color: black;
}
.navigation:hover, #active input.delete:hover, input.delete:hover, a.editcolumn:hover, input.toggle:hover {
        background-color: #ffff69;
}
input.chosen { 
        background-color: #fac600;
}

td.a_yes__, table.checkboxes tr.input-a_yes__ td { 
        background-color: #D3E7C5;
}
input.disabled, td.undecided {
        color: #999;
}
td.c_no___, table.checkboxes tr.input-c_no___ td { 
        background-color: #F4E0E8;
}
td.b_maybe, table.checkboxes tr.input-b_maybe td { 
        background-color: #FDF2D1;

}

input.notchosen, input.disabled {
        background-color: #FFF;
}
input.notchosen:hover, input.chosen:hover, input.disabled:hover {
        background-color: #ffff69;
}


td.settingstable, table.settingstable td{
        text-align: left;
}
table.settingstable td.label{
        text-align: right;
}

table.calendarday td{
        border-spacing: 0;
        margin: 0;
        padding: 0;
        border: #999 1px solid;
}
table.calendarday td.navigation{
        padding: 0 .5em 0 .5em;
        border: none;
}
table.calendarday td.invisible, table.calendarday td.addColumnManual {
        border: none;
}
table.polltable {
        padding: 10px;
        border-collapse: separate;
        border-spacing: 2px;
}
#polltable {
        overflow: auto;
}
table, table td {
        border: none;
}

table.polltable td {
        padding: 0.25em 0.5em 0.25em 0.5em;
        margin: 1em;
}

td.name {
        text-align: right;
}
table.polltable tr.participantrow:hover {
        background-color: #E5EBF2;
}
table.polltable td.checkboxes {
        padding:0;
        margin: 0;
}

table.checkboxes {
        border-spacing: 0;
        width: 100%;
}

table.polltable tr td.input-radio {
        border-right:none;
}
table.polltable tr td.input-label {
        border-left:none;
}

tr#add_participant {
        background-color: #f5f9fc;
}

form#ac_participant, form#ac {
        background-color: #E5EBF2;
}
form#ac_participant, form#ac, form#ac_admin{
        padding: 1em;
}

div#wizzard_navigation {
        text-align: center;
}

div#languageChooser {
        position:absolute;
        top:15px;
        padding:0;
        margin:0;
        right: 1.2em;
        color: #000;
        font-weight: 700;
}
div#languageChooser li, #breadcrumbs li, #footer1 li {
        list-style:none;
        display: inline;
}
#breadcrumbs ul {
        padding:0;
        margin:0;
}
li.lang ~ li.lang:before {
        content:"| ";
}
li.breadcrumb ~ li.breadcrumb:before {
        content: " » ";
}

#footer1 {
        background-color: #860000;
        height: 2.15em;
        border: none;
}
#footer1 a {
        font-weight: bold;
        color: #fff;
        text-decoration: none;
        margin-right: 1.25em;
}
#footer1 ul {
        padding: .4em 1.25em;
        margin: 0;
}

div#languageChooser a {
        border: none;
        color: #FFF;
}
div#languageChooser a:hover {
        text-decoration: underline;
}

Para que todo funcione correctamente, le damos permisos a www-data a todo el directorio

# cd /var/www
# chown -R www-data:www-data dudle

Y listos! ya podemos acceder a dudle por http. En esta configuración lo he expuesto sólo al puerto 80 porqué los certificados lo gestiono en un frontal con nginx.

PS: Justo al usar la primera encuesta me sale el error “An error occurred while executing DuD-Poll.”, para ello será necesario ejecutar:

# cd /var/www/dudle
# git config --global --add safe.directory /var/www/dudle
# git grep -l YAML::load | xargs sed s/YAML::load/YAML::unsafe_load/ -i
]]>
https://blackhold.nusepas.com/2023/12/14/instalar-dudle-con-debian-12/feed/ 0 6219
Thinkpad no suspende con debian 12 (bookworm) https://blackhold.nusepas.com/2023/11/12/thinkpad-no-suspende-con-debian-12-bookworm/ https://blackhold.nusepas.com/2023/11/12/thinkpad-no-suspende-con-debian-12-bookworm/#respond Sun, 12 Nov 2023 21:20:47 +0000 https://blackhold.nusepas.com/?p=6205 Otra vez he encontrado el tiempo para reinstalar el sistema, 4 meses más tarde, nada mal!

Vamos, pues con una nueva versión del “Thinkpad no suspende con debian” pero ahora para debian 12 llamada bookworm.

Seguí el artículo pero no servía, así que para solucionar el problema de suspensión con esta nueva versión es distinto. Olvida todo lo otro del otro artículo.

Al iniciar me salía este error:

de nov. 12 00:55:59 melatonina kernel: hpet_acpi_add: no address or irqs in _CRS
de nov. 12 00:55:59 melatonina kernel: Linux agpgart interface v0.103
de nov. 12 00:55:59 melatonina kernel: DMAR: DRHD: handling fault status reg 3
de nov. 12 00:55:59 melatonina kernel: DMAR: [DMA Read NO_PASID] Request device [00:12.4] fault addr 0xd7fff000 [fault reason 0x02] Present bde nov. 12 00:55:59 melatonina kernel: tsc: Refined TSC clocksource calibration: 2592.007 MHz
de nov. 12 00:55:59 melatonina kernel: clocksource: tsc: mask: 0xffffffffffffffff max_cycles: 0x255cbd4609f, max_idle_ns: 440795221428 ns
de nov. 12 00:55:59 melatonina kernel: clocksource: Switched to clocksource tsc
de nov. 12 00:55:59 melatonina kernel: tpm tpm0: Operation Timed out
de nov. 12 00:55:59 melatonina kernel: tpm tpm0: Operation Timed out
de nov. 12 00:55:59 melatonina kernel: tpm_crb: probe of MSFT0101:00 failed with error -62
de nov. 12 00:55:59 melatonina kernel: AMD-Vi: AMD IOMMUv2 functionality not available on this system - This is not a bug.

La solución:

# vi /etc/default/grub
GRUB_CMDLINE_LINUX="intel_iommu=off"

Recargamos la configuración de grub

# grub-mkconfig -o /boot/grub/grub.cfg

Reiniciamos y listos, funcionando!…

]]>
Otra vez he encontrado el tiempo para reinstalar el sistema, 4 meses más tarde, nada mal!

Vamos, pues con una nueva versión del “Thinkpad no suspende con debian” pero ahora para debian 12 llamada bookworm.

Seguí el artículo pero no servía, así que para solucionar el problema de suspensión con esta nueva versión es distinto. Olvida todo lo otro del otro artículo.

Al iniciar me salía este error:

de nov. 12 00:55:59 melatonina kernel: hpet_acpi_add: no address or irqs in _CRS
de nov. 12 00:55:59 melatonina kernel: Linux agpgart interface v0.103
de nov. 12 00:55:59 melatonina kernel: DMAR: DRHD: handling fault status reg 3
de nov. 12 00:55:59 melatonina kernel: DMAR: [DMA Read NO_PASID] Request device [00:12.4] fault addr 0xd7fff000 [fault reason 0x02] Present b>
de nov. 12 00:55:59 melatonina kernel: tsc: Refined TSC clocksource calibration: 2592.007 MHz
de nov. 12 00:55:59 melatonina kernel: clocksource: tsc: mask: 0xffffffffffffffff max_cycles: 0x255cbd4609f, max_idle_ns: 440795221428 ns
de nov. 12 00:55:59 melatonina kernel: clocksource: Switched to clocksource tsc
de nov. 12 00:55:59 melatonina kernel: tpm tpm0: Operation Timed out
de nov. 12 00:55:59 melatonina kernel: tpm tpm0: Operation Timed out
de nov. 12 00:55:59 melatonina kernel: tpm_crb: probe of MSFT0101:00 failed with error -62
de nov. 12 00:55:59 melatonina kernel: AMD-Vi: AMD IOMMUv2 functionality not available on this system - This is not a bug.

La solución:

# vi /etc/default/grub
GRUB_CMDLINE_LINUX="intel_iommu=off"

Recargamos la configuración de grub

# grub-mkconfig -o /boot/grub/grub.cfg

Reiniciamos y listos, funcionando!

]]>
https://blackhold.nusepas.com/2023/11/12/thinkpad-no-suspende-con-debian-12-bookworm/feed/ 0 6205
Django runserver sobre https https://blackhold.nusepas.com/2023/10/02/django-runserver-sobre-https/ https://blackhold.nusepas.com/2023/10/02/django-runserver-sobre-https/#respond Mon, 02 Oct 2023 19:01:30 +0000 https://blackhold.nusepas.com/?p=6191 Estos dos últimos meses he estado trabajando con una tienda virtual desarrollada con django llamada django oscar. Muy interesante el juguete, tunearla ha habido momentos de todo, en algunos momentos era una delicia por la documentación que tienen y por otros iba más perdida que un pingüino en el desierto y los atascos eran monumentales además de la presión del cliente que tenía que tener el proyecto en funcionamiento un mes y pico antes de que se me diese acceso al código para poder arreglar y aplicar las funcionalidades extra que se requerían. El proyecto era uno que había dejado a medias otro desarrollador y tuve que rehacer todo el código porqué tal como se me entregó la aplicación había cosas básicas de funcionamiento rotas, además de variables en inglés, en castellano y catalán (no es el primer proyecto que me encuentro así).

Django oscar se usa como aplicación de Backend y de frontend hay desplegado un NextJS que tras dar alguna que otra vuelta he terminado modificando alguna cosa.

En entorno local de desarrollo todo funcionaba correctamente, pero al ponerlo en producción la cosa se ha puesto divertida y ha requerido investigar varias cosas que voy a exponer en este post en modo de notas.

La aplicación de frontend se conecta con django oscar por API y hasta que no añadí la opción CSRF_TRUSTED_ORIGINS la comunicación era infructuosa.

# vi settings.py
# this allows nextjs to connect to django without using csrf_token
CSRF_TRUSTED_ORIGINS = 'http://127.0.0.1'
CSRF_COOKIE_SAMESITE = "None"
…]]>
Estos dos últimos meses he estado trabajando con una tienda virtual desarrollada con django llamada django oscar. Muy interesante el juguete, tunearla ha habido momentos de todo, en algunos momentos era una delicia por la documentación que tienen y por otros iba más perdida que un pingüino en el desierto y los atascos eran monumentales además de la presión del cliente que tenía que tener el proyecto en funcionamiento un mes y pico antes de que se me diese acceso al código para poder arreglar y aplicar las funcionalidades extra que se requerían. El proyecto era uno que había dejado a medias otro desarrollador y tuve que rehacer todo el código porqué tal como se me entregó la aplicación había cosas básicas de funcionamiento rotas, además de variables en inglés, en castellano y catalán (no es el primer proyecto que me encuentro así).

Django oscar se usa como aplicación de Backend y de frontend hay desplegado un NextJS que tras dar alguna que otra vuelta he terminado modificando alguna cosa.

En entorno local de desarrollo todo funcionaba correctamente, pero al ponerlo en producción la cosa se ha puesto divertida y ha requerido investigar varias cosas que voy a exponer en este post en modo de notas.

La aplicación de frontend se conecta con django oscar por API y hasta que no añadí la opción CSRF_TRUSTED_ORIGINS la comunicación era infructuosa.

# vi settings.py
# this allows nextjs to connect to django without using csrf_token
CSRF_TRUSTED_ORIGINS = 'http://127.0.0.1'
CSRF_COOKIE_SAMESITE = "None"
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = "None"

Hasta aquí, la comunicación en local bien, el nextjs se conecta a django por http://127.0.0.1:8000.

Al montar el uwsgi para django (instalar entorno de producción de python), cambiando la url de comunicación al dominio expuesto a internet de django-oscar, adiós comunicación. Se ha probado de levantar una instancia de django con “python manage.py runserver” para que se levantase http://127.0.0.1:8000 la comunicación PUT y POST tampoco funcionaba (creo, y creo porqué no se ha podido llegar a probar por los errores de página con mixto http y https) y además las imágenes en el frontend se exponían con http://127.0.0.1:8000 y evidentemente no eran accesibles desde fuera del servidor.

Se ha tratado de poner el CSRF_TRUSTED_ORIGINS a varias configuraciones (IP pública, IP interna, dominio del frontend) y nada, incluso se ha puesto * y tampoco.

Otra prueba que se ha hecho y motivo por el cuál hago este post por parecerme una solución curiosa aunque poco recomendable para poner un django o aplicación de python en producción es levantar el runserver pero sobre https

Para ello será necesario instalar

# pip install django-extensions Werkzeug pyOpenSSL
# pip freeze > requirements.txt
# apt -y install python3-openssl # esto no sé si realmente es necesario si se instala el pyOpenSSL

Luego en settings.py se añade en INSTALLED_APPS el django-extensions

# vi settings.py
INSTALLED_APPS = [
     ....
     "django_extensions",
]

Luego se ejecuta el runserver así (como ya tenía certificados de letsencrypt le he puesto los certificados)

# python manage.py runserver_plus --cert-file /etc/letsencrypt/live/tgcback.xxx.com/cert.pem --key-file /etc/letsencrypt/live/tgcback.xxx.com/privkey.pem tgcback.xxx.com:8000

Pero no había nada accesible a la URL en el puerto 8000. Así que he vuelto al post donde nos plantean usar mkcert para generar certificados autofirmados

# apt -y install mkcert
# mkcert -cert-file cert.pem -key-file key.pem localhost 127.0.0.1

Y he ejecutado el runserver_plus así

python manage.py runserver_plus --cert-file /root/certs/cert.pem --key-file /root/certs/key.pem 0.0.0.0:8000

Era accesible pero evidentemente la comunicación era infructuosa por un problema de certificados.

He probado también con nginx machacar el https autofirmado así

   location / {
    proxy_pass https://127.0.0.1:8000; 
    proxy_ssl_server_name on;

No ha funcionado tampoco la comunicación, pero también lo dejo aquí como nota interesante a tener en cuenta cuando pueda ser necesario.

Y aquí lo dejo, de momento no hemos hallado la solución para que funcione el PUT y el POST en producción sin exponer ips locales que evidentemente no sirven. Mi propuesta es de modificar las URL en nextjs.

Hasta el momento contenta de haber descubierto cosillas nuevas, pero frustrada de aún no tener la aplicación funcionando en producción según lo esperado y un trabajo que se está alargando más de lo que me gustaría.

]]>
https://blackhold.nusepas.com/2023/10/02/django-runserver-sobre-https/feed/ 0 6191
Conectar moodle y wordpress https://blackhold.nusepas.com/2023/08/29/conectar-moodle-y-wordpress/ https://blackhold.nusepas.com/2023/08/29/conectar-moodle-y-wordpress/#respond Tue, 29 Aug 2023 21:44:49 +0000 https://blackhold.nusepas.com/?p=6172 Hay algunas tareas de estas que se convierten en titánicas y no sabes muy bien el porqué y esta lo ha sido. Así que traigo este post para que otra persona que quiera montar este sistema no tenga que dar tantas vueltas y poder hacerlo a la primera :)

Hace unos meses que estamos preparando cursos bajo la marca escueladeeuropa. Los cursos la idea es montarlos en el LMS Moodle (learning management system), pero luego quedaba la cosa como los vendíamos… nos dijimos, sería chulo que los cursos se vendiesen solos a través de la página web hecha con wordpress… y ahí empezó la carrera.

Conectar wordpress y moodle es posible gracias a dos plugins, el primero edwiser y el segundo lmsace. El primero es mucho más intuitivo pero a la que quieres empezar a conectarlo con woocommerce, paga. El segundo… ¡ay el segundo! ¡vamos a verlo!

Concepto
La idea es que en tu moodle tienes unos cursos y en tu wordpress el woocommerce. Para conectar moodle y wordpress es necesario primero de todo crear los usuarios en wordpress que luego se crearán automáticamente en moodle. Esto se llama “user enrolment”.

Para conectar ambos programas será necesario instalar 1 plugin en wordpress y (atención) 2 en moodle! todo lo que necesitas saber lo encontrarás en la wiki de lmsace.

Moodle

Empezamos con moodle, será necesario instalar los dos plugins, lmsace-connect-moodle y lmsace-connect-moodleauth. El segundo aunque en la wiki pone que es opcional, hasta que no lo he …

]]>
Hay algunas tareas de estas que se convierten en titánicas y no sabes muy bien el porqué y esta lo ha sido. Así que traigo este post para que otra persona que quiera montar este sistema no tenga que dar tantas vueltas y poder hacerlo a la primera :)

Hace unos meses que estamos preparando cursos bajo la marca escueladeeuropa. Los cursos la idea es montarlos en el LMS Moodle (learning management system), pero luego quedaba la cosa como los vendíamos… nos dijimos, sería chulo que los cursos se vendiesen solos a través de la página web hecha con wordpress… y ahí empezó la carrera.

Conectar wordpress y moodle es posible gracias a dos plugins, el primero edwiser y el segundo lmsace. El primero es mucho más intuitivo pero a la que quieres empezar a conectarlo con woocommerce, paga. El segundo… ¡ay el segundo! ¡vamos a verlo!

Concepto
La idea es que en tu moodle tienes unos cursos y en tu wordpress el woocommerce. Para conectar moodle y wordpress es necesario primero de todo crear los usuarios en wordpress que luego se crearán automáticamente en moodle. Esto se llama “user enrolment”.

Para conectar ambos programas será necesario instalar 1 plugin en wordpress y (atención) 2 en moodle! todo lo que necesitas saber lo encontrarás en la wiki de lmsace.

Moodle

Empezamos con moodle, será necesario instalar los dos plugins, lmsace-connect-moodle y lmsace-connect-moodleauth. El segundo aunque en la wiki pone que es opcional, hasta que no lo he instalado no funcionaba nada! aquí tienes los ficheros zip que debes descargarte:

A continuación a moodle, vas a Admin > Plugins > Install plugins y subes los dos ficheros zip y sigues las instrucciones.

Lo siguiente será generar un token para conectar el wordpress con el moodle desde Admin > Plugins > Local plugins > LMSACE Connect y pulsas a la pestaña Generate token.

Ahora lo siguiente será permitir que los usuarios con rol estudiante puedan hacer login por el protocolo REST. Para ello será necesario ir a Admin > Users > Permissions > Define Roles > Student (engranaje) y buscar “webservice/rest:use” y decirle “Allow”. Luego guardar.

Ahora cogemos el token que nos ha generado el plugin y vamos al wordpress

WordPress

En wordpress he instalado los plugins Woocommerce, user registration y los he configurado.

En woocommerce en Ajustes > Cuentas y privacidad, he desmarcado la casillas referentes a “pago como invitado”. Aquí dejo una captura de pantalla para ver como debe quedar

A continuación, descargo e instalo el plugin lmsace para wordpress y lo instalo (github, zip).

Una vez instalado será necesario ir a LMSACE Connect e introducir la URL del moodle y el token que nos ha generado y darle al botón “connect”. Si no funciona, es posible que haya un problema de comunicación o otro que me ha hecho perder una gran cantidad de tiempo que el plugin funciona máximo en la versión PHP8.1, a partir de la versión PHP8.2 las funciones que usa el plugin están deprecated y no funcionan. La configuración la estoy haciendo con WordPress 6.3. He dejado en todo momento los enlaces a github del proyecto que parece que está activo (por lo menos al momento de hacer este post la versión 2.0 de los plugins se publicó hace 1 mes).

Una vez sale todo verde, será necesario ir a la pestaña LMSACE Connect > Import courses, seleccionamos los cursos que queramos importar y recomiendo marcar todas las casillas de abajo. Finalmente “Start import courses”.

Ahora los cursos importados aparecerán en Productos. Aquí será necesario añadir un precio para cada uno de ellos.

LMSACE tiene una tercera pestaña de configuración del comportamiento de creación de los usuarios, “General setup” que la documentación de la wiki indica en dejar en suspend los usuarios que cancelen los cursos y asignarlos al rol “student” (que es el que le hemos dado permisos para hacer login con REST).

SSO Activation Method sirve para indicar en qué momento se crea el usuario en el moodle, si al momento de crear el usuario en el wordpress o en el momento de validar el primer curso. En mi caso lo he dejado en la opción “Login on WP” que permite activar la casilla “Redirectless login” que te da la opción según he entendido/visto que una vez pulsado el enlace para acceder al curso desde wordpress, te permite abrir más pestañas con este login.

Luego está la opción “Create users in Moodle on new registration” que nos interesará activarla si queremos que los usuarios que hayan comprado un curso en el wodpress puedan hacer login al moodle.

Finalmente está la última opción que me genera bastante confusión y no sé si lo que he comentado de permitir abrir varias pestañas es de aquí (en manual) o de “Redirectless login”. Disculpadme que aún no entiendo muy bien como funciona esta parte y cuando se aplican las configuraciones si al configurar esto o al vincular el usuario a un curso.

Comprar un curso y proceso de login
Esta parte aún no la tengo muy clara pero tal como lo tengo ahora la cosa va así:

1. Registro del usuario El usuario se registra con la página que hayamos definido con el plugin “User registration” que nos pedirá crear un formulario de registro. En mi caso he creado una página con el slug “registro” con este contenido [user_registration_form id=”422″]

2. Login Una vez el usuario ha hecho el registro, puede hacer login en la página de woocommerce. En mi caso con el slug “cuenta” con este contenido [woocommerce_my_account]. El plugin “User registration” también te da la opción de crear una página de login, pero me quedaré con la de woocommerce que contiene los enlaces que me interesan

3. Acceder a la tienda de cursos Una vez el usuario ha hecho login, puede ir a la página de la tienda (en mi caso he puesto el slug “tienda”) y añadir los productos al carrito. Aquí en la tienda de woocommerce tuve problemas con el plugin Elementor, a la que le ponía la etiqueta para la tienda woocommerce se rompía todo, así que he optado por un elemento de elementor pro para mostrar los productos de woocommerce por categorías y ha quedado bastante digno

4. Comprar curso Una vez el usuario compra un curso (para hacer la prueba he creado un voucher con el mismo precio del producto y así no ha saltado la pasarela de pago, en mi caso SumUp con el plugin SumUp Payment Gateway For WooCommerce). Me ha pedido los datos de facturación (que son un poco coñazo ahí y debo mirar si puedo replantearlo o usar los datos del perfil creado con el plugin “User registration”).

5. Validar curso (admin) Cuando el usuario ha hecho el pedido, este aparece en Woocommerce > pedidos en estado “Procesando” y a la que lo pongo en “Completado” se hace el enrol del usuario en el curso, es decir, se vincula el usuario al curso de moodle y se crea el usuario depende de la configuración que hayamos puesto en el LMSACE (2a pestaña).

6. Acceso curso Ahora el usuario, si va al slug que yo tengo como “cuenta”, podrá ir al apartado “My Courses” y ahí verá los cursos a los que está apuntado y un enlace que lo llevará al moodle.

Una cosa que no he conseguido de ninguna forma (de momento) es que el usuario que he creado con el wordpress pueda acceder al moodle y mirando la configuración del usuario, creo que no se va a poder y es necesario siempre acceder al moodle a través del login de wordpress. Para esta última cosa, me estoy planteando un plugin para ver la actividad de los usuarios y así ver gráficamente el movimiento que hay en la escuela de cursos.

Y hasta aquí mi experiencia! muy contenta de conseguir que funcionase todo, ahora a mejorar la usabilidad que tal como la tengo es necesario un curso y mi idea es que los usuarios accedan a los cursos sin tener que darles un curso de como acceder a los cursos! Espero que en los próximos días vaya viendo mas claro como funciona el juguete.

Espero que os haya sido de ayuda! :)

PS: He dejado un issue al github del plugin con mis dudas.

]]>
https://blackhold.nusepas.com/2023/08/29/conectar-moodle-y-wordpress/feed/ 0 6172
script para identificar registros duplicados en sqlite3 https://blackhold.nusepas.com/2023/06/04/script-para-identificar-registros-duplicados-en-sqlite3/ https://blackhold.nusepas.com/2023/06/04/script-para-identificar-registros-duplicados-en-sqlite3/#respond Sun, 04 Jun 2023 15:05:46 +0000 https://blackhold.nusepas.com/?p=6149 En uno de mis proyectos estamos ya en la recta final a punto de poner en producción el nuevo programa. A último momento siempre te encuentras con aquellas puñetitas que hacen que la migración no salga como es lo esperado, es por esto que es muy importante practicar la migración al igual que si de una audición se tratase.

La puñetita con la que me he encontrado trata precisamente de la importación de datos mas grande, la de una tabla de 4Gb de datos, que contiene las lecturas de unos sensores, he decidido separarlas en alrededor de 4.000 ficheros de base de datos sqlite3, uno para cada uno de los sensores. Al realizar la importación de cada uno de los sensores de forma individual, de maravilla, pero a la que se hace el proceso de migración de todos los sensores, algunas de las lecturas se duplican y no he conseguido hallar el porqué ocurre.

Ya hace unos días puse un par de controles que si el fichero ya existía o la base de datos estaba siendo usada, pasase al siguiente sensor, aún así seguían duplicandose los datos. La solución era añadir un tercer control que comprobase que el registro ya estaba guardado, pero que ralentizaría la migración de los datos de forma significativa y la primera migración el interés es que sea lo más ágil posible. Así que dándole un par de vueltas y muy importante, tras más de 24h de no tocar el código (se llama descansar), he encontrado …

]]>
En uno de mis proyectos estamos ya en la recta final a punto de poner en producción el nuevo programa. A último momento siempre te encuentras con aquellas puñetitas que hacen que la migración no salga como es lo esperado, es por esto que es muy importante practicar la migración al igual que si de una audición se tratase.

La puñetita con la que me he encontrado trata precisamente de la importación de datos mas grande, la de una tabla de 4Gb de datos, que contiene las lecturas de unos sensores, he decidido separarlas en alrededor de 4.000 ficheros de base de datos sqlite3, uno para cada uno de los sensores. Al realizar la importación de cada uno de los sensores de forma individual, de maravilla, pero a la que se hace el proceso de migración de todos los sensores, algunas de las lecturas se duplican y no he conseguido hallar el porqué ocurre.

Ya hace unos días puse un par de controles que si el fichero ya existía o la base de datos estaba siendo usada, pasase al siguiente sensor, aún así seguían duplicandose los datos. La solución era añadir un tercer control que comprobase que el registro ya estaba guardado, pero que ralentizaría la migración de los datos de forma significativa y la primera migración el interés es que sea lo más ágil posible. Así que dándole un par de vueltas y muy importante, tras más de 24h de no tocar el código (se llama descansar), he encontrado la solución, realizar un postprocesamiento de los datos una vez importados.

Una de las problemáticas que tenía es que el id del registro era la fecha en formato unix y milisegundos para procesar los datos en highcharts (que lo pide así).

La solución estos dos scripts de bash:

script1
Este lo uso en modo de control para comprobar si hay ficheros sin registros o con registros duplicados

#!/bin/bash

echo "This scripts checks duplicated rows"
echo

cd ../sqlite/

for p in *.db
do
    NUMBER_ROWS=`sqlite3 "$p" 'select time_ms from sensor_data' |wc -l`
    DUPLICATED_ROWS=`sqlite3 "$p" 'select time_ms from sensor_data group by time_ms having count(*) != 1' |wc -l`

    if [ ${NUMBER_ROWS} == "0" ] || [ ${DUPLICATED_ROWS} -gt 2 ]; then
        echo "### File ${p} must be attended ##########"
        echo "Number of rows: ${NUMBER_ROWS}"       

        if [ ${NUMBER_ROWS} == "0" ]; then
            echo "No data imported"
        fi

        if [ ${DUPLICATED_ROWS} != "0" ]; then
            echo "Duplicated rows: ${DUPLICATED_ROWS}/${NUMBER_ROWS}"
        fi

        echo ""
    fi
done

script2
Este otro, elimina los registros duplicados y guarda solo uno de ellos. Además, para aprovechar mejor el servidor, aquellos ficheros con más de 5000 registros, los mando a Background para usar mas CPU y agilizar el procesamiento

#!/bin/bash

echo "This script searches and deletes duplicated data"

# choose number CPU to use
NUMBER_CPU=3

cd ../sqlite/

for p in *.db
do
    NUMBER_ROWS=`sqlite3 "$p" 'select time_ms from sensor_data' |wc -l`
    DUPLICATED_ROWS=`sqlite3 "$p" 'select time_ms from sensor_data group by time_ms having count(*) != 1' |wc -l`

    if [ ${NUMBER_ROWS} == "0" ] || [ ${DUPLICATED_ROWS} -gt 2 ]; then
        echo "### File ${p} must be attended ##########"

        if [ ${NUMBER_ROWS} == "0" ]; then
            echo "No data imported"
        fi

if [ ${DUPLICATED_ROWS} != "0" ]; then
            echo "Duplicated rows: ${DUPLICATED_ROWS}/${NUMBER_ROWS} - processing delete duplicated"
            NUMBER_BACKGROUND_PROCESSES=`ps aux |grep sqlite |grep DELETE |wc -l`
            if [[ ${NUMBER_ROWS} -gt 5000 ]] && [[ ${NUMBER_BACKGROUND_PROCESSES} -le ${NUMBER_CPU} ]]; then
                echo "More than 5000 rows to manage, sending to background and continue with process"
                sqlite3 "$p" 'DELETE FROM sensor_data WHERE EXISTS (SELECT 1 FROM sensor_data p2 WHERE sensor_data.time_ms = p2.time_ms AND sensor_data.rowid > p2.rowid)' && sqlite3 "$p" 'vacuum' &
            else
                sqlite3 "$p" 'DELETE FROM sensor_data WHERE EXISTS (SELECT 1 FROM sensor_data p2 WHERE sensor_data.time_ms = p2.time_ms AND sensor_data.rowid > p2.rowid)'
                sqlite3 "$p" 'vacuum'
                NEW_NUMBER_ROWS=`sqlite3 "$p" 'select time_ms from sensor_data' |wc -l`
                CONTROL_DUPLICATED_ROWS=`sqlite3 "$p" 'select time_ms from sensor_data group by time_ms having count(*) != 1' |wc -l`
                echo "Process executed, result: ${CONTROL_DUPLICATED_ROWS}/${NEW_NUMBER_ROWS} - done!"
            fi
        fi

        echo ""
    fi
done

El vacuum es para reducir el tamaño de los ficheros.

Y hasta aquí esta pequeña receta a para dar solución a otro de los problemas cotidianos de la vida de un informático.

He optado para usar bash en lugar de python para agilizar el proceso de ejecución y también porqué había la posibilidad de ejecutar comandos sqlite3 de la forma mostrada.

]]>
https://blackhold.nusepas.com/2023/06/04/script-para-identificar-registros-duplicados-en-sqlite3/feed/ 0 6149
Instalar php8.2 en debian 11 https://blackhold.nusepas.com/2023/05/12/instalar-php8-2-en-debian-11/ https://blackhold.nusepas.com/2023/05/12/instalar-php8-2-en-debian-11/#respond Fri, 12 May 2023 13:58:25 +0000 https://blackhold.nusepas.com/?p=6141 La versión de php de debian 11 es la 7.4 y algunas aplicaciones ya piden sólo php 8, así que vamos a ver como instalamos php 8.

# echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" /etc/apt/sources.list.d/php.list
# apt -y install gnupg2
# wget -qO - https://packages.sury.org/php/apt.gpg | apt-key add -
# apt update

Ahora lo siguiente será ver qué librerías tenemos instaladas, instalar las nuevas y cambiar la configuración de apache2 o nginx

# dpkg -l |grep php

Una vez esté todo funcionando, eliminamos las versiones antiguas de php

# apt remove --purge php7.3* php7.4* libapache2-mod-php7.3 libapache2-mod-php7.4
# apt-get clean
…]]>
La versión de php de debian 11 es la 7.4 y algunas aplicaciones ya piden sólo php 8, así que vamos a ver como instalamos php 8.

# echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list
# apt -y install gnupg2
# wget -qO - https://packages.sury.org/php/apt.gpg | apt-key add -
# apt update

Ahora lo siguiente será ver qué librerías tenemos instaladas, instalar las nuevas y cambiar la configuración de apache2 o nginx

# dpkg -l |grep php

Una vez esté todo funcionando, eliminamos las versiones antiguas de php

# apt remove --purge php7.3* php7.4* libapache2-mod-php7.3 libapache2-mod-php7.4
# apt-get clean
]]>
https://blackhold.nusepas.com/2023/05/12/instalar-php8-2-en-debian-11/feed/ 0 6141
Gitlab: configurar el envío de correos electrónicos https://blackhold.nusepas.com/2023/05/10/gitlab-configurar-el-envio-de-correos-electronicos/ https://blackhold.nusepas.com/2023/05/10/gitlab-configurar-el-envio-de-correos-electronicos/#respond Wed, 10 May 2023 11:43:52 +0000 https://blackhold.nusepas.com/?p=6137 Gilab es un software para gestionar repositorios de software. Existe github y gitlab tiene también su repositorio público. Gitlab permite la instalación del software en tu propia infraestructura.

En éste otro post explicaba como instalarlo y actualizarlo con sus repositorios. Hoy veremos como configurar gitlab para que mande correos electrónicos.

Lo primero que tendremos que hacer es modificar el fichero de configuración que se encuentra en /etc/gitlab/gitlab.rb, buscar las siguientes líneas, descomentarlas y configurarlas de la siguiente forma (para que funcione con la configuración del servidor de correo electrónico instalado con modoboa)

# vi /etc/gitlab/gitlab.rb
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "mail.capa8.net"
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = "noreply@capa8.net"
gitlab_rails['smtp_password'] = "xxxxxxxxxxx"
gitlab_rails['smtp_domain'] = "capa8.net"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = false
gitlab_rails['smtp_pool'] = true

gitlab_rails['gitlab_email_enabled'] = true

gitlab_rails['gitlab_email_from'] = 'noreply@capa8.net'
gitlab_rails['gitlab_email_display_name'] = 'GitLab Capa8'
gitlab_rails['gitlab_email_reply_to'] = 'noreply@capa8.net'

A continuación ejecutamos lo siguiente para aplicar la nueva configuración

# gitlab-ctl reconfigure

Una vez realizado el reconfigure, entramos en la consola de gitlab y comprobamos que la configuración se haya aplicado y mandamos un correo electrónico de prueba

# gitlab-rails console -e production
--------------------------------------------------------------------------------
 Ruby:         ruby 3.0.6p216 (2023-03-30 revision 23a532679b) [x86_64-linux]
 GitLab:       15.11.0 (96ee37680b6) FOSS
 GitLab Shell: 14.18.0
 PostgreSQL:   13.8
------------------------------------------------------------[ booted in 32.38s ]
Loading production environment (Rails 6.1.7.2)

irb(main):001:Notify.test_email('info@capa8.net', 'Hello World', 'This is a test message').deliver_now

Y si todo es correcto, deberías recibir un correo electrónico de prueba.…

]]>
Gilab es un software para gestionar repositorios de software. Existe github y gitlab tiene también su repositorio público. Gitlab permite la instalación del software en tu propia infraestructura.

En éste otro post explicaba como instalarlo y actualizarlo con sus repositorios. Hoy veremos como configurar gitlab para que mande correos electrónicos.

Lo primero que tendremos que hacer es modificar el fichero de configuración que se encuentra en /etc/gitlab/gitlab.rb, buscar las siguientes líneas, descomentarlas y configurarlas de la siguiente forma (para que funcione con la configuración del servidor de correo electrónico instalado con modoboa)

# vi /etc/gitlab/gitlab.rb
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "mail.capa8.net"
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = "noreply@capa8.net"
gitlab_rails['smtp_password'] = "xxxxxxxxxxx"
gitlab_rails['smtp_domain'] = "capa8.net"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = false
gitlab_rails['smtp_pool'] = true

gitlab_rails['gitlab_email_enabled'] = true

gitlab_rails['gitlab_email_from'] = 'noreply@capa8.net'
gitlab_rails['gitlab_email_display_name'] = 'GitLab Capa8'
gitlab_rails['gitlab_email_reply_to'] = 'noreply@capa8.net'

A continuación ejecutamos lo siguiente para aplicar la nueva configuración

# gitlab-ctl reconfigure

Una vez realizado el reconfigure, entramos en la consola de gitlab y comprobamos que la configuración se haya aplicado y mandamos un correo electrónico de prueba

# gitlab-rails console -e production
--------------------------------------------------------------------------------
 Ruby:         ruby 3.0.6p216 (2023-03-30 revision 23a532679b) [x86_64-linux]
 GitLab:       15.11.0 (96ee37680b6) FOSS
 GitLab Shell: 14.18.0
 PostgreSQL:   13.8
------------------------------------------------------------[ booted in 32.38s ]
Loading production environment (Rails 6.1.7.2)

irb(main):001:> Notify.test_email('info@capa8.net', 'Hello World', 'This is a test message').deliver_now

Y si todo es correcto, deberías recibir un correo electrónico de prueba.

]]>
https://blackhold.nusepas.com/2023/05/10/gitlab-configurar-el-envio-de-correos-electronicos/feed/ 0 6137