Así puedes crear tu primera API REST con Deno

deno js background

No es necesario explicar los pasos para instalarlo. Están muy bien descritos en la web oficial. Yo para este ejemplo he usado VS Code, para el que existe este plugin oficial.

¿Cómo es Deno?

Deno es muy parecido a Node, pero lo que intenta hacer es ser más bueno construyendo un runtime más seguro y corrigiendo los errores de base que tenía Node. Deno es seguro por defecto. Esto significa que no tiene acceso a tu disco duro, ni a tu red. Lo tendrá únicamente si tú se lo das. Node, en cambio, tiene acceso a prácticamente todo nada más instalarlo.

Por ejemplo, si seguimos la guía oficial y ejecutamos el primer comando, veremos que funciona sin problema.

deno run https://deno.land/std/examples/welcome.ts

Pero si lo hacemos con el ejemplo que viene a continuación:

import { serve } from "https://deno.land/[email protected]/http/server.ts";
const s = serve({ port: 8000 });

console.log("http://localhost:8000/");
for await (const req of s) {
  req.respond({ body: "Hello World\n" });
}

Nos encontraremos con un error de permisos de acceso a la red:

Compile file://welcome.ts
error: Uncaught PermissionDenied: network access to "0.0.0.0:8000", run again with the --allow-net flag
    at unwrapResponse ($deno$/ops/dispatch_json.ts:43:11)
    at Object.sendSync ($deno$/ops/dispatch_json.ts:72:10)
    at Object.listen ($deno$/ops/net.ts:51:10)
    at listen ($deno$/net.ts:152:22)
    at serve (https://deno.land/[email protected]/http/server.ts:261:20)
    at file://welcome.ts:2:11

Esto sucede porque el primer ejemplo no necesita ningún tipo de acceso adicional para funcionar, pero el segundo necesita acceso a nuestra red. Deno, al tener seguridad por defecto, no nos permite hacerlo a no ser que le otorguemos permiso para ello.

En este ejemplo, basta con ejecutarlo con el parámetro –allow-net, y funcionará.

Lo mismo ocurre con los ficheros, en este caso necesitamos –allow-read

Crear una API con Deno

Qué mejor forma para empezar a jugar con Deno que creando nuestra primera API REST.

Con este pequeño tutorial voy a crear un array muy simple de películas y los 5 métodos para listar, buscar, crear, actualizar y eliminar elementos.

El primer paso es crear un fichero de arranque. En este caso app.ts. Lo primero será cargar Oak, un framework middleware para el servidor http de Deno. Oak está inspirado en Koa, un middleware para Node.js. Parece ser que siguen con el juego de palabras. Al final, nos ayuda a que escribir APIs sea más sencillo.

Es un ejemplo bastante sencillo que se explica prácticamente por si solo. El servidor escuchará por el puerto 4000 y cargará las rutas definidas en el fichero router.ts que veremos justo después. En el fichero ./api/controller.ts pondré la definición de las funciones para los distintos endpoints.

import { Application } from "https://deno.land/x/oak/mod.ts";
import router from "./router.ts";
import {
  getMovies,
  getMovie,
  createMovie,
  updateMovie,
  deleteMovie,
} from "./api/controller.ts";

const env = Deno.env.toObject();
const HOST = env.HOST || "127.0.0.1";
const PORT = env.PORT || 4000;

const app = new Application();

app.use(router.routes());
app.use(router.allowedMethods());

console.log(`API is listening on ${HOST}:${PORT}...`);
await app.listen(`${HOST}:${PORT}`);

Momento de definir las rutas en el fichero router.ts. Aquí importaremos también el Router de Oak y las definiciones que crearemos en el controller.ts

Instanciamos un Router y definimos las 5 rutas comentadas.

MétodoFunción
getMoviesDevuelve todas las películas
getMovieDevuelve una película a partir de un id
createMovieCrea una nueva película
updateMovieActualiza una película ya existente
deleteMovieElimina una película
import { Router } from "https://deno.land/x/oak/mod.ts";
import {
  getMovies,
  getMovie,
  createMovie,
  updateMovie,
  deleteMovie,
} from "./api/controller.ts";

const router = new Router();

router
  .get("/movies", getMovies)
  .get("/movie/:id", getMovie)
  .post("/movies", createMovie)
  .put("/movies/:id", updateMovie)
  .delete("/movies/:id", deleteMovie);

export default router;

Ahora es momento de crear el fichero controller.ts para definir los métodos de la API y el array con la base de datos de prueba.

interface Movie {
  id: string;
  title: string;
  rating: number;
}

Justo a continuación, creamos el array comentado:

/**
 * Sample array with movies
 */
let movies: Array<Movie> = [
  {
    id: "1",
    title: "TENET",
    rating: 10,
  },
  {
    id: "2",
    title: "No Time to Die",
    rating: 8,
  },
  {
    id: "3",
    title: "The Way Back",
    rating: 7,
  },
  {
    id: "4",
    title: "The Invisible Man",
    rating: 9,
  },
  {
    id: "5",
    title: "Onward",
    rating: 8,
  },
];

Y ahora los distintos métodos, empezando por el que lista todas las películas. Quedaría así de simple:

/**
 * Returns all the movies in database
 */
const getMovies = ({ response }: { response: any }) => {
  response.body = movies;
};

Vamos a por el siguiente, el encargado de devolver una película a partir de un ID que le podremos pasar por parámetro.

/**
 * Returns a movie by id
 */
const getMovie = ({
  params,
  response,
}: {
  params: { id: string };
  response: any;
}) => {
  const movie = movies.filter((movie) => movie.id == params.id)[0];
  if (movie) {
    response.status = 200;
    response.body = movie;
  } else {
    response.status = 404;
    response.body = { message: "404 Not found" };
  }
};

Si probamos a lanzar la petición con Postman, veremos que funciona.

image 12
Lanzamos la petición para un ID en concreto.

Le toca el turno al método createMovie para crear una película. El código es el siguiente:

/**
 * Creates a new movie
 */
const createMovie = async ({
  request,
  response,
}: {
  request: any;
  response: any;
}) => {
  const body = await request.body();
  const movie: Movie = body.value;
  movies.push(movie);
  response.body = { success: true, data: movie };
  response.status = 201;
};

Si lanzamos la petición de prueba, el servidor nos contestará con el mensaje programado.

image 14

Si a continuación lanzamos la petición para devolver todas las películas, veremos como aparece la nueva correctamente.

image 15

Es el turno del método updateMovie para actualizar una película. El código es:

/**
 * Updates an existing movie
 */
const updateMovie = async ({
  params,
  request,
  response,
}: {
  params: { id: string };
  request: any;
  response: any;
}) => {
  const movie = movies.filter((movie) => movie.id == params.id)[0];
  if (movie) {
    const body = await request.body();
    movie.title = body.value.title;
    movie.rating = body.value.rating;
    response.status = 200;
    response.body = {
      success: true,
      data: movies,
    };
  } else {
    response.status = 404;
    response.body = {
      success: false,
      message: "Movie not found",
    };
  }
};

Lanzamos la correspondiente petición PUT con Postman, y obtendremos la respuesta correcta.

image 17

Y para finalizar, solo nos queda el método deleteMovie que, en este caso, elimina una película a partir de un id. Lo que hago es utilizar el filter() para actualizar el array manteniendo todas las películas con id distinto al enviado.

/**
 * Deletes a movie by a given id
 */
const deleteMovie = ({
  params,
  response,
}: {
  params: { id: string };
  response: any;
}) => {
  movies = movies.filter((movie) => movie.id !== params.id);
  response.status = 200;
  response.body = { success: true, message: "Movie removed" };
};

Probamos con Postman…

image 19

Y efectivamente acaba de desaparecer la película con id = 1.

image 21

Puedes descargarte todo el código de este ejemplo en este repositorio de mi GitHub.

eCommerce y Google Analytics: Venderás mañana si sabes porqué lo haces (o no) hoy

photo 1563013544 824ae1b704d3 1

Este es el primero de una serie de artículos que escribiré en colaboración con Josep Maria Florista. Un gran compañero en ACTIUM Digital que se encarga del marketing digital en los proyectos propios y de nuestros clientes. Josep Maria y yo trabajamos codo con codo para que las partes de SEO y marketing digital corran acompañadas de la parte más técnica y de desarrollo de los proyectos web y eCommerce.

Por esta razón, hemos decidido aportar valor conjunto en forma de publicaciones en nuestros respectivos blogs personales, para aportar sobre un tema que nos ha causado más de un quebradero de cabeza en los últimos meses. La analítica en los eCommerce. Josep María se centra en este artículo en la parte de configuración y uso de Google Analytics y yo, en la parte más técnica así como los puntos a tener en cuenta para que todo funcione bien, las comprobaciones a realizar y los posibles errores que puedes encontrarte en el proceso.

El objetivo es eliminar errores y lograr que las transacciones se registren de la forma más exacta posible.

Saber porqué te compran (y porqué no)

¿Qué es más importante, conocer porqué un usuario no acaba comprando, o saber cuando sí lo hace? Ambas cosas. Llevas dos meses pensando en comprar esa chaqueta de 400€ que te encanta. La tienes en el carrito de la tienda .com. Decides finalizar el pedido porque, simplemente, has tenido un buen día. O te lo quitas de la cabeza porque crees que tu mujer te pone los cuernos. Somos impredecibles.

Para tomar decisiones necesitamos sacar conclusiones, y para esto necesitamos datos. Fiables y exactos. No solo el número de visitas, los clics y las páginas más visitadas. Con esto no hay suficiente. De hecho, quizá sean los datos más irrelevantes en una tienda eCommerce.

Explica Josep Maria Florista que Google Analytics se ha convertido en la herramienta indispensable para poder medir, controlar y monitorizar todo lo que sucede en nuestro site. Cuando analizamos una página web con Google Analytics nos centramos más en aspectos como qué páginas consultan, con qué dispositivos lo hacen, o desde qué fuentes nos visitan.

La gran mayoría de estos análisis se realizan sin tener un objetivo que deban alcanzar los usuarios, bien porque es una página informativa, un blog o una página que tenga una actividad comercial directa, como puede ser una empresa que ofrece servicios.

Pero… ¿Qué sucede con los eCommerce o tiendas online? Este tipo de proyectos sí tienen un claro objetivo en el que basaremos el funcionamiento de las mismas: las transacciones.

Google Analytics dispone de informes de comercio electrónico muy potentes donde podremos conocer, entre otras métricas:

  • El total de ingresos
  • La tasa de conversiòn
  • El total de transacciones
  • El valor medio del pedido
  • El listado de los productos más vendidos

Además de mostrar estos datos en todos los informes de Google Analytics, podremos analizarlos en base a la transacciones realizadas. Si tenemos un eCommerce, el uso de los informes de Comercio Electrónico es una herramienta indispensable para poder monitorizar toda la información de las transacciones y por consecuencia poder analizar y optimizarlo.

Saber captar datos (y que no se pierdan)

Para la obtención de esos datos, deben comunicarse el navegador del cliente, el servidor del eCommerce, y los módulos externos (TPV). Entre ellos y sin ninguna grieta. Todos repercuten directamente en las estadísticas de venta. Si uno falla, tendrás incongruencias en los datos.

Preparar un eCommerce para averiguar estos datos parece fácil hasta que lo intentas. Entonces, te das cuenta de que no se trata simplemente de añadir el código que te facilita Google Analytics. En un proceso de compra intervienen muchos factores técnicos que debes contemplar para que los datos de navegación y compra se registren correctamente. Si falla alguno de los componentes, tendrás ventas sin registrar.

50 ventas en la web, 48 en Analytics. ¿Qué ocurre?

Has ganado una venta, pero no sabrás porqué. En algún momento Google Analytics ha dejado de trazar al usuario, y puede que sea tu culpa (o no). Cabe la posibilidad de que el usuario tenga un bloqueador de anuncios o que haya desactivado manualmente las cookies de tu web. En cualquiera de estos casos, no será posible hacer un tracking correcto de la transacción si usamos Google Analytics integrado en el cliente. Es decir, con la librería Javascript. Para evitarlo podemos usar la integración server-side, pero dejaremos este tema para otro artículo.

A continuación, explicaré cómo configurar bien tu eCommerce para intentar que eso no ocurra. Considerar todos los escenarios y explicarte qué debes tener en cuenta para no perder información por el camino. Que los analistas de ventas tengan toda la información posible, y que el cliente sepa qué ocurre en cada transacción.

Paso 1: Configurar Google Analytics

Según cuenta Josep Maria, antes que nada debemos insertar el código de seguimiento en nuestro eCommerce, esta acción la podemos llevar a cabo mediantes plugins del CMS que utilice nuestra tienda online o con Google Tag Manager. Es importante asegurar que la etiqueta está insertada y se activa en todas las páginas de nuestro sitio web, podemos realizar la comprobación con la extensión para navegadores Google Tag Assistant.

Para poder visualizar los informes de Comercio Electrónico accederemos a la pestaña de Administración de Google Analytics y, en la columna Vistas entraremos en la Configuración de comercio electrónico para habilitarla.

Una vez activada nos aparecerá una nueva opción para Habilitar los informes de comercio electrónico mejorados, esta acción nos habilitará un nuevo informe en forma de funnel que nos permitirá analizar todo el proceso de compra de nuestra página: usuarios que acceden, visitas a productos, añadir productos a la cesta y realizar el checkout de compra. Para implementar el informe de comercio electrónico mejorado deberemos realizarlo a través de plugins del propio CMS o directamente con Google Tag Manager.

Magento, por ejemplo, permite configurarlo sin necesidad de instalar ningún otro módulo. Basta con seguir esta guía oficial.

Configuración de Google Analytics en Magento
Configuración de Google Analytics integrada en Magento 2

También podemos considerar el uso de plugins externos que añaden funcionalidades y análisis mucho más exhaustivos como la extensión Google Analytics Enhanced eCommerce UA GTM Tracking de Welt Pixel. Tiene una versión gratuita y una de pago. Con ambas podemos llegar a tener datos mucho más específicos que con la integración del Magento base, como por ejemplo el uso de cupones o los productos comparados.

Si usas WooCommerce, tienes el plugin gratuito WooCommerce Google Analytics que también integra las funcionalidades del Enhanced E-Commerce Analytics.

Otro tipo de informes que son de gran ayuda para identificar mejor el target de usuarios que acceden a nuestra página web son los informes demográficos y intereses. Estos informes son fáciles de activar, basta con acceder a la sección Audiencia > Datos demográficos e Intereses donde además podremos consultar toda esta información.

La gran mayoría de eCommerce utilizan una plataforma TPV externa para introducir los datos bancarios. Una vez verificada la compra, los usuarios accederán a la thank you page, página desde donde se envía el hit con toda la información de la compra hacia Google Analytics.

Si no realizamos ninguna configuración cuando accedamos al informe de Adquisición > Canales de captación, nos encontraremos que las ventas proceden de un referral que será en realidad la plataforma TPV, ya que ha sido el último punto de acceso a la página. Lo que ocurre es que de ser así perderemos la información real del acceso de los usuarios que han realizado una compra, así que deberemos omitir las páginas TPV como referrals de nuestra página web.

Esta acción la podemos llevar a cabo en la Administración de Google Analytics, en la columna de Propiedad > Configuración de la propiedad > Lista de exclusión de referencias. En este punto debemos excluir los dominios de los métodos de pago externos de nuestra página web como pueden ser sis.redsys.com o paypal.com.

Una vez hecho esto y activada la funcionalidad de eCommerce en Google Analytics, asegúrate de que las visitas entran en la web visualizando el estado en tiempo real del dashboard de GA.

Vista en tiempo real de visitas en Google Analytics
Vista en tiempo real de visitas en Google Analytics

Nos llegan las visitas a la web, bien!. Pero falta lo más importante, que lleguen los datos relacionados con el comportamiento dentro del propio eCommerce. Ahí es donde está la verdadera magia. Con los datos concretos de analítica eCommerce podremos saber qué ocurre desde que empieza el proceso de compra hasta que termina. Todo lo que nos lleva a conseguir más ventas en un futuro.

Primero, asegurate de que las opciones de eCommerce están activadas en tu cuenta de Google Analytics. Puedes encontrarlas en la sección de administración, y en «Ecommerce Settings»

Confirmación de ajustes para eCommerce en Google Analytics
Confirmación de ajustes para eCommerce en Google Analytics

Paso 2: Revisar el envío de datos a GA

Vamos a comprobar si todo lo hecho hasta el momento, funciona. En esta parte es muy importante que los equipos de marketing digital y los desarrolladores estén bien comunicados.

No hay mejor forma que probar con una compra lo más real posible. Prepara tu entorno de pruebas y todo lo necesario para completar una transacción. Es importante tener siempre un entorno de test disponible porque esta no será la única vez que necesites validar todo el proceso. En un software complejo, por muchos tests que tengas, todo puede fallar. Un eCommerce no es una web corporativa. Todo lo que ocurre durante un proceso de compra debe funcionar a la perfección. Una sola pieza que falle, y vas a perder dinero.

Prepara y documenta usuarios de prueba, una tarjeta de crédito e incluso cupones de descuento. Añade productos en el carrito con distintas cantidades y configuraciones. Puedes usar un generador online de datos falsos que incluye los que puedes llegar a necesitar. Selecciona un método de pago (y repite la prueba con todos los que tengas). Llega hasta el final. Y luego, haz también un pedido cancelado.

No es difícil preparar un plan de pruebas. Basta con un excel. Si tienes todo el plan documentado, te será fácil repetirlo una y otra vez. Además, podrás delegar la tarea. O mejor, automatizarla. Quiero hablar de esto en un futuro.

¿Cómo comprobar si llegan bien las compras?

Es la parte más fácil. Revisa Google Analytics y asegúrate de esto. Verás rápidamente si interpreta bien los datos del eCommerce.

Shopping Behavior Analysis de Google Analytics
Shopping Behavior Analysis de Google Analytics

No es así. ¿Qué hago?

Además de revisar la correcta configuración de Google Anaytics, debes revisar si realmente los datos se están enviando hacia los servidores de Google.

Para hacerlo, puedes usar el inspector de Chrome (o de tu otro navegador favorito) y analizar la petición enviada. Necesitas entrar en la pestanya Network, encargada de mostrar las peticiones de red, y filtrar por las que contienen «collect«. En el ejemplo de la captura, vemos una petición realizada haciendo clic en el botón de añadir producto en el carrito.

Pestaña Network del navegador Chrome para verificar el hit
Pestaña Network del navegador Chrome para verificar el hit

Paso 3: Confirmación del pago

Aunque parezca que no, esta es una de las partes más importantes. Es el paso final. Si Google Analytics no logra rastrear este paso, no se confirmará correctamenet tu venta.

A veces, la diferencia entre que sea fácil o difícil, es que el método de pago esté integrado en la web o sea un enlace externo. Podremos como ejemplo la pasarela estándar de Redsys. Esta integración puede realizarse de distintas formas. Si optamos por la más básica, el cliente saldrá de nuestra web para introducir los datos de su Visa y volverá al eCommerce cuando se confirme o cancele el pago. Aquí está el problema, que el usuario sale y tiene que volver para que Google Analytics lo sepa.

Cuando el usuario ha pagado, aparece esta pantalla. Si pulsamos continuar, volveremos al eCommerce y la venta se registrará. Si cerramos la ventana, no. ¡Ups!

Pantalla de confirmación de pago de Redsys
Pantalla de confirmación de pago de Redsys

Los métodos de pago (Redsys, Paypal, etc) tienen una URL de callback para avisarnos del resultado del pago y poder confirmar realmente la venta. Nos envían de forma cifrada los datos y el eCommerce puede validar el proceso. Esto suele ir en paralelo al tracking de Google Analytics, que se realiza en el cliente. Este segundo es el que puede perderse en el ejemplo anterior.

Hay dos formas de controlar esta parte. Garantizar el envío del tracking desde el servidor y no solo desde el cliente (porque lo hace via JavaScript) o bien configurar el TPV para que no pase por esta pantalla y evitar el maldito continuar. El tracking server-side merece un artículo dedicado, así que lo dejaré para más adelante.

Al grano. Debemos asegurar que Redsys no muestra esta pantalla y, en su lugar, redirige al cliente hacia nuestro eCommerce. Si evitamos este paso, aseguraremos que el cliente carga de vuelta nuestra web y podremos registrar la venta.

Desgraciadamente, Redsys implementa de forma distinta su TPV para cada banco, así que la opción de redirección no está siempre disponible. En muchos casos tendremos que avisar a Redsys para que realice la gestión. Estarán acostumbrados y encantados.

Un último detalle, si tu web utiliza Cloudflare, revisa también la configuración para que el callback de Redsys llegue sin problemas.

Paso 4: Probar, probar y probar

Modificaciones, plugins nuevos, cambios de última hora… Cuando modificamos nuestro eCommerce, todo puede fallar. Comprueba todo por duplicado, y prepara un plan de pruebas.

¿Qué puedo testear?

  • Compra de un producto
  • Compra de varios productos
  • Compra con un usuario registrado y otro anónimo
  • Prueba todos los métodos de pago
  • Compra de un producto con múltiples cantidades
  • Múltiples productos, múltiples cantidades
  • Añade productos al carrito desde las distintas páginas posibles (detalle, categoría, etc)
  • Compra con un total exacto (10.00€)
  • Compra más de 1.000€ (prueba los separadores de miles)
  • Compra productos con carácteres especiales en los nombres
  • Compra con descuentos
  • Compra utilizando la misma dirección como entrega y facturación (y al revés)

Contempla posibles errores

El usuario no siempre sigue el paso lógico de compra que testeas tu, así que comprueba también todos los posibles caminos y errores, como por ejemplo.

  • Refresca la pantalla de pago
  • Pulsa atrás en el navegador dentro de la pantalla de pago
  • Deja tu sesión inoperativa por 1 hora y luego continua con la compra

Queda claro que no es un tema nada fácil, verdad?

Ya puedes sincronizar extensiones y más en Visual Studio Code de forma oficial

vscode settings sync

Somos muchos los que trabajamos con distintos entornos y ordenadores. Ya sea en el trabajo, en casa, con un portátil y con PC de sobremesa. Podemos llegar a tener distintas instalaciones de Visual Studio Code y eso puede ser un problema si trabajamos con los mismos proyectos, ya que tendremos que replicar las configuraciones y extensiones que utilicemos en cada una.

Hasta ahora podíamos utilizar esta famosa extensión llamada Settings Sync que consigue hacer esto mismo. Sincronizar configuraciones, snippets, temas, iconos, workspaces, y más. Para lograrlo utiliza el Gist de tu cuenta de Github.

image 6

Como es lógico, esto habrá llamado la atención de Microsoft y han decidido añadir de serie esta funcionalidad. De forma oficial, dentro de VS Code.

Al tratarse de una opción muy nueva, todavía no está disponible en las versiones estables de Visual Code, pero ya la están testeando en la versión para Insiders. En este mismo enlace podréis ver la documentación oficial. Al parecer le faltan opciones todavía para poder substituir el plugin anteriormente mencionado, pero si se trata de una opción oficial y viendo lo bien que trata Microsoft a VS Code, estoy convencido de que acabará siendo mejor opción.

image 7

Como activar la sincronización

En primer lugar debes descargar la versión para Insiders de Visual Studio Code

Como explican en la página oficial, basta con activar la sincronización haciendo click en el icono del engranaje y seleccionando «Turn on Preferences Sync«

En este caso, la sincronización se realizará a través de vuestra cuenta de Microsoft o la de GitHub. Tras hacer login y enlazar la cuenta, VS Code empezará a sincronizar vuestra configuración en la nube.

Los atajos de teclado, como es lógico, se sincronizarán por plataforma (Mac, PC,…) aunque podemos desactivar esta limitación. Las extensiones se sincronizarán también por defecto, pudiendo desactivar extensiones concretas que no queramos sincronizar.

Muy interesante también la sincronización de la interfaz. Se incluyen, entre otros, el panel de entradas, los layouts y los comandos recientes.

Por el momento, tras unas horas de uso, puedo decir que no he notado mayor diferencia a utilizar la extensión Settings Sync, pero la configuración inicial ha sido mucho más sencilla y rápida. Como decía, habrá que esperar un poco porque al tratarse de una opción oficial de Microsoft, lo más probable es que termine siendo el método preferido.

Generar ficheros Word con Django y python-docx

Os explicaré como poder generar ficheros Word en formato .docx de forma dinámica con Django. En mi caso lo he utilizado para desarrollar una funcionalidad para StoryDevil, que permite a los usuarios exportar en .docx una serie de documentos necesarios para enviar cartas de presentación y propuestas editoriales en base a unos datos generados en la aplicación.

De esta forma, el usuario tiene ficheros editables que puede personalizar antes de generar un PDF o hacer el envío definitivo. Y esto, con Django y el paquete python-docx, es sorprendentemente sencillo.

Además, os compartiré el código que utilicé para que el documento se descargue directamente en el navegador del usuario, en lugar de almacenarse en el servidor.

image 5
Interfaz de StoryDevil con la funcionalidad que genera los .docx

Con la librería python-docx (Github y PyPI) puedes crear documentos Word y, como decía, de forma bastante sencilla. Tiene soporte para párrafos, estilos, tablas, personalizar cabecera y pié, objetos de forma, secciones, y más. Es compatible con Python 2.6, 2.7, 3.3 y 3.4. La única dependencia adicional es la librería lxml.

image 4

A modo de ejemplo, estas tres líneas son suficientes para crear un documento, añadirle un título, y un párrafo a continuación.

document = Document()
document.add_heading('Título del documento', 0)
p = document.add_paragraph('Esto es un documento Word generado con Python.')

Instalación

Como la mayoría de paquetes Python, la instalación se puede hacer con la utilidad pip o también con easy_install. Si preferimos el método manual, podemos descargar la última versión, descomprimirla, y instalar con python setup.py install.

# Instalar con pip
pip install python-docx

# Instalar con easy_install
easy_install python-docx

#Instalación manual
tar xvzf python-docx-{version}.tar.gz
cd python-docx-{version}
python setup.py install

También puedes añadir el paquete en el fichero requirements.txt de Django especificando la última versión compatible.

Django==3.0.3
...
python-docx==0.8.10

Crear el formulario para la descarga

Para este ejemplo vamos a crear un formulario sencillo con un botón que, tras pulsarlo, llamará a una vista que generará el documento y lo descargará directamente en el navegador.

Este es el formulario con un action que llamará a la URL que apunta a la vista.

<form action="{% url 'publication:export' %}" method="post" role="form">
    {% csrf_token %}
    <button type="submit" class="btn btn-success">
        <i class="fa fa-download"></i> Generar y descargar .docx
    </button>
</form>

En el fichero urls.py configuramos dicha URL con:

app_name = 'publication'
urlpatterns = [
    path('', views.publication, name='publication'),
    path('coverletter/export', views.coverletter_export, name='coverletter_export'),
    ....
]

De esta forma podemos llamar a la función url en la plantilla de Django para que cree automáticamente la URL correcta que llamará a nuestra vista.

Configurar la vista que generará el documento y la descarga

En el fichero views.py, he creado una función llamada coverletter_export tal y como se indica en el fichero urls.py.

Antes que nada, hay que importar lo necesario:

# Para la generación y descarga del fichero
import io

# Para utilizar algunas de las funciones de la librería
from docx import Document
from docx.shared import Inches, Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.text import WD_BREAK

En mi caso he importado algunas funciones concretas de la API de python-docx como Inches y Pt, que permiten indicar longitudes (de párrafos por ejemplo) en pulgadas y en puntos, además de WD_ALIGN_PARAGRAPH que puedes utilizar para personalizar la alineación de los párrafos, pero debes consultar la documentación para ver qué necesitas exactamente.

Al grano, aquí va el ejemplo entero para generar un documento Word básico con un título centrado, y dos párrafos de texto.

Algunos detalles:

  • Podemos crear párrafos en blanco con add_paragraph()
  • Con add_heading() añadimos texto en un elemento de título, y con alignment = WD_ALIGN_PARAGRAPH.CENTER lo centramos.
  • Es posible que si le pasamos un texto con líneas en blanco a la función add_paragraph(), nos lo renderizará con párrafos enteros en blanco. Podemos recorrer el texto con splitlines() descartando las líneas en blanco y se renderizará como es debido.
  • También podemos modificar el espaciado entre líneas usando .paragraph_format.line_spacing e indicándole un valor float.
  • Podemos usar ‘\n’ en el texto para añadir saltos de línea.
def coverletter_export(request):
    document = Document()
 
    # Get the user's fullname
    if request.user.get_full_name():
        document_data_full_name = request.user.get_full_name()
    else:
        document_data_full_name = "[NOMBRE] [APELLIDOS]"
 
    # Print the user's name
    document_elements_heading = document.add_heading(document_data_full_name, 0)
    document_elements_heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
 
    # Add empty paragraph
    document.add_paragraph()
 
    # Print biography and careerpath
    for line in document_data_biography.splitlines():
        if line:
            document_element_biography = document.add_paragraph(line)   
            document_element_biography.alignment = WD_ALIGN_PARAGRAPH.LEFT
            document_element_biography.paragraph_format.line_spacing = 1.15
 
    for line in document_data_careerpath.splitlines():
        if line:
            document_element_careerpath = document.add_paragraph(line)   
            document_element_careerpath.alignment = WD_ALIGN_PARAGRAPH.LEFT
            document_element_careerpath.paragraph_format.line_spacing = 1.15
 
    # Add empty paragraph
    document.add_paragraph()
 
    # Sincerely and name
    document.add_paragraph("Atentamente,\n" + document_data_full_name)

Descarga del documento generado

Como parte final, queda la generación del fichero docx.

En el ejemplo oficial se utiliza la función save() que guarda el fichero en el servidor.

document.save('demo.docx')

Pero en mi caso programé la siguiente función para que en lugar de guardarse en el servidor, el fichero se descargue directamente en el navegador del usuario y no se guarde ninguna copia en nuestros sistemas.

Basta con hacer lo siguiente:

# Save document to memory and download to the user's browser
document_data = io.BytesIO()
document.save(document_data)
document_data.seek(0)
response = HttpResponse(
    document_data.getvalue(),
    content_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
)
response["Content-Disposition"] = 'attachment; filename = "Carta de Presentación.docx"'
response["Content-Encoding"] = "UTF-8"
return response

Con esto deberías tener todo lo necesario para generar tus propios ficheros Word desde cualquier aplicación Python.

Creando StoryDevil: Los primeros resultados

Siempre nos ha costado mucho arrancar un proyecto y conseguir usuarios. El marketing puede llegar a tener un presupuesto infinito, y nuestra idea principal era el boostrapping. Arrancar con capital propio y sin inversiones externas.

Publicamos el proyecto a principios de febrero. Sin avisar a prácticamente nadie. Solo a nuestros amigos, conocidos, y con algún post en nuestros propios perfiles de Facebook, Twitter y LinkedIn. No queríamos salir con grandes fallos así que nos decantamos por un soft-launch.

En nuestra hoja de ruta tenemos un par de planes que empezaremos a ejecutar en breve para lograr nuevos nichos de usuarios que creemos que pueden funcionar, pero antes de llegar a ese punto necesitábamos un poco de masa de usuarios para recibir feedback real y que no sea de nuestros amigos.

Aunque todavía es muy pronto para sacar conclusiones, estos son los primeros resultados.

La prensa especializada habla de nosotros

El dia 14 de abril apareció un pequeño artículo en Wwwhatsnew sobre StoryDevil. Este fue un primer punto de inflexión que causó una considerable subida en los nuevos registros. Hasta este momento no habíamos realizado ninguna campaña de marketing ni habíamos publicitado la web en ningún sitio.

image 1

Este gráfico muestra los nuevos usuarios registrados durante los primeros días tras la aparición del artículo. En total logramos 504 usuarios nuevos en 7 días.

image 2

De estos usuarios, nos interesaba analizar cuantos de ellos terminaban el registro correctamente activando la cuenta, y cuantos entraban en la aplicación y creaban como mínimo un proyecto.

Primeros problemas, usuarios sin activar

Sorprendentemente, el proceso de registro nos funcionó mucho mejor de lo esperado y de 504 usuarios, solo 14 no activaron la cuenta.

Igualmente queríamos saber el porqué de todos los fallidos. Quizá teníamos un problema con el envío del correo de verificación, o un problema de experiencia de usuario.

A las pocas horas del día 14, empezaron a llegar algunos comentarios enviados con la herramienta Userback que tenemos instalada dentro de la aplicación. Todos (4) nos comentaron lo mismo. No les había llegado el correo de confirmación para poder activar la cuenta. Pensamos que 4 de 500 eran muy pocos, pero algo estaba sucediendo. Resultó que todos ellos usaban una direccion de @hotmail. Descubrimos un pequeño desajuste en nuestra configuración de mailing que hacía que Hotmail detectase los correos como Spam. Contactamos con todos los usuarios y les activamos manualmente la cuenta.

Es imprescindible detectar todos los problemas en las primeras fases de un proyecto porque luego, si la actividad se dispara, los fallos tienen consecuencias más graves.

Más tarde llegaron más peticiones de soporte para el resto de cuentas. Pero solo detectamos un error nuevo, usuarios que se equivocaron al escribir su dirección de correo durante el registro. Por ejemplo, escribiendo @gmail.com en lugar de @outlook.com

Empezamos a recibir feedback

Empieza una de las partes más divertidas. Recibir feedback de usarios reales y que no sean de tus amigos y por compromiso.

Tras analizar unas cuantas herramientas para tal, nos decidimos por Userback, que permite empezar con una cuenta gratuita con la que puedes poner un widget en la web o aplicación y los usuarios pueden enviarte feedback que luego recibes en un entorno web, desde el cual puedes gestionarlos como un sistema de ticketing y incidencias.

image

Durante todos estos días hemos recibido 7 tickets a través de Userback.

De estos 7 mensajes, 5 de ellos han sido comentarios con propuestas de mejora, y 2 con bugs detectados por usuarios.

image 3
Interfaz de administración de Userback

Propuestas de mejora

De entre las ideas que nos han llegado, destacamos las propuestas de:

  • Crear una sección donde guardar «Recursos» (objetos) utilizados en producciones.
  • Crear una sección con «Localizaciones» para marcar lugares en un mapa y/o añadir fotos sobre sitios donde grabar.
  • Formatos especiales para telenovelas en Latinoamérica.
  • 2 propuestas de mejoras visuales para facilitar la escritura de textos.

Errores

Estamos bastante satisfechos con este punto porque solo dos usuarios nos han contactado tras detectar un bug que hemos podido corregir de forma bastante rápida. Eran sencillos. Uno de ellos era que al intentar subir una foto de un personaje editado, éste daba error. Permitía hacerlo con nuevos personajes pero no al editarlos si estos eran de tipo «Monstruo».

Causar una muy buena impresión

Es una herramienta gratuita y los usuarios no pagan nada, pero son tus usuarios y dedican su tiempo a utilizarla. Todos son importantes. No hace falta decir que sin usuarios no hay web.

Nuestra intención es responder lo más rápido posible y hacerlo de la mejor forma. Creo que es importante causar un efecto «wow» en este sentido, y que sepan que tras la aplicación hay un equipo con ganas.

A modo de ejemplo, una imagen vale más que mil palabras. Pudimos arreglar un error detectado por un usuario en apenas 13 minutos y a las 21h de un lunes.

Image

Usuarios que se dan de baja

Por el momento, hemos tenido un total de 3 bajas. Todas realizadas en menos de 48 horas tras el registro, por lo que deducimos que estos usuarios se han percatado rápidamente que la herramienta no les encaja. A día de hoy (1 de mayo) no tenemos más bajas, aunque sí tenemos usuarios (8 en total) que tras el registro y primer día de uso, no han vuelto a entrar.

Visitas a la Wiki

StoryDevil tiene una Wiki completamente abierta donde todo el mundo puede consultar información sobre distintos aspectos de la producción cinematográfica y la escritura de guiones.

Estas son las estadísticas de acceso durante todo el mes de abril de 2020. Se notan los primeros días tras la publicación del artículo de Wwwhatsnew y luego una considerable bajada de actividad. Habrá que esperar un poco y analizar si los usuarios realmente lo utilizan a diario, de manera puntual, o bien reforzamos la estrategia SEO para que la Wiki sea un punto de entrada de usuarios y podamos convertirla en algo más global.

image 2

Cómo decir “No”. Plantillas para contestar de forma elegante

0*ShCGJ0P 9vPSSiGZ
Photo by Gemma Evans on Unsplash

El otro día se me ocurrió crear una página en Notion con una serie de plantillas de correo para responder “No” de forma educada y elegante en distintas situaciones.

1 Jnuz27RqhJTOy5JYPwC 8A

Quería ponerlas aquí pero es más cómodo mantener el repositorio en Notion por si voy añadiendo más. Si has llegado hasta aquí y quieres colaborar con alguna, no dudes en enviármela!

Aquí tenéis el enlace: https://www.notion.so/mpampols/C-mo-decir-No-496b2b29fdf74f3db855de4cb21e3a5c

Creando StoryDevil. Parte 1

Creo que será interesante escribir una serie de artículos explicando de forma totalmente transparente el proceso de creación y evolución del proyecto StoryDevil, del que soy fundador junto a Carles Gòdia.

Wireframe de una de las pantallas de StoryDevil

Carles es el típico amigo con el que compartes algo más que amistad. En mi caso, también aventuras empresariales. Empezamos YoteConozco.com en el año 2006, escribimos el guión de una película el año pasado, y publicamos StoryDevil hoy.

¿Por qué lo creamos?

Hace años que mi socio Carles se dedica, además de su profesión, a escribir, dirigir y incluso interpretar obras de teatro. Por mi parte, tras dedicarme muchos años al 3D y los VFX como hobby, empecé a escribir el guión de una película sobre una historia que llevaba gestando años.

Llegado a este punto, Carles y yo decidimos colaborar y escribir juntos el guión para otra película, una adaptación de su obra de teatro “Vecinas”. Más tarde nos dimos cuenta de que no existía ninguna herramienta online potente para juntar todo el trabajo de creación de una obra. Aquí es cuando decidimos crear StoryDevil.

Manos a la obra. ¿Cómo debía ser esta herramienta?

Un entorno para escribir debe ser amplio y limpio. Con una interfaz moderna. Enfocada a usuarios que escriben con dispositivos tipo ordenadores de sobremesa y tablets con teclados externos. No suele ser un trabajo para dispositivos smartphone.

Teníamos claro que StoryDevil sería una herramienta capaz de organizar y gestionar todo el trabajo de creación de una obra, con todos sus detalles, y de manera ordenada. Un lugar donde el creador tenga todo lo necesario para dar vida a su historia.

Nuestro otro objetivo era la formación. No encontramos ningún otro entorno web con información bien estructurada sobre cómo crear un guión. De ahí salió la idea de crear una Wiki para ordenar todos los documentos que explican cómo crear historias. Un lugar que pueda ser consultado en todo momento y, además, de forma libre y abierta para todo el mundo.

Diseño de la aplicación

Empezamos a diseñar los wireframes o retículas de la aplicación con el software Adobe XD. Hacerlo de esta manera nos otorgó una gran agilidad en poder cambiar constantemente los elementos de la interfaz para simular las funcionalidades de las distintas secciones.

Teníamos muy clara la estructura general de la aplicación, así que diseñamos de esta forma el esqueleto de todas y cada una de las secciones para validar los campos de datos a añadir, el funcionamiento de los botones y tenerlo todo muy claro antes de empezar a programar el front y el backend.

Wireframe de una de las pantallas de StoryDevil
Wireframe de una de las pantallas de StoryDevil

Mínimo Producto Viable (MVP)

Una de las cosas de las que te das cuenta en este proceso es de todo lo que sobra y no es indispensable para sacar la primera versión de un producto. Es importante no tener un listado extenso con todo lo que queremos implementar en una aplicación ideal. Teníamos claro que queríamos empezar siguiendo un MVP.

En el diseño anterior, por ejemplo, queríamos añadir la posibilidad de mercar como favoritas las ideas para las storylices. No es algo complejo, pero tampoco es indispensable y no hace más que añadir tiempo de desarrollo quizá innecesario. No definirá el futuro de la aplicación, así que puede esperar.

Tras definir todo lo que imaginamos como indispensable, terminamos con el diseño de todas las pantallas de StoryDevil.

1 Bm8GHMFB7piQTmCae97kyQ
Pantallas de StoryDevil con el software AdobeXD

Herramientras para trabajo en equipo y a distancia.

No tenemos oficina. Cada uno de nosotros tiene otro trabajo y otra casa. Nos toca crear StoryDevil completamente en remoto.

1 dYuXisjP9Ml11t VwmmE0w

Dropbox para repositorio de ficheros de trabajo, Github para el código, y Google Docs para documentación colaborativa. Estas son las tres herramientas principales que hemos utilizado.

Poder trabajar con todo el código en Github nos ha facilitado también enormemente el día a día. Allí continuamos gestionando todas las tareas, actualizamos el estado de las mismas, y guardamos todo el código que forma parte de StoryDevil. La web, la aplicación y la Wiki.

1 2uZeIVNdURqGw7wcf8zU4Q

En futuros artículos explicaré en detalle qué plataforma técnica elegimos y cómo empezamos el proceso de desarrollo del front-end y del back-end.

Arquitectura REST. Qué es y para qué sirve

Photo by Pankaj Patel on Unsplash

Si has llegado hasta aquí es porque quieres aprender un poco sobre esta arquitectura y probablemente tengas pocas nociones sobre esto. De lo contrario, te recomiendo que no sigas leyendo. Es un artículo para principiantes.

El problema con las API

Llegó el año 2000, y empezaron a verse de forma clara las ventajas de los Web Services para la comunicación entre sistemas. Entre aplicaciones web. Cliente-Servidor. En aquel entonces no existía ningún estándar sobre cómo diseñar una API. Se implementaban usando otros protocolos como SOAP. Difíciles de implementar. Pesados. Más complejos en todos los aspectos.

Entonces, un grupo de expertos liderado por Roy Fielding, creó la llamada arquitectura REST. Esto cambió por completo el desarrollo de servicios web hasta la fecha.

REpresentational State Transfer

Esto es lo que significan sus siglas. Se trata de una arquitectura para estandarizar comunicaciones web entre sistemas, logrando que se entiendan mucho mejor entre ellos. A los servicios que cumplen con este diseño se les llama servicios RESTful.

Cliente y Servidor

El servidor expone la API para que el cliente haga uso de ella. Por ejemplo, los servidores de Twitter o Instagram nos facilitan el formato de sus API y nosotros, desde una aplicación web o móvil, accedemos a las mismas para leer o incluso modificar información.

El cliente hace una petición, y el servidor envía una respuesta.

Una de las ventajas de REST es que cliente y servidor no tienen porqué depender del mismo código para entenderse intercambiando mensajes. El servidor puede actualizarse y cambiar, sin que el cliente tenga que hacerlo. Y al revés. Mientras no cambie el formato de los mensajes, no hay problema.

Sistemas Stateless (sin estado)

Antes que nada es importante saber qué significa que un sistema no tenga estado. En una arquitectura REST, cuando se comunican servidor con cliente, estos no necesitan saber en qué estado se encuentra cada uno. Entienden igualmente el mensaje. Ambos pueden entenderlo sin tener en cuenta los anteriores. La base de la arquitectura REST define una interacción orientada a recursos.

Estructura de las comunicaciones

La arquitectura REST se basa en que el cliente envía peticiones para recuperar o modificar recursos, y el servidor responde con el resultado, que puede ser con los datos que hemos pedido o el estado de la petición.

Una petición está formada por:

  • Un verbo HTTP que define la operación a realizar.
  • Una cabecera o header que incluye información sobre la petición.
  • Una ruta o path hacia un recurso.
  • El cuerpo del mensaje o body con los datos de la petición.

Verbos HTTP

Existen 4 verbos principales que forman parte de las peticiones HTTP para interactuar con una API REST, estos son:

  • GET
    Para recuperar información sobre un recurso específico.
  • POST
    Para crear un nuevo recurso.
  • PUT
    Actualizar un recurso
  • DELETE
    Eliminar un recurso

Por ejemplo, en el caso de una API sobre películas.

GET: Consulta y lee información sobre películas.
“/movies” nos permite acceder a todas ellas
“/movies/419” accedemos a la película con ID 419

GET https://www.domain.com/movies/419
Respuesta:
Status Code - 200 (OK)
{
"id": 419,
"title": "Eraserhead",
"year": "1977"
}

POST: Crea una nueva película
“/movies”

POST https://www.domain.com/movies
Respuesta:
Status Code - 201 (CREATED)
{
"movie": {
"id": 419,
"title": "Eraserhead",
"year": "1977"
}
}

PUT: Actualiza los datos de una película
“/movies/419” Actualizamos los datos de la película con id: 419

PUT https://www.domain.com/movies/419
Respuesta:
Status Code - 200 (OK)

DELETE: Elimina una película
“/movies/419” Elimina la película con id: 419

DELETE https://www.domain.com/movies/419
Respuesta:
Status Code - 204 (NO CONTENT)

Con esto deberíamos ofrecer las 4 funciones básicas de un modelo de datos. También conocidas con las siglas CRUD. Crear (Create), Leer (Read), Actualizar (Update) y Eliminar (Delete).

Cabeceras (headers) y parámetros Accept

A lo que he explicado le falta un detalle. Las cabeceras a incluir en las peticiones. El cliente debe enviar un tipo de contenido que el servidor sea capaz de entender. Para esto sirve el campo Accept. Se asegura que el servidor no enviará contenido que el cliente no pueda interpretar. Las opciones a enviar son MIME Types. Estos se utilizan para identificar los tipos de contenido del campo Accept. Consisten en un tipo y un subtipo.

Un tipo puede ser “image”, y un subtipo “image/jpeg”. O bien “text/css” para contenido de estilos CSS o “text/plain” para texto plano.

Algunos ejemplos más:

image — image/png, image/jpeg, image/gif
audio — audio/wav, image/mpeg
video — video/mp4, video/ogg
application — application/json, application/pdf, application/xml, application/octet-stream

Por ejemplo, un cliente que quiera acceder al contenido de una película, podría enviar una petición como esta:

GET /movies/885
Accept: text/html, application/xhtml

Respuestas del servidor

Cuando termina la operación de la API en el servidor, este nos enviará el resultado junto con una serie de datos para que podamos interpretar el éxito o no de la operación y el tipo de datos que recibimos.

Content Types

Cuando el servidor envía un payload hacia el cliente, debe incluir una cabecera llamada content-type. Esto es para que el cliente sepa qué tipo de contenido le llega. Estos datos también son MIME Types, tal y como indicamos anteriormente. Y además, el MIME Type que nos envía el servidor debe coincidir con el que envía el cliente en la cabecera Accept.

Pongamos un ejemplo sencillo que lo explica todo mucho mejor.

Petición del cliente:

GET /movies/445 HTTP/1.1
Accept: text/html, application/xhtml

Respuesta del servidor:

HTTP/1.1 200 (OK)
Content-Type: text/html

Códigos de respuesta

Las respuestas del servidor deben contener códigos de estado para avisar al cliente del éxito o no de cada operación. Para cada verbo HTTP tenemos una serie de códigos esperados que nos indicarán la ejecución correcta de cada operación:

  • GET: Código 200 (OK)
  • POST: Cödigo 201 (CREATED)
  • PUT : Código 200 (OK)
  • DELETE : Código 204 (NO CONTENT)

Las 3 mejores dashboard admin templates gratuitas de 2020

Las 3 mejores dashboard admin templates gratuitas de 2020

He hecho un poco de investigación para encontrar buenos Dashboards estilo “Admin” para uno de mis proyectos, y los he publicado en este artículo para compartir mi lista. Son solo 3, pero son las que he elegido tras analizar más de 30 porque creo que son las mejores.

Todos los que expongo son totalmente gratuitos con licencia MIT y tienen un mínimo de actividad y seguidores que aseguran, en mayor o menor medida, su continuidad y soporte de la comunidad.

Puntos a tener en cuenta para elegir un buen dashboard

  • Continuidad y soporte. Lo que os comentaba. Por muy bonito que sea, debe tener una comunidad mínimamente activa.
  • Que use un framework de CSS conocido y actualizado. La última versión de Foundation o de Bootstrap.
  • Que esté mínimanente probado en entornos productivos.
  • Que tenga repositorio público.
  • Revisa el apartado “Issues” para comprobar que los pequeños detalles e imperfecciones se van corrigiendo.
  • Que sea modular y utilice un gestor de paquetes.

Sleek Dashboard

Uno de los que más me ha gustado. Aunque todavía está en beta, a día de hoy la última versión es del 14 de enero de 2020. Además de los típicos componentes, formularios y tablas, tiene algunas páginas personalizadas interesantes como las de Chat, Contactos, Team y Calendario.

10TFrQGtSlB1jbOT5KcmHEw

Adminator

A destacar que también tiene adaptaciones a Flask y Django

0 Vf96FmFLQmQEvNBN

AdminLTE

Uno de los más veteranos, con distintas versiones para Bootstrap 3 y Bootstrap 4. Está creada por Colorlib y tiene plantillas premium opcionales. Se actualiza constantemente (la última actualización en el momento de escribir esto es de hace apenas 11 días). Actualizan constantemente las dependencias y hay mucha actividad. A tener muy en cuenta.

Debug remoto en Django con Docker y VS Code

Con el uso de contenedores virtualizados con Docker, el proceso de debug ha cambiado un poco. Ahora debemos conectarnos a las instancias virtualizadas para poder hacer debug de nuestro código, y a veces no es tan sencillo como parece.

En este caso explicaré cómo configuro normalmente mi entorno con VS Code para hacer debug remoto para el desarrollo de mis aplicaciones Django bajo Docker.

1uFOXOycdz17QphtnBCpHfQ

VS Code ya tiene una extensión de Docker que facilita las cosas. Soporta Node.js, Python y .NET Core.

Si abrimos la pestaña de docker, ya deberíamos ver un resumen con los contenedores, imágenes y demás.

El primer paso es añadir el módulo ptvsd (Python Tools for Visual Studio debug server) en nuestro fichero requirements.txt

ptvsd == 4.3.2

La versión 5 lleva tiempo en alpha, por lo que te recomiendo mirar en el repositorio oficial.

Ahora debes modificar el fichero launch.json de VS Code con este contenido:

{
"name": "Python: Django",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"console": "integratedTerminal",
"args": [
"runserver",
"0.0.0.0:8000",
],
"django": true
},

Lo siguiente es modificar el fichero manage.py para añadir las siguientes lineas:

if settings.DEBUG:    
if os.environ.get('RUN_MAIN') or os.environ.get('WERKZEUG_RUN_MAIN'):
    import ptvsd
    ptvsd.enable_attach(address = ('0.0.0.0', 3000))
    print "Attached remote debugger"

Django hace live reload de manera que actualiza cada vez que detecta cambios en el código mientras trabajamos. El segundo if es para evitar que se inicialice dos veces.

El fichero manage.py (en mi caso con Django 3) quedaría de la siguiente forma:

El siguiente paso es asegurar que tenemos abierto el puerto 3000 desde Docker. En el caso de docker-compose.yml, yo tengo:

...
services:
...
web:
ports:
- "8000:8000"
- "3000:3000"

Instala también la extensión ms-python.python

2 1

Una vez hecho esto, ya deberíamos poder lanzar la instancia con nuestra configuración de debug. Deberíamos tener el perfil Python: Django (creado en el fichero launch.json) disponible en la pestaña de debug de VSCode, y basta con darle al play para que todo arranque.

3 1

Si lo hemos hecho bien, podremos ver como aparece “Dev Container: Existing Docker Compose…” en la parte inferior, lo cual nos indica que estamos conectados al contenedor Docker a través de VSCode y tenemos todo listo para debugar. En la siguiente captura podemos ver también el print del mensaje “Attached remote debugger” que hemos puesto en el manage.py.

4 3