Create a SSH gateway for Git SSH backends

This post shows how to connect to a Gitlab (or any Git SSH server) private server via SSH through a front-end public server you own.

[CLIENT] --> [FRONT-END SSH-SERVER] --> [BACK-END GIT SSH-SERVER]

On Git back-end server

Create the keys for your users as usual (in this example, we assume Gitlab, so the web interface is enough)

Go to the file /var/opt/gitlab/.ssh/authorized_keys and copy all entries. An example of the contents of this file with two users could be:

command="/opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell key-1",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3Nz...rbR6L75887 user1@gmail.com
command="/opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell key-2",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1...hVE/141 user2@hotmail.com

On front-end (intermediate) server

Create the user git, and create and edit the .ssh/authorized_keys file.
sudo adduser git
su git
mkdir .ssh
touch ./ssh/authorized_keys && chmod 700 .ssh/authorized_keys

Paste the contents of the file, but by replacing the “command” in each entry with this content:

command="ssh git@backend-server $SSH_ORIGINAL_COMMAND" ssh-rsa AAAAB3Nz...rbR6L75887 user1@gmail.com
command="ssh git@backend-server $SSH_ORIGINAL_COMMAND" ssh-rsa AAAAB3NzaC1...hVE/141 user2@hotmail.com

On client machine

Create or edit your .ssh/config file by adding the following entry:
host frontend-server-name.com
hostname frontend-server-name.com
user git
identityfile /home/user1/.ssh/id_rsa
ForwardAgent yes

The important element here is ForwardAgent which allows the intermediate server to use our key when login via ssh to the backend server. You may need to add the key explicity to the SSH agent via:

ssh-add /home/user1/.ssh/id_rsa

La famosa tasa de abandono escolar (datos crudos)

Una de las justificaciones para la aprobación de la famosa reforma educativa (LOMCE) es que España tiene una tasa de abandono escolar insoportable (se maneja la famosa cifra del 30%).
Dejo aquí los datos raw (crudos) sobre los cuales “cocinan”, al gusto del consumidor, los diferentes actores políticos.

Que cada uno saque sus propias conclusiones.

early_leavers_chart

 

Hoja Excel con todos los países (listo para cocinar ;-)): early_leavers_1992_2012

html5_css_javascript

Interfaces de usuario HTML/CSS/Javascript ¿nuevo estándar?

En los últimos años la interfaces de usuario para aplicaciones web “ricas”, implementadas con el estándar HTML/CSS/Javascript, han experimentado grandes avances, en detrimento de las tecnologías basadas en plugins, como Flash o los Applets Java. Comenzando, por ejemplo, por el componente Canvas de HTML5, que permite el dibujado 2D a bajo nivel con Javascript, posibilitando la creación de casi cualquier componente de interfaz de usuario sobre el navegador web sin necesidad de plugins. Continuando por la proliferación de potentes frameworks Javascript para animaciones (JQueryMootoolsScript.aculo.us, etc.), widgets avanzados (JQuery UIJQuery Mobile) o, incluso, lógica y data-binding (d3.jsbackbone.jsAngularJS, etc.), a los cuales se unen nuevos lenguajes de todavía más alto nivel como CoffeescriptDart o LESS (para CSS). Todo ello conforma uno de los ecosistemas de tecnologías más activo actualmente, clave para los diseñadores de interfaces de usuario de las aplicaciones web más impactantes. Pero ¿hay vida más allá de la propia web para HTML/CSS/Javascript? La respuesta es sí.

El primer sector fuera de la Web, es el sector de las interfaces de usuario para dispositivos móviles. El hecho de que los Smartphone, que presentan grandes capacidades a nivel nativo (como localización GPS, cámara de fotos/vídeo, acelerómetro, etc.), se comercialicen sobre tres plataformas muy distintas (AndroidiOSBlackBerry OS y Windows Phone), unido a la necesidad de publicar aplicaciones en las “app store” rápidamente, ha llevado consigo que se busquen soluciones “programa una vez, ejecuta en cualquier parte” (a costa de sacrificar rendimiento). Aquí también se ha abierto camino HTML/CSS/Javascript (p. ej: JQuery Mobile/PhoneGap). ¿Cómo funcionan en general? Básicamente la idea de esta solución consiste en que la API de la plataforma (móvil en este caso) proporcione:

  1. Un motor de renderizado HTML/CSS/Javascript (es decir, un navegador incrustable), que se suele conocer normalmente desde la API nativa como “WebView” (frecuentemente está implementado con WebKit).
  2. Una función para “inyectar” objetos nativos y hacerlos visibles como objetos Javascript bajo el nombre de una variable. De esta forma las llamadas desde Javascript al objeto inyectado en Javascript serán atendidas realmente por el objeto nativo.
  3. Una función para ejecutar código Javascript arbitrario (en forma de cadena de texto, al estilo de la propia eval() de Javascript). De esta forma se pueden llamar a funciones Javascript desde el código nativo.

Un ejemplo paradigmático que explota esta vía es PhoneGap, que amplía la API de Javascript para dar una capa de abstracción multiplataforma sobre las capacidades de los smartphone.

Finalmente, en el campo de las interfaces de usuario para aplicaciones de escritorio, siguen surgiendo y mejorándose multitud de toolkits gráficos para los diferentes lenguajes de programación, muchos de ellos multi-plataforma, o para al menos Windows/Linux/OSX (como Swing, JavaFX, GTK+, Qt, WxWidgets, etc.). Aunque existen aplicaciones que implementan la interfaz de usuario basada en HTML/CSS/Javascript, su uso es menor. En todo caso es posible hacerlo, y en esta entrada se pondran ejemplos de ello (en concreto en Java con el WebView de JavaFX y en C++ con el WebView de Qt).

Ventajas y desventajas

Plantearse la implementación de la interfaz de usuario de un proyecto software empleando HTML/CSS/Javascript, presenta las siguientes ventajas:

  • Evita conocer un toolkit gráfico propio del lenguaje de programación sobre el que se vaya a desarrollar el proyecto. La curva de aprendizaje de los toolkits gráficos suele ser elevada, sobre todo si se quiere sacar el máximo provecho.
  • Facilita la separación de interfaz de usuario de capas inferiores (lógica y acceso a datos), pudiendo asignar las diferentes partes a programadores especializados en cada parte.
  • Facilita la reutilización de la interfaz de usuario, sobre todo en distintas plataformas móviles.
  • Facilita encontrar programadores especialistas. La estandarización de estos lenguajes y la proliferación de la Web como plataforma para aplicaciones facilita que exista un gran número de profesionales estén familiarizados con estas tecnologías.

Como desventajas, se podrían destacar:

  • Rendimiento. El hecho de que la interfaz de usuario esté implementada con lenguajes de tan alto nivel e interpretados hace que el rendimiento sea inferior a toolkits más cercanos al lenguaje de programación nativo.
  • Aspecto “alien”. Las aplicaciones hechas con estas tecnologías difícilmente se adaptan a las guías de estilo de los sistemas operativos.
  • Dificultad o imposibilidad de implementar interfaces avanzadas (por ejemplo 3D). Aunque cada vez esto queda restringido a menos casos. Basta con citar que se implementan motores videojuegos directamente con Canvas (p. ej: PlayN de Google).
  • Demasiados lenguajes de programación para back-end y front-end (que ya de por sí son tres: HTML/CSS/Javascript).

Un ejemplo práctico

Vamos a implementar un ejemplo sencillo tratando de reutilizar una misma interfaz (front-end) HTML/CSS/Javascript con diferentes implementaciones del back-end (una mini-lógica). La idea es que se podría cambiar el back-end sin tocar ninguna línea del front-end.

Por otra parte, y aplicando el principio de diseño SOLID de la inversión de dependencias, separaremos front-end de back-end mediante una abstracción, que actuará a modo de contrato y que deberá ser llamada por el front-end e implementada por los diferentes back-ends.

//interfaz back-end
/* suma dos numeros (dos primeros parámetros). El resultado se
pasará como parámetro a la función de callback proporcionada
desde el front-end */
sum = function (int, int, callback )
/* obtiene un listado de nombres de frutas que, una vez "calculado",
será pasado como parámetro a la función de callback proporcionada
desde el front-end */
fruits = function ( callback )

La arquitectura se resume en la siguiente imagen:
webview-architecture

Antes de continuar una pregunta: ¿Por qué no devolver el resultado como valor de retorno de las funciones? Se puede hacer sin problema, pero este diseño facilita que se puedan hacer llamadas asíncronas. Imaginemos que fruits() tarda cierto tiempo en calcularse, por lo que el back-end lanza un hilo de cálculo en segundo plano para no bloquear la interacción con el usuario, o que se conecta asíncronamente a un servidor remoto. Este diseño permite que cuando termine el proceso en segundo plano, éste llame de vuelta al front-end y le pase los resultados.

El front-end

El front-end consiste en un fichero HTML, que emplea una hoja de estilos CSS pequeña, junto con un Javascript sencillo a modo de controlador (controller.js).
index.html
<html>
	<head>
		<link rel="stylesheet" href="style.css" />
		<script type="text/javascript" src="jquery-1.9.1.min.js"></script>

		<!--
		BACKEND CONFIGURATION
		You can also use the same backend file (e.g.: backend.js)
		and replace its real contents in order to avoid modifying
		index.html to change the backend implementation
		-->
		<script type="text/javascript" src="backend-local-java.js"></script>
		<!--<script type="text/javascript" src="backend-local-cpp.js"></script>-->
		<!--<script type="text/javascript" src="backend-remote-server.js"></script>-->
		<!--<script type="text/javascript" src="backend-local-js.js"></script>-->

		<script type="text/javascript" src="controller.js"></script>
	<head>

	<body>

		<div id="fruits">
			<h1>Fruits</h1>
			<ul id="fruitslist"></ul>
		</div>

		<div id="calculator">
			<h1>Calculator</h1>			

			<label>A:</label>
			<input id="aValue" type="text"/>

			<label>B:</label>
			<input id="bValue" type="text"/>

			<input id="calculatebutton" type="button" value="calc"/>

			<div id="result">Result: <span id="resultvalue"></span>
			</div>

		</div>
	</body>
</html>

Las líneas que aparecen señaladas, muestran los diferentes back-end que se han implementado. La idea es que en un software real sólo habría una de esas cuatro líneas.

El controlador en Javascript (controller.js) se encarga de atender a los eventos de usuario, llamar al back-end a través de la interfaz abstracta (líneas resaltadas) y actualizar la interfaz de usuario con los resultados (modificando el HTML con JQuery).

//FRUITS CONTROLLER
function loaded(){
	// call the back-end and provide
	// a callback function to draw the results
	backend.fruits(function(data){
		for(var fruit in data){
			$("#fruitslist").append("<li>"+data[fruit]+"</li>");
		}
	});

};
$(document).ready(function(){
		// It seems that the backend object is not yet available on the
		// body onload() or $(document).ready(). It seems that timers
		// are started after the backend is injected.
		setTimeout(loaded, 10);
});

//CALCULATOR CONTROLLER
//connect the 'calc' button listener
$(document).ready(function(){

		$("#calculatebutton").click(function(){
			var a = parseInt($("#aValue").val());
			var b = parseInt($("#bValue").val());

			// call the back-end and provide a callback
			// function to draw results
			backend.sum(a, b, function(result) {
				$('#resultvalue').html(result);
			});

		});
	}
);

Los diferentes back-end

Ahora se muestran los diferentes back-end: Java, C++, Servidor HTTP remoto (ej. PHP) y Javascript
backend Java con WebView JavaFX
Comenzamos por el fichero javascript que se debe incluir en el HTML para conectar el backend: backend-local-java.js
// nothing to do. The "backend" variable will be
// injected from the backend itself

Como se puede observar, no es necesario hacer nada, ya que la instanciación de la variable backend se hará en Java y desde allí se inyectará para que esté disponible en el código Javascript.

Veamos ahora la implementación en Java del back-end.

package es.uvigo.ei.sing.webviewdemo.backend;

import javafx.application.Platform;
import javafx.scene.web.WebEngine;
import es.uvigo.ei.sing.javafx.webview.JavascriptBridge;

public class BackendImpl extends JavascriptBridge {

	public BackendImpl(final WebEngine engine, String varname) {
		super(engine, varname);
	}

	public void fruits(final String callbackfunction){
		new Thread(){
			public void run() {
				try {
					Thread.sleep(1000);
					Platform.runLater(new Runnable(){
						@Override
						public void run() {
							call(callbackfunction,
									new String[]{
									"apple",
									"orange",
									"banana"});
						}
					});
				} catch (InterruptedException e) {	}
			}
		}.start();
	}

	public void sum(int a, int b,
			final String callbackfunction){
		call(callbackfunction, a+b);
	}
}

La clase hereda de una clase de utilidad que hemos creado para facilitar dos cuestiones comunes:

  • Reconectar el objeto de nuevo al contexto Javascript, ya que las variables se borran cuando se cambia de URL.
  • Implementar un método call() para realizar los callback de vuelta hacia el front-end.
package es.uvigo.ei.sing.javafx.webview;

import java.util.LinkedList;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker.State;
import javafx.scene.web.WebEngine;
import netscape.javascript.JSObject;

/**
 * A bridge intended to be a superclass of Java objects
 * connected to the Javascript engine It is specifically
 * designed to implement Java utility objects as a set of
 * methods receiving a javascript callback function to be
 * called-back with results. This is useful to make
 * asynchronous designs from the Web UI tier to the bussiness
 * logic tier.
 *
 * @author lipido
 */
public class JavascriptBridge {

	protected WebEngine webEngine;
	private String varname;
	private InternalChangeListener changeListener;

	protected JavascriptBridge(WebEngine engine,
			final String varname){

		this.webEngine = engine;
		this.varname = varname;

		//Listen to state changes and reconnect to web engine
		this.changeListener = new InternalChangeListener();
		webEngine.getLoadWorker().stateProperty().
			addListener(changeListener);
	}

	private class InternalChangeListener implements
					ChangeListener<State>{

		public void changed(ObservableValue<? extends State> ov,
				State oldState, State newState) {

			if(newState == State.SUCCEEDED){
				//reconnect the backend
				connectToWebEngine();
			}
		}
		private void connectToWebEngine() {
			JSObject window = (JSObject)
				webEngine.executeScript("window");

			window.setMember(varname, JavascriptBridge.this);
		}
	};

	protected void call(String callback, Object argument) {
		webEngine.executeScript("___toEval = "+callback);
		JSObject res = null;
		JSObject window = (JSObject)
				webEngine.executeScript("window");

		if (argument instanceof String){
			//it can parse as a json object
			try{
				res = (JSObject) webEngine.executeScript(
						"eval("+argument.toString()+")"
				);
				window.call("___toEval", res);
			}catch(Exception e){
				// it is not parseable to a json object,
				// so let the API to create the javascript
				// object
				window.call("___toEval", argument);
			}
		}else{
			// it is not a json object, so let the
			// API to create the javascript object
			window.call("___toEval", argument);
		}
	}
}

Finalmente, una clase con el método de entrada main donde se crea el WebView de JavaFX, se le conecta una instancia de BackendImpl y se carga el index.html

package es.uvigo.ei.sing.webviewdemo;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Region;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import es.uvigo.ei.sing.webviewdemo.backend.BackendImpl;

public class WebViewDemoJavaBackend extends Application {

	@Override
	public void start(Stage primaryStage) {

		// create the JavaFX webview
		final WebView webView = new WebView();
		primaryStage.setScene(new Scene(new Region(){
			{
				getChildren().add(webView);
			}

		}, 340, 380));
		primaryStage.setTitle("WebView with Java backend");

		// connect the backend to the webview
		new BackendImpl(webView.getEngine(), "backend");

		// load index.html
		webView.getEngine().load(
				getClass().getResource("/index.html").
				toExternalForm());

		primaryStage.show();
	}

	public static void main(String[] args) {
		launch(args);
	}
}

El resultado final es el siguiente:
webview-java

Backend C++ con Webview Qt

Al igual que en el caso de Java, el fichero de backend javascript (backend-local-cpp.js) no contiene nada, puesto el objeto backend se inyectará desde el código C++ al arrancar el WebView.

// nothing to do. The "backend" variable will be
// injected from the backend itself

Continuamos por la implementación del Backend, con dos ficheros backend.h y backend.cpp, que constituyen la implementación de las funciones sum y fruits en C++, haciendo un callback hacia Javascript.
backend.h

#ifndef BACKEND_H
#define BACKEND_H
#include <qobject.h>
#include <QWebFrame>
#include <QString>

class Backend : public QObject
{
    Q_OBJECT

private:
    QString toFruits, varname;

    QWebFrame * webview; //the engine
    void call(QString callback, QString data);

public:
    Backend(QWebFrame * webview, QString varname);

public slots:
    void sum(int a, int b, QString callback);
    void fruits(QString callback);

private slots:
    void connectToWebEngine();
    void doFruits();
};

#endif // BACKEND_H

backend.cpp

#include "backend.h"
#include <QThread>

Backend::Backend(QWebFrame * webframe, QString varname)
{
    this->varname = varname;
    this->webview = webframe;
    connect(webframe, SIGNAL(javaScriptWindowObjectCleared()),
            SLOT(connectToWebEngine()));
}
void Backend::connectToWebEngine(){
    this->webview->addToJavaScriptWindowObject(this->varname, this);
}
void Backend::sum(int a, int b, QString callback){
    this->call(callback, QString::number(a+b));
}

void Backend::fruits(QString callback2){
    //we will run this inside a background thread
    QThread * thread = new QThread();
    this->toFruits = callback2;
    connect(thread, SIGNAL(started()), this, SLOT(doFruits()));
    thread->start();
}

void Backend::doFruits(){
    QThread::sleep(2);
    this->call(this->toFruits,
                   QString("['orange','apple','banana']"));
}

void Backend::call(QString call, QString data){
    this->webview->evaluateJavaScript("__toEval = "+call);
    QString s("__toEval("+data+")");
    this->webview->evaluateJavaScript(s);
}

Al igual que en el caso de Java, es necesario reconectar el objeto C++ al WebView cada vez que se borran las variables Javascript. En este caso, no hemos creado una superclase con este comportamiento.
Finalmente, el fichero con el punto de entrada main donde se crea una ventana Html5ApplicationViewer, creada automáticamente con el asistente de Qt Creator cuando se crea un proyecto HTML 5.
main.cpp

#include <QApplication>
#include <QWebFrame>
#include "html5applicationviewer.h"
#include "backend.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    Html5ApplicationViewer window;
    window.setOrientation(
                Html5ApplicationViewer::ScreenOrientationAuto);

    window.loadFile(QLatin1String("html/index.html"));
    window.resize(340, 380);
    window.setWindowTitle("Qt WebView with C++ Backend");
    window.showExpanded();

    new Backend(window.webFrame, "backend");
    return app.exec();
}

El resultado final es el siguiente:
webview-cpp

Backend remoto HTTP (servidor en PHP)

Este ejemplo refleja un diseño más clásico. El WebView que es, en realidad, el navegador del usuario que se conecta con un servidor mediante AJAX para invocar las funciones sum y fruits. Aunque no tiene nada especial, se incluye aquí para demostrar que es un back-end a mayores del mismo front-end que permanece intacto.
En primer lugar, el fichero Javascript backend-remote-server.js que, esta vez sí, tiene contenido. Su misión es servir de intermediario para la comunicación HTTP con el back-end que se encuentra en un servidor remoto. Dicha comunicación se implementa con JQuery empleando la técnica AJAX.
backend-remote-server.js

// server implementation of backend
backend = {

		sum: function(a, b, callback){
			jQuery.ajax({
				url: "http://localhost/sum.php?a="+a+"&b="+b,
				success: function(data){ callback(data); }
			});
		},
		fruits: function(callback){
			jQuery.ajax({
				url: "http://localhost/fruits.php",
				success: function(data){ callback(eval(data)); }
			});
		}
	};

El servidor en PHP implementa cada una de las funciones del back-end en un fichero PHP sencillo. Habitualmente lo que se encuentra en el servidor es un framework MVC con facilidades para servir peticiones al estilo API REST. Pero por sencillez se deja así.
sum.php

<?
echo $_GET["a"] + $_GET["b"];
?>

fruits.php

<?
echo "['banana', 'orange']";
?>
Back-end en Javascript

En este último ejemplo, el WebView vuelve a ser el navegador Implementar el Back-end en Javascript puede ser interesante, sobre todo en dos casos:

  1. Implementación de un prototipo del backend (mock). De esta forma, el diseñador de la interfaz puede simular las respuestas del back-end sin tenerlo disponible todavía.
  2. Conseguir que la aplicación sea totalmente portable, sin emplear ningún lenguaje de programación nativo. Sin embargo, si se desea acceder a funciones de más bajo nivel, sería necesario algo estilo PhoneGap (para funciones de smartphones) o conectar al lenguaje nativo como se ha visto en los ejemplos anteriores.

A continuación, se incluye la implementación del back-end en Javascript: fichero backend-local-js.js

// local javascript implementation of backend
backend = {

		sum: function(a, b, callback){
			return callback(a + b);
		},
		fruits: function(callback){
			return callback(['orange', 'strawberry']);
		}

	};

El resultado final es el siguiente:
webview-chrome

Resumen

El conjunto de estándares más empleados en el desarrollo de las interfaces de usuario Web, formadas principalmente por HTML/CSS/Javascript está experimentando en los últimos años grandes avances. Tanto es así que se han exportado al desarrollo de aplicaciones en otros contextos, concretamente móviles y escritorio. Este post trata de demostrar que es posible, y relativamente sencillo, crear la interfaz de usuario de una aplicación de escritorio mediante estas tecnologías. Además, separando la lógica de negocio (back-end) de la interfaz de usuario (front-end) se pueden incluir verdaderos especialistas en la Web en proyectos de aplicaciones de escritorio.

Código fuente

Finalmente adjunto el código fuente.

Teorema de los 4 colores

Hoy me he encontrado en la Wikipedia con el teorema de los 4 colores. El teorema está enunciado de la siguiente manera:

Dado cualquier mapa geográfico con regiones continuas, éste puede ser coloreado con cuatro colores diferentes, de forma que no queden regiones adyacentes (es decir, regiones que compartan no sólo un punto, sino todo un segmento de borde en común) con el mismo color.

Ejemplo de un mapa

mapamundi con 4 colores
Es decir, dos países con frontera (que tienen un segmento común), no comparten color, o dicho de otro modo, si viajamos por los países, cruzar una frontera nos obliga a cambiar de color.

Según se puede leer en la wikipedia, la demostración de este teorema es polémica, ya que sólo se ha podido llevar a cabo con métodos computacionales “poco elegantes”. No con lápiz y papel.

Sea esto cierto o no y, ya que NO soy matemático deduje (con muy buen criterio :D) que debería dedicarle un tiempo de sábado a esto mientras iba al supermercado a comprar. Tras comportarme como un zombi en el supermercado pensando en el problema, esto es lo que traigo…

Al parecer es suficiente con 4 colores. Si eso no fuese así, debería poder existir un mapa donde 5 países se conociesen entre sí, haciendo falta 5 colores.

Sin embargo, yo creo que es imposible y enuncio ( :D ) lo siguiente:

Si 4 países comparten frontera todos entre sí, entonces uno de ellos está totalmente rodeado.

Supongamos que lo que digo es cierto. Si es así, el quinto país que apareciese no podría formar frontera con el país rodeado, por lo que no se cumpliría nunca que 5 países se conocen entre sí. O, dicho de otro modo, el quinto país podría reutilizar el color del país rodeado.

Para demostrar que, cuando añadamos 4 países a un mapamundi, siempre quedará uno rodeado, vamos a ir construyéndolo. Trato de que que todas las afirmaciones se cumplan para cualquier mapamundi que construyamos con 4 países conocidos entre sí.

En cada paso, se deberá cumplir lo siguiente:

El nuevo país debe tener frontera con todos los que ya están y ninguno debe quedar rodeado. Es decir, todos deben preservar algo de “costa”.

  1. Paso 1. Añadimos el primer país. Fácil. Paso 1
  2. Paso 2. Ahora debemos añadir el segundo país. Para tener frontera con todos los existentes (1 por ahora), siempre debemos crear dos puntos que delimiten la nueva frontera. Después, crearemos el país con su costa y su frontera. Quedando así.Paso 2
  3. Paso 3. En este punto, los países existentes tienen todos a) una línea de costa y b) frontera (o fronteras) con los demás. Entonces, para dar cabida a un nuevo país, cada país existente siempre deberá ceder parte de su costa para la nueva frontera con el nuevo país, es decir, tendremos que poner un punto en algún lugar de la costa. A un lado del punto habrá la nueva frontera y, al otro, seguirá habiendo costa. Busquemos esos dos puntos en el ejemplo: Paso 3
    Y creamos el país:

    Paso 3b

  4. Paso 4.. Ahora debemos hacer lo mismo que antes. Es decir, buscar un punto en la costa de cada país (nota: no hacerlo en la costa haría que no añadiesemos un nuevo país al final, sino varios de golpe y esto no sería una “historia de la formación de países” válida. Todo mapamundi tiene una historia en la que en cada paso se añade sólo uno). Necesitamos, por lo tanto, 3 puntos:
    Paso 4.
    Problema: No podemos añadir un único país sin reunciar a uno de los 3 puntos. El país que tenga el punto al que vamos a renunciar a) o bien quedará rodeado o b) no conocerá al nuevo país. La siguiente figura muestra la renuncia al punto del país verde y la aparición de un nuevo país que lo termina rodeando.
    Paso 4b
    La otra opción sería trazar el azul por el otro lado, pero no conocería al país verde, no cumpliéndose que tenemos 4 países que se conocen entre sí.

Mi teoría es que esta es la única forma de crear un mapamundi de 4 países que se conocen entre sí, es decir, uno siempre quedará rodeado, por lo que el quinto nunca puede conocer a todos, es decir, es imposible un mapamundi de cinco países todos con frontera entre sí.

Esto debe ser todo menos una demostración, pero en fin… :D

Hacer streaming de Spotify en Ubuntu 11.04, con icecast2 + darkice y pulseaudio

Esto vale para escuchar Spotify en dispositivos que pueden reproducir streams, como smartphones, tablets, o TVs y para los que no hay una versión de Spotify (o necesitan cuentas premium). En mi caso vale para poner a sonar Spotify de fondo en mi TV de casa, (que suena mejor que el portátil :-P ).

Lo que se hará en realidad, es streaming de cualquier sonido que esté reproduciendo el PC. Pasos:

Primero, se instalará un servidor de streaming.

  1. Instalar icecast2 (apt-get install).
  2. Editar el fichero /etc/icecast2/icecast.xml para escuchar en todas las IP del PC, descomentando y poniendo:

    <bind-adress>0.0.0.0</bind-adress>

  3. Editar /etc/default/icecast2 y poner:

    ENABLED=true

  4. Arrancar icecast2 con:

    sudo /etc/init.d/icecast2 restart

A continuación, instalamos un módulo para servir el audio local (pulse) como si fuese un mp3.

  1. Instalar darkice (apt-get install).
  2. Crear el fichero ~/darkice.cfg con el siguiente contenido:
  3. [general]
    duration = 0 # duration in s, 0 forever
    bufferSecs = 1 # buffer, in seconds
    reconnect = yes # reconnect if disconnected
    [input]
    device = pulse # for Pulseaudio
    sampleRate = 44100 # sample rate 11025, 22050 or 44100
    bitsPerSample = 16 # bits
    channel = 2 # 2 = stereo
    [icecast2-0]
    bitrateMode = vbr # variable bit rate (cbr for constant)
    quality = 1.0 # 1.0 is best quality
    format = mp3 # format
    bitrate = 256 # bitrate
    server = localhost # or IP
    port = 8000 # port for IceCast2 access
    password = hackme # source password to the IceCast2 server
    mountPoint = mystream.mp3 # mount point on the IceCast2 server or any name
    name = mystream

  4. Arrancar el módulo darkice con:

    darkice -c ~/darkice.cfg

Podemos comprobar que el servidor icecast2 y el módulo funcionan, dirigiendo el navegador a http://localhost:8000 y ver que hay montado un stream llamado mystream.mp3 (a través de darkice).

Ponemos a sonar Spotify. Por último, configuramos el audio en pulse audio. Arrancamos el Pulse Audio Volume Control. Nos aseguramos de que:

  • Playback (Reproducción): Spotify esté saliendo por la tarjeta de sonido habitual, p. ej: “Internal Audio Analog Stereo”.
  • Recording (Grabación): Deberíamos ver la aplicación “darkice” y le seleccionamos que capture de: “Monitor of Internal Analog Stereo”.
  • Input Devices (Dispositivos de entrada): Miramos que aparezca el que seleccionamos para darkice pidiendo que muestre todos los dispositivos y nos aseguramos que no esté en mute.
  • Si es necesario, cambiamos de canción en Spotify.

Por último, hacer apuntar el reproductor que queramos (móvil, TV, etc) a:

http://[ip_del_pc]:8000/mystream.mp3

Tutorial basado en: http://askubuntu.com/questions/28496/how-do-i-setup-an-icecast-server-for-broadcasting-audio-in-my-network.

Caminando por el fondo del río Miño

Debido a las obras en el embalse de Belesar, sobre el río Miño (provincia de Lugo), partes que habían quedado sumergidas durante sesenta años, vuelven a estar en la superficie. La verdad es que es una oportunidad única para viajar al pasado, ya que, una vez que las obras finalicen, el río volvera a ocultar durante otros tantos años. Entre otras cosas, se pueden ver antiguos bancales con viñedos (incluso continúan las cepas), un poblado con capilla incluida, además de un antiguo puente sobre el río.

El lugar en concreto es este:

Ver mapa más grande
El el mapa de Google, abajo a la izquierda se ve el puente por el que se puede cruzar el río miño. Antiguamente, había un puente más arriba en el río, a unos 500 metros.

Este es el puente por el que hoy se cruza el río.
Puente actual

Y este es el antiguo puente que ha quedado descubierto (al fondo).
Antiguo puente sobre el río Miño, que ha quedado al descubierto.
imag3681.jpg

Cepas de vid y bancales donde se cultivaba:
Cepa

imag3699.jpg

Bancales

Un poblado:
Iglesia

Se puede ver que las construcciones eran de piedra, donde las mejores piedras se empleaban para las ventanas, quedando a la vista, y las peores y pequeñas hacían el resto del cerramiento que, posiblemente, estuviese cubierto de un mortero con cal que ahora no existe.
Pueblo

El suelo está formado por arena (ahora seca) que arrastró que alcanza una altura equivalente a la mitad del primer piso de las casas del poblado.
imag3790.jpg

Pueblo

Como nota negativa, la basura que arrastra el río también queda al descubierto.
Basura

¿No le afecta la corrupción ni la crisis al PP?

Dejando de lado los resultado municipales, donde el PP no ha sacado una diferencia significativa (en torno a un 2% más de votos), además de que se suele votar menos en clave nacional, observemos los resultados autonómicos en % de votos teniendo en cuenta dos factores.

  • Gobernar (sí/no). Vemos si el PP gobernaba en 2007 en la comunidad, es decir, estaba luchando con la crisis.
  • Corrupción (sí/no). Incluyo aquí las comunidades donde la corrupción ha levantado más ruído mediático (Madrid, Valencia y Baleares).

Ahora veamos la tabla de resultados electorales, ordenada por la diferencia en % de votos.

Tabla de resultados para Elecciones Autonómicas del PP

Todo aparece apuntar que es muy distinto estar en el grupo que gobierna y se asocian casos de presunta corrupción (Madrid y Valencia), que estar en el grupo de los que tienen uno de los dos factores, que estar en el grupo que ni se conoce corrupción presuntamente asociada al PP, ni se encontraba combatiendo la crisis.

Por supuesto, esto es simplemente un dato que me he encontrado removiendo en los resultados, seguro que tiene infinidad de matices. Pero me ha llamado la atención.

Trying Google Calendar support to KOrganizer via CalDav with kcaldav in kubuntu 10.04

1. Install the required packages:

sudo apt-get install kdepim-dev kdepimlibs5-dev libqt4-dev libical-dev libcurl4-openssl-dev libglib2.0-dev cmake

2. Download the source code of kcaldav in: http://code.google.com/p/kcaldav/

3. Compile and install:

make
make install

4. Start korganizer. You will be able to add CalDav calendars.

5. To configure a caldendar from google, the most tricky thing is the URL which must be:

https://www.google.com:443/calendar/dav/[google_calendar_id]/events/

Replace the google_calendar id with the corresponding ID, which can be seen in its properties in Google calendar.
REMEMBER:
Replace the @ character with %40
Include the final slash in the URL

Grabar Spotify en Ubuntu 9.10 con PulseAudio y Audacity

Tras varios intentos, hoy he conseguido hacer funcionar la grabación de la mezcla stereo en Ubuntu 9.10. Traduzco el tutorial que me ha funcionado (fuente: https://wiki.ubuntu.com/PulseAudio).

Ojo 1. Mi Ubuntu lo tengo en Inglés, por lo que he hecho traducciones al castellano al vuelo y puede que tengas que echarle imaginación.

Ojo 2. Este método no separa y nombra las canciones automáticamente. De hecho lo único que explico es cómo hacer que Audacity grabe lo que se oye por los altavoces en un equipo con Ubuntu. Nada más.

Requisitos:

  • Ubuntu 9.04+. Probado en 9.04 por la fuente original y en 9.10 por mí.
  • Audacity, disponible en los repositorios de Ubuntu.

Método:

  1. Pon a sonar Spotify.
  2. Abre “Aplicaciones -> Sonido y Vídeo -> Control de volumen PulseAudio”. Busca en la solapa de “Dispositivos de salida” la aplicación quieres grabar, en este caso Spotify, y selecciona como su dispositivo de salida tus altavoces preferidos (eg: “auriculares USB”, si no se encuentra ya seleccionado). No cierres el control de volumen PulseAudio.
  3. Abre Audacity y selecciona “Edición -> Preferencias”. En la sección de “Grabación”, desmarca las dos casillas bajo “Reproducción a través”. Bajo “Dispositivos” selecciona “pulse” tanto para “Reproducción” como “Grabación”. Canales deja 2 (Stereo). Pulsa OK para guardar. Cierra y abre Audacity otra vez.
  4. En Audacity, haz clic en el icono de Grabación y que comience a grabar. Grabará un silencio. No te preocupes.
  5. Vuelve al control de volumen de PulseAudio y selecciona la solapa de Grabación. Allí deberá estar Audacity como programa que se encuentra grabando. Cambia su dispositivo de donde graba a “Monitor de auriculares USB”.
  6. Pon la canción de interés al principio en Spotify.
  7. Volviendo a Audacity, verás que está grabando sonido en vez de silencio. Cuando termine lo que quieres grabar, puedes seleccionar y suprimir el silencio inicial o cosas que no interesen (lo que se grabó antes de poner de nuevo la canción al principio). Finalmente puedes exportar la grabación a MP3, por ejemplo.

La brecha digital

Tras la revuelta desencadenada en la red, a raíz del intento del Gobierno de facilitarles la vida a las gestoras de los derechos de autor, evitándoles la necesidad de acudir a un juez para cerrar un sitio web que consideren que atenta contra sus derechos, han quedado patentes muchas cosas. Primero, el Gobierno sabe cómo hacer favores. Si hay que meter una norma impopular o para ayudar a unos pocos a costa de otros, se calza camuflada y de noche, a poder ser en una ley que no tenga nada que ver. ¿Cuántas veces se habrá hecho esto?. Segundo, si la cosa estalla, también sabe cómo actuar: diciendo “Yo no he sido. Fue Sinde”. Venga hombre. Tercero, el PP no sabe muy bien de qué va la historia, pero tira de piloto automático y opta por la posición contraria al Gobierno, luego se posiciona con los internautas. Cuarto, da vergüenza ajena oir a Zapatero y a Rajoy pronunciar ‘blog’ (cada uno tiene su versión), o incluso a los medios pronunciar Spotify (dicen ‘espotifi’, con acento en ‘ti’, muestra de que no lo han usado, ya que en su publicidad se escucha hasta la saciedad). Relacionado con esto último está el resto del post. Y es que ha quedado patente, una vez más, la brecha digital.

No hay más que oír a los medios clásicos de comunicación -radio y TV-, los que otrora eran la única alternativa para informar, en el mejor de los casos; crear opinión, en muchos otros; y manipular en el peor de ellos, para comprobar que ya no son lo que eran. Las tertulias y debates, donde participan expertos analistas políticos, tocan -por obligación- el tema de la guerra entre los “internáutas” -esos seres extraños-, y los gestores de los derechos de autor sólo de pasada. Muchos se limitan a admitir que no están suficientemente informados y no tienen una postura al respecto. Enric Sopena, por ejemplo decía en RNE, “¿quién son esos que se reunieron con la Ministra?”. Los tertulianos que habitualmente emplean un lenguaje de lo más asertivo, se convierten en pequeños corderitos mirando unos para otros y diciendo: “paso palabra”.

Lo único que se sabe es que esto es una revuelta social (me atrevería a decir sin precedentes en algún aspecto), armada con redes sociales, y que eso son votos, luego hay tratarlo, hay que opinar, aunque sin opinión.

Para finalizar un ejemplo. Iñaki Gabilondo en su habitual opinión en Noticias Cuatro, comenzaba reconociendo su atraso y pidiendo tiempo para formarse una opinión, pasando rápidamente al tema de los crucifijos en las escuelas.

En fin.