---
title: Afrontando un MVP con impacto social
tags: laborsord, speechToText
date: 2020-01-23
author: Jorge Aguiar y Yodra López
image: https://images.unsplash.com/photo-1509062522246-3755977927d7?ixlib=rb-1.2.1&auto=format&fit=crop&w=2604&q=80
---
Hace unos meses se nos presentó la oportunidad de colaborar en un proyecto social con la asociación [Laborsord](http://www.laborsord.org/), dedicada a la integración socio-laboral de personas con discapacidad.
El proyecto que se nos planteó consiste en crear una plataforma para que las personas con discapacidad auditiva pudieran seguir una clase en vivo, de manera que mientras el profesor hablaba, ellos pudieran ver en tiempo real la transcripción de la clase. Esta necesidad surge de la falta de intérpretes de signos durante todas las horas lectivas. Del mismo modo, la plataforma se planteó como una alternativa a tomar apuntes ya que seguir la clase leyendo y tomando apuntes al mismo tiempo no resulta muy sencillo.
Lo más importante para sacar este proyecto adelante es tener un buen sistema de transcripción, como pueden imaginar para una persona con discapacidad auditiva puede ser muy difícil mantener la atención y seguir una clase. Por lo tanto, nos enfocamos en tener un MVP basado en esta funcionalidad, para obtener feedback de los usuarios lo antes posible y poder mejorar. La aplicación no tendría sentido si la transcripción no es fluida y correcta.
Es por ello que decidimos probar el servicio de transcripción que ofrece [AWS](https://aws.amazon.com/es/transcribe/) que soporta castellano y nos dió buenos resultados de manera que nos evita invertir tiempo en probar diferentes sistemas de transcripción.
Como el título ya adelanta es un proyecto social que hacemos sin ánimo de lucro, lo que supone tener un tiempo limitado para hacer un producto con la mejor calidad posible.
Así que nos pusimos manos a la obra y creamos un proyecto con el stack tecnológico que ya hemos utilizado en otros proyectos.
Para el frontend hemos utilizado [React](https://es.reactjs.org/) haciendo uso de `Funtional Components` con `Hooks` y hemos hecho testing con [testing-library](https://testing-library.com/). El código está disponible en [Github](https://github.com/lean-mind/laborsord-frontend).
{{< highlight jsx "linenos=table,hl_lines=8 15-17,linenostart=1" >}}
import * as React from 'react';
import './AudioButtons.scss';
import { FC, useState } from 'react';
import { AudioService } from '../../services/AudioService';
import { Button } from '../Button';
import { Container } from '../Container';
import { BrowserMediaService } from '../../services/BrowserMediaService';
interface Dependencies {
audioService: AudioService;
browserMediaService?: BrowserMediaService;
}
export const AudioButtons: FC<Dependencies> = ({ audioService, browserMediaService }) => {
const [isListening, setIsListening] = useState(false);
const startAudio = () => {
setIsListening(true);
if ( browserMediaService ) {
browserMediaService.startAudio({ audio: true, video: false })
.then((userMediaStream: any) => {
const now = new Date();
audioService.streamAudioToWebSocket(userMediaStream);
});
}
};
const stopAudio = () => {
setIsListening(false);
audioService.closeSocket();
};
return (
<Container className="AudioButtons">
<Button className="start" ariaLabel="start" onClick={startAudio} disabled={isListening}>Empezar clase</Button>
<Button className="stop" ariaLabel="stop" onClick={stopAudio} disabled={!isListening}>Parar clase</Button>
</Container>
);
};
AudioButtons.displayName = 'AudioButtons';
{{< / highlight >}}
A la hora de crear los componentes hemos decidido utilizar [Atomic Design](http://atomicdesign.bradfrost.com/) ya que de esta forma podemos realizar un cambio de React a React Native modificando el menor número de líneas de código, puesto que solo sería necesario modificar los componentes creados como átomos.
Algo que destacaríamos de `testing-library` es la dificultad que tenemos los desarrolladores de adoptar el rol de usuario a la hora de hacer los tests y no testear con el conocimiento que ya tenemos como desarrolladores.
{{< highlight jsx "linenos=table,hl_lines=8 15-17,linenostart=1" >}}
import * as React from 'react';
import { render } from '@testing-library/react';
import { AudioButtons } from './';
import { AudioService } from '../../services/AudioService';
import { BrowserMediaService } from '../../services/BrowserMediaService';
const browserMediaServiceMock: BrowserMediaService = {
startAudio: jest.fn(() => Promise.resolve()),
};
// @ts-ignore
const audioServiceMock: AudioService = {
streamAudioToWebSocket: jest.fn(),
};
const renderAudioButton = () => {
const utils = render(
<AudioButtons audioService={audioServiceMock} browserMediaService={browserMediaServiceMock}/>);
const buttonStart = utils.getByLabelText('start');
const buttonStop = utils.getByLabelText('stop');
return { buttonStart, buttonStop, ...utils };
};
describe('AudioButtons', () => {
test('should render a enabled start button and a disabled stop button', () => {
const { buttonStart, buttonStop } = renderAudioButton();
expect(buttonStart).not.toHaveAttribute('disabled');
expect(buttonStop).toHaveAttribute('disabled');
});
test('should change to disable start button when clicking in start', () => {
const { buttonStart, buttonStop } = renderAudioButton();
buttonStart.click();
expect(buttonStart).toHaveAttribute('disabled');
expect(buttonStop).not.toHaveAttribute('disabled');
});
});
{{< / highlight >}}
A la hora de realizar los tests nos dimos cuenta que no habíamos implementado la lógica de habilitar y deshabilitar los botones. Por lo que en ese momento aplicamos TDD para añadir esta lógica. De modo que creamos el segundo test, en el cual, __como usuarios__, al acceder la primera vez a la página, vemos un botón _start_ habilitado y un bóton _stop_ dehabilitado. En este momento el test estaba en rojo, ya que no teníamos el atributo `disabled` contemplado en la lógica del componente. Añadimos un nuevo estado a nuestro componente, llamado `isListening`, el cual indicará al componenete `Button` si debe estar habilitado o no.
Hemos usado un estado para indicar el momento de la acción en la que nos encontramos, de manera que si ya hemos empezado la clase, no podamos iniciar otra puesto que no tendría sentido. Del mismo modo que no deberíamos poder terminar una clase sin haberla empezado. En cuanto a por qué usar un estado y no una propiedad en el componente `AudioButtons`, se debe a que este se modifica siempre dentro del propio componente.
Para finalizar comprobamos que al pulsar el botón _start_ el comportamiento que espera el usuario es el correcto.
Por otra parte, aunque sabemos que no es buena practica, y no sería la versión final del producto, hemos decido hacer la llamada al servicio de AWS Transcribe desde el frontend, sacrificando la seguridad a favor de mejorar la latencia y reducir el tiempo de desarrollo para el MVP. A futuro habrá que buscar una alternativa para evitar el acceso a estas credenciales, a poder ser, sin poner en riesgo la baja latencia. Esto es deuda técnica planificada para obtener el MVP en el menor tiempo posible.
En el backend hemos usado el framework [SpringBoot](https://spring.io/projects/spring-boot) con Java haciendo uso de WebSockets debido a la necesidad de distribuir el texto de la transcripción, a los distintos alumnos, según lo recibimos de AWS. Para ello hemos utilizado el [ejemplo que nos proporciona el framework](https://spring.io/guides/gs/messaging-stomp-websocket/), por lo que ha quedado bastante sencillo. El código está disponible en [Github](https://github.com/lean-mind/laborsord-backend).
## Conclusiones
Creemos que la clave de este MVP, ha sido focalizar los esfuerzos en conseguir lo que considerábamos __la parte esencial__ del proyecto, en nuestro caso, tal y como hemos comentado, la transcripción a texto. Esta manera de enfocarlo nos ha permitido, en una semana y media, hacer pruebas con usuarios reales, hecho que consideramos clave para la viabilidad del mismo.
Cabe destacar también la importancia de saber asumir el rol de usuario a la hora de hacer los tests de los componentes, focalizando así el desarrollo en lo que el usuario necesita y no en como se ha implementado la solución.