Agregador de canales de noticias
Preparando el vídeo promocional
Hemos estado todo el equipo de CarMetry ocupados con el rodaje del vídeo promocional de nuestro servicio.
La jornada ha sido picar código entre entre plaquetas y focos, donde unos pocos ultiman los detalles de nuestra aplicación otros hemos estado pensando y rodando los planos del vídeo.
Agradecer a Loreto Alcántara por dejarse asaltar y ofrecer su imagen, al igual que Jose Murcia por prestarnos su equipo de grabación y su tiempo.
Sin más, dejamos trabajando a los diseñadores con una buena reserva de cafés del #Starbucks y una lista en #Spotify de música indie.
Nueva entrega.
La versión de la aplicación casi definitiva está ahora en la forja.
Os recordamos cuál es: https://github.com/savetimeapp/Save-Time-App
Lo único que nos falta ahora es añadir algún texto descriptivo y mejorar la apariencia.
Saludos!
Francisco Aroca.
Final de CUSL
Bueno, pues ya ha sido anunciada la final del Concurso Universitario de Software Libre y, por supuesto, ¡ SWADroid estará presente en ella !
La final se celebrará el próximo día 30 en el salón de grados de la ETSIIT a las 12:00. Para más información, podéis consultar la siguiente dirección: http://osl.ugr.es/2014/04/09/final-cusl-2013-2014/
SWADroid se presentará a la final con un gran número de mejoras respecto a versiones anteriores, realizadas todas durante el transcurso del concurso, destacando ente ellas:
- Cambio completo de interfaz.
- Adición de una ActionBar.
- Posobilidad de consultar módulos informativos como son: Sistema de evaluación, Programa de teoría, Programa de prácticas, etc.
a parte de otras tantas que expondremos el día de la final.
¡ Nos vemos allí !
Final of the local edition of the Free Software University Contest
Next 30th April the OSLUGR will host the final of the CUSL7 in the ETSIIT.
OSGiLiath Evolutionary Framework, with the other awesome projects, will be present, of course.
More information is available in this webpage. Join us!
Nueva versión de Save Time App
Saludos cordiales,
Una nueva versión de la app ya está disponible en nuestra forja: https://github.com/savetimeapp/Save-Time-App
Espero que lo encontréis interesante.
Francisco Aroca.
Primera vista con el motor de plantillas Blade
Laravel incluye un potente motor de plantillas llamado Blade. Un fichero de Blade es similar a uno de HTML, pero puede incluir algunas órdenes especiales para mostrar datos de PHP, bucles, condicionales, etc.
El nombre de los ficheros Blade termina en .blade.php, y se colocan en el directorio app/views. Una interesante característica de Blade es la posibilidad de generar una disposición para todas las páginas de la aplicación web. Esta disposición podría incluir un menú, pie de página y demás elementos comunes.
El fichero app/views/layout.blade.php tendrá la estructura de una página web completa, pero incluirá la directiva
@yield('content')para indicar que se cree una sección en ese punto, pero sin definir su contenido.
Las demás páginas tendrán un aspecto similar a éste:
@extends('layout') @section('content') <h1>Soy una página</h1> He sido creada con Blade. @stopLa línea @extends('layout') indica qué disposición usaremos para mostrar nuestro contenido, que inyectamos entre las directivas @section('content') y @stop, insertándose en el punto de la plantilla de disposición donde ponía @yield(‘content’).
Haciendo uso de los bucles y condicionales de Blade la vista app/views/animals/read.blade.php mostrará en una tabla los datos de todos los animales (que habíamos obtenido en el controlador):
@if ($animals->isEmpty()) <p> Currently, there is no animal!</p> @else <table class="table"> <thead> <tr> <th>#</th> <th>Name</th> <th>Species</th> <th>Neutered</th> </tr> </thead> <tbody> @foreach($animals as $animal) <tr> <td>{{ $animal->id }} </td> <td>{{ $animal->name }}</td> <td>{{ $animal->species_id}}</td> <td>{{ $animal->neutered ? 'Yes' : 'No'}}</td> </tr> @endforeach </tbody> </table> @endifTareas pendientes
En el wiki del repositorio hemos creado una sección con las tareas pendientes, y que iremos actualizando a medida que vayan surgiendo.
https://gitorious.org/r-squared/pages/Tareas%20pendientes
Ahora mismo son las siguientes:
Tareas pendientes- Lista de modelos lineales
- Almacenar cada modelo lineal en una nueva variable
- Mostrar todos los modelos lineales creados
- Se puede usar ls() para listar todas las variables
- Comprobar si variable$call es distinto de NULL y empieza por “lm”
- Añadir Box-Cox para calcular las posibles transformaciones
- Se necesita cargar library(MASS)
- boxcox(variable_lm)
- Añadir la posibilidad de nuevas tranformaciones no estándares
- Por ejemplo: variable^1.45
Primera versión alfa empaquetada
Aquí ponemos una primera versión alfa del programa empaquetado para 64-bits:
http://vps47904.ovh.net/owncloud/public.php?service=files&t=b99990461adff38ddd8200a02bac74e2
Hay que realizar los siguiente pasos para instalarlo:
- apt-get install r-base
- dpkg -i r-squared_0.1-1_amd64.deb
Ejecutar R en la consola y escribir:
- install.packages(c(“Rcpp”,”RInside”))
- install.packages(“R2HTML”, dependencies=TRUE)
Third prize
Yesterday at the 2014 Seville HackForGood Escuchadme project won third prize!
New features
Good morning, I added the code to Github new features to the mobile app as geolocation and the logo of the application. It has also solved a couple of problems with user login
Twitter Bootstrap
Idealmente, las etiquetas HTML se deben usar para definir la estructura de la página, mientras que para controlar la presentación se deben usar las hojas de estilo en cascada CSS. Para ayudarme en la presentación voy a hacer uso de Bootstrap, un framework liberado por Twitter en 2011.
Bootstrap proporciona un conjunto de hojas de estilo que proveen definiciones básicas de estilo para todos los componentes de HTML. Esto proporciona uniformidad y una apariencia moderna para el formateo de los elementos de texto, tablas y formularios. La versión actual de Bootstrap está pensada desde el primer momento para funcionar bien en dispositivos móviles (smartphones y tablets).
Además de los elementos normales de HTML, Bootstrap proporciona una interfaz para elementos tales como botones con características avanzadas (grupos de botones, botones con opción de menú desplegable, listas de navegación, etiquetas horizontales y verticales, ruta de navegación, paginación, etc.), etiquetas, capacidades avanzadas de miniaturas tipográficas, formatos para mensajes de alerta y barras de progreso.
Por medio de jQuery también provee elementos adicionales de interfaz de usuario como diálogos, tooltips y carruseles.
Un ejemplo de página que hace uso de Bootstrap:
<!--DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Bootstrap 101 Template</title> <!-- Bootstrap --> <link href="css/bootstrap.min.css" rel="stylesheet"> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <!--[endif]--> </head> <body> <h1>Hello, world!</h1> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> <!-- Include all compiled plugins (below), or include individual files as needed --> <script src="js/bootstrap.min.js"></script> </body> </html>Un buen recurso para aprender Bootstrap, además de la documentación de su web, es este libro. En webs como Start Bootstrap se pueden encontrar más temas y plantillas.
Nuevas funcionalidades de EvalCourse
EvalCourse ha mejorado mucho en los últimos meses con la incorporación de Álvaro al equipo. Se han añadido diversas opciones que hacen que la herramientas sea más manejable, potente y útil para el profesor. Estas funcionalidades se pueden resumir en el siguiente listado:
- Posibilidad de consultar varios cursos en una misma consulta.
- Elección del tipo de fichero de salida: se puede elegir si los datos devueltos por el programa queremos tenerlos únicamente en un fichero xml, gephi, diagrama de barras o de sectores.
- Simplificación del fichero xml de salida.
- Añadido un nuevo tipo de fichero de salida, csv.
- Creación de un programa de escritorio para facilitar el uso al usuario. Aplicación RCP de Eclipse.
- Posibilidad de realizar la consulta directamente sobre la base de datos de moodle (hay que tenerla instalada en el equipo desde el cual se realice) o bien desde una copia de seguridad del curso. En este caso, el profesor se descarga la copia de seguridad desde la web, la adjunta al programa y éste analiza el conjunto de ficheros para devolver la consulta.
En este momento estamos trabajando en la incorporación del wiki al conjunto de actividades que pueden ser consultadas desde EvalCourse. Estamos estudiando la estructura de la parte de la BD de Moodle que almacena la información del Wiki.
Las novedades están disponibles en el repositorio de Assembla: https://www.assembla.com/spaces/evalcourse/
Primer controlador
El patrón MVC se compone de tres partes (modelos, vistas y controladores) que nos permiten separar conceptos. En las dos entradas anteriores he creado los modelos iniciales, y ahora le toca el turno al primer controlador.
Voy a hacer un prototipo de página que me muestre datos de todos los animales que hay en la base de datos. Para ello tendré que definir un controlador (app/controllers/AnimalsController.php) y una vista (app/views/animals/read.blade.php). Cada controlador contendrá varias acciones, agrupando así la lógica referida a ese objeto.
Para que cuando se acceda al URL http://servidor.red/animals se ejecute la acción read() del controlador AnimalsController, inserto esta línea en el fichero app/routes.php:
Route::get('/animals', 'AnimalsController@read');
El fichero AnimalsController.php tendrá inicialmente este contenido (posteriormente le añadiré más acciones):
class AnimalsController extends BaseController { public function read() { $animals = Animal::all(); return View::make('animals.read')->with('animals', $animals); } }La acción read() obtiene todos los registros de la tabla animals, y se los pasa a la vista animals/read.blade.php, de la que hablaré en una próxima entrada.
El alma de la máquina
Hoy, día 31 de marzo, por fin ha llegado ese momento tan esperado durante meses y que proporciona sentido a todo este proyecto, hoy ha nacido la parte fundamental de toda máquina…aquello que la gobierna y la hace única, su alma.
Durante meses hemos estado trabajando en el desarrollo del software que hace posible un funcionamiento eficiente de UrCocktail y por fin somos capaces de decir que existe una versión estable, eficiente y totalmente funcional. Esta realidad se hace posible a través del conocido sistema de control Arduino, el cual mediante su programación, consigue dotar a cualquier máquina electro-mecánica de funcionamiento propio. Este desarrollo, incubado durante meses, ha sufrido cambios drásticos en repetidas ocasiones. Desde el comienzo estaba proyectado para hacer funcionar a un actuador que iría colocado en el lugar donde se situaría el vaso y que accionaría un dispensador acoplado a cada botella, sin embargo, el hecho de tener que emplear bebidas carbonatadas hizo pensar que el uso de éstas utilizando un dispensador podría llegar a ser totalmente ineficaz debido a la sobrepresión que generaría el gas al rellenarlo. La solución óptima adoptada fue cambiar la manera en la que se llenaba el vaso a lo largo de su recorrido, es decir, en lugar de actuar desde la plataforma, se decidió actuar desde cada botella mediante la utilización de electro-válvulas.
Otro de los contratiempos que tuvimos que subsanar fue la gran cantidad de código que en un principio se aspiraba hacer funcionar debido a la estructura inicial para la que se había planteado el proyecto. El programa estaba siendo pensado para ser lo más versátil posible, o dicho de otra manera, se pretendía reducir la comprensión de éste al elemento más simple posible y ensamblar desde ahí cualquier información entrante. Para ello hicimos uso de la herramienta switch, la cual nos facilitaba mediante el uso de casos las distintas variantes que se podían adoptar con cada carácter leído. El remedio encontrado tras días de modificaciones en el planteamiento del proyecto fue parametrizar en la medida de lo posible el manejo de toda la información a través de un simple algoritmo matemático.
Por último, es necesario decir que la esencia de esta máquina no está completa aún, falta la interfaz gráfica que llegará a hacer de UrCocktail un concepto de ocio fácil, único e intuitivo para cualquiera que tenga el gusto de hacer uso de UrCocktail. Esta interfaz, realizada en lenguaje java, brinda al usuario el privilegio de disfrutar de un magnífico cóctel sencillamente utilizando un cómodo menú por el cual navegar hasta escoger la opción mas deseada.
Chat Multiusuario con WebRTC
Para entender un poco mejor como vamos a conseguir transmitir vídeo en el navegador mediante P2P sin necesidad de plugins gracias a WebRTC, primero necesitamos entender cómo funcionan los elementos básicos de la API que nos permitirán hacer esto, hablamos de RTCPeerConnection y RTCDataChannel. Para ello, vamos a construir un pequeño ejemplo de un chat multiusuario con el objetivo de entender como trabajar con la API WebRTC.
El chat estará compuesto por los siguientes elementos:
- Peer: Es la parte del cliente, cada usuario ejecutará un peer en su navegador. El peer lo vamos a escribir en HTML5+JavaScript+WebRTC
- Server (SignalingServer): Es la parte servidora, su misión es simple, hacer que los peer se conozcan entre sí. Sólo eso, los mensajes nunca pasarán por el servidor, estos serán intercambiados directamente entre los peers. El Server lo vamos a escribir en Python.
Nota: Si todo esto te suena a chino te recomiendo que eches un vistazo a WebRTC Comunicación en tiempo real sin plugins antes de seguir leyendo.
¡Manos a la obra!
El Server (SignalingServer)
Para que los clientes (peer) se conozca entre si es necesario hacer uso de un servidor de señalización que nos de la información necesaria de cada uno para poder establecer una comunicación directa entre ellos. Necesitamos una forma de comunicarnos con los peer que se están ejecutando en el navegador de cada usuario, algo que nos permita intercambiar la información entre el servidor y cada uno de los clientes. La forma más sencilla y directa es usar WebSocket, para ello haremos uso de la siguiente librería SimpleWebSocketServer by opiate, es software libre y nos facilita su implementación en Python.
Partiendo del módulo sólo tenemos que reescribir los meéodos handleMessage, handleConnected y handleClose para que hagan lo que nosotros búscamos, en este caso es muy sencillo, intercambiar la información de cada peer con el resto de peers.
#hanleMessage se ejecuta cuando un mensaje es recibido def handleMessage(self): #recibimos un mensaje (json) datos=str(self.data) #decodificamos el mensaje (json) try: decoded = json.loads(datos) except (ValueError, KeyError, TypeError): print "JSON format error" #Reenviamos el mensaje a resto de clientes for client in self.server.connections.itervalues(): if client != self: try: client.sendMessage(str(self.data)) except Exception as n: print n #handleConnected se ejecuta cuando un nuevo cliente se conecta def handleConnected(self): global nextid try: #enviamos al cliente su id de peer self.sendMessage(str('{"numpeer":"'+str(nextid)+'"}')) #enviamos al cliente la lista de peer actual self.sendMessage(str('{"peerlist":"'+str(peerlist)+'"}')) #agregamos el nuevo peer a la lista peerlist.append(nextid) peeridlist[self]=nextid nextid=nextid+1 except Exception as n: print n #handleClose se ejecuta cuando un cliente se desconecta def handleClose(self): #eliminamos el peer de la lista peerlist.remove(peeridlist[self]);Nota: El código completo del SignalingServer está actualizado y disponible en Launchpad -> P2PSP > Experimentos > ChatMultiusuario > Server.py
El Peer
Como ya hemos dicho, la parte del cliente se ejecuta en el navegador y toda la funcionalidad vamos a escribirla en JavaScript. A continuación sólo vamos a comentar algunas partes del código que son interesantes. Recuerda que puedes acceder al código fuente completo de este experimento en Launchpad -> WebRTCMultiPeerChat
Primero definimos el SignalingServer que será la URL donde se está ejecutando Server.py y configuration para RTCPeerConnection que será el servidor STUN que se encarga de proporcionarnos la información de red externa (IP, Puerto, etc).
var signalingChannel = new WebSocket("ws://127.0.0.1:9876/"); var configuration = {iceServers: [{ url: 'stun:stun.l.google.com:19302' }]}A continuación se muestra el resto del código necesario para el peer, está comentado para una comprensión más sencilla:
// Inicializamos las conexión // isInitiator = true o false // i= id del peer function start(isInitiator,i) { //Inicializamos RTCPeerConnection para el peer i. pcs[i] = new webkitRTCPeerConnection(configuration, {optional: [{RtpDataChannels: true}]}); // Enviar cualquier ICE candidate a los otros peer. pcs[i].onicecandidate = function (evt) { if (evt.candidate){ signalingChannel.send(JSON.stringify({ "candidate": evt.candidate , "idtransmitter":'"'+idpeer+'"', "idreceiver":'"'+i+'"'})); } }; // dejar a "negotiationneeded" generar ofertas (Offer) pcs[i].onnegotiationneeded = function () { pcs[i].createOffer(function(desc){localDescCreated(desc,pcs[i],i);}); console.log("Create and send OFFER"); } if (isInitiator) { // Crear el datachannel para ese peer channel[i] = pcs[i].createDataChannel("chat"+i); setupChat(i); } else { // Establecer el datachannel para ese peer pcs[i].ondatachannel = function (evt) { channel[i] = evt.channel; setupChat(i); }; } console.log("Saved in slot: "+i+" PeerConection: "+pcs[i]); } //Establecer localDescriction y y enviarla a los otros peer (ellos la estableceran como remoteDescription) function localDescCreated(desc,pc,i) { pc.setLocalDescription(desc, function () { console.log("localDescription is Set"); signalingChannel.send(JSON.stringify({ "sdp": pc.localDescription , "idtransmitter":'"'+idpeer+'"', "idreceiver":'"'+i+'"'})); }, logError); } //Se ejecuta cuando se recibe un mensaje desde el signalingChannel (WebSocket) signalingChannel.onmessage = function (evt) { handleMessage(evt); } //Manipular el mensaje function handleMessage(evt){ var message = JSON.parse(evt.data); //Si es el ide del peer se almacena if (message.numpeer){ idpeer=message.numpeer; console.log('Peer ID: '+idpeer); return; } //Si es la lista de peer se almacena if (message.peerlist){ console.log('Peer List '+message.peerlist); peerlist=JSON.parse(message.peerlist); for (i in peerlist){ console.log("Peer: "+peerlist[i]); } return; } //guardamos el id del que envia el mensaje y el id del que debe recibirlo var id=(message.idtransmitter).split('"').join(''); var idreceiver=(message.idreceiver).split('"').join(''); console.log("Received from: "+id+" and send to: "+idreceiver); //Si es nuevo para este peer se configura la RTCPeerConection y se añade a la lista de peer. if (!pcs[id]) { console.log('%cCreate a new PeerConection','background: #222; color: #bada55'); peerlist.push(id); console.log("PEER LIST UPDATE: "+peerlist); start(false,id); } //Si el mensaje va dirigido a mi y es SDP (informacion de la sesion) if (message.sdp && idreceiver==idpeer){ //Estableco la información de la conexión remota (remoteDescription) pcs[id].setRemoteDescription(new RTCSessionDescription(message.sdp), function () { console.log("remoteDescription is Set"); // Si recibimos una oferta enviamos una respuesta. if (pcs[id].remoteDescription.type == "offer"){ console.log("Create and send ANSWER"); pcs[id].createAnswer(function(desc){localDescCreated(desc,pcs[id],id);}); } }); } //Si el mensaje va dirigido a mi y es un ICE candidate if (message.candidate && idreceiver==idpeer){ //agrego el candidato a la lista de candidatos de ese peer. console.log("Received ice candidate: "+ message.candidate.candidate); pcs[id].addIceCandidate(new RTCIceCandidate(message.candidate)); } } //Configuracion del chat function setupChat(i) { //Cuando se abra el dataChannel con ese peer se habilita el boton enviar. channel[i].onopen = function () { btnSend.disabled=false; document.getElementById("chatcontrols").style.display="inline"; }; //Cuando se reciba un mensaje por DataChannel (WebRTC) se muestra el mensaje. channel[i].onmessage = function (evt) { document.getElementById("receive").innerHTML+="<br />"+evt.data; }; } //Enviar un mensaje por DataChannel (al resto de peer) function sendChatMessage() { document.getElementById("receive").innerHTML+="<br />"+document.getElementById("login").value+ ": "+msg.value; //Para cada peer de la lista... for (i in peerlist){ if (peerlist[i]!=idpeer){ console.log("send to "+peerlist[i]); //Se envial el mensaje con el nombre de usuario y el texto a enviar. try{ channel[peerlist[i]].send(document.getElementById("login").value+ ": "+msg.value); }catch(e){ console.log(i+" said bye!"); } } } }Nota: El código completo del Peer está actualizado y disponible en Launchpad -> P2PSP > Experimentos > ChatMultiusuario > Peer.py
Una versión de este experimento está disponible para probarlo en http://www.p2psp.org/chat. Si al introducir un nickname aparece el mensaje "Conecting..." pero no aparece el cuadro de texto con el botón "send" es posible que no haya ningún otro peer (o esté detrás de un NAT simétrico), para comprobar que funciona correctamente puedes abrir otra vez la misma URL en otra pestaña de tu navegador o incluso en otro equipo de tu red.
Si tienes cualquier duda o sugerencia, usa los comentarios :-)
Este experimento, por el momento, sólo funciona en Google Chrome (incluido Chrome for Android)
Todo listo para la Valoración en el CUSL
Pues tras estar las últimas semanas liado con cosas varias, múltiples proyectos han salido en los que estoy inmerso, participé en la II Startup Weekend Tenerife en el equipo Seiku (www.seikutales.com) con el que conseguimos 3 de los 6 premios que daban (los tres premios especiales), etc. también he dedicado huequecitos a Qdemos para afinarla y dejarla lista para la valoración por parte del jurado del Concurso Universitario de Software Libre de este año.
He añadido el .apk en Github: Descargar App Android para que quien quiera la pueda descargar y probar. Faltan pequeños detallitos, pero a grosso modo funciona todo. No la he publicado en Google Play (Tengo cuenta como desarrollador desde hace varios años) porque primero la quiero probar en beta cerrada (También podría haber optado por la publicaciones de betas de Google Play), antes de lanzarla al market y distribuirla oficialmente.
Como el cometido del concurso, bajo mi punto de vista, es crear código que sea reutilizable por terceros para sus proyectos propios, o mejorar este proyecto, pues me he centrado más en dejar maquetado un código ‘decente’ que en preocuparme en lanzarla en Google Play y ver la repercusión que pudiera tener. Aún así eso se hará en el futuro.
Supuestamente el Jurado del CUSL, empieza a partir del día 1 de Abril a valorar los proyectos, así que dejo todo cerrado para que valoren Qdemos!, aunque seguiré trabajando en él mientras el tiempo que me dejen libre me lo permita :). Como comentaba, lo único que le falta es probarla con personas ajenas a mí, y que empiecen a utilizarla para hacerle todas las perrerías que deseen y empezar a recibir bugs que hasta ahora no había detectado (sobre todo de interfaz de usuario e interacción).
Para la beta, que podéis descargar de aquí, he levantado mi servidor Node.js en una microinstancia de Amazon de su capa gratuita AWS. El tiempo de respuesta es realmente espectacular (el que da el servicio de Amazon), ahí tengo otros servidores míos de otras apps. Aconsejo a quién dude donde montar su servidor dedicado gratuito para probar sus ‘juguetitos’ que le echeis un ojo a AWS (que durante un año te da un servicio gratuito muy notable).
Así que podéis probar entre varios amigos la app (notar que si un amigo al que invitéis no tiene instalada la App y se ha logueado en ella, lógicamente no va a recibir la notificación push con la invitación, pero si que aparecerá como invitado) y hacerme llegar todos los fallitos con los que os encontréis, o al correo, o comentando en el blog, o lo que sería ya la repanocha, abriendo issues en GitHub.
Como siempre os dejo con un commit, en este caso el último COMMIT que he realizado preparando todo el README.md para poner el link de descarga de la versión Beta.
Esto es todo, y hasta la próxima.