ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA INFORMÁTICA INGENIERÍA DEL SOFTWARE Servicio multiplataforma para la consulta y gestión de contenido multimedia Multiplatform service for consultation and manage multimedia content Realizado por Jesús Garrido Moscoso Tutorizado por Eduardo Guzmán De los Riscos Departamento Lenguajes y Ciencias de la Computación UNIVERSIDAD DE MÁLAGA MÁLAGA, DICIEMBRE 2016 Fecha defensa: El Secretario del Tribunal
! 5 Resumen: En la actualidad, el consumo de streaming de vídeo bajo demanda está casi al mismo nivel que el de la televisión convencional, solo 5 puntos por debajo de ésta. A esto añadimos la multitud de plataformas multimedia existentes para ver estos contenidos, provocando una gran fragmentación del material que visualizas, la pérdida de listas clasificadas o preferencias y la repetición de contenido entre una plataforma u otra. Este proyecto trata de solucionar estos problemas con el desarrollo de una aplicación web y móvil, para los sistemas operativos Android e iOS. Su objetivo es centralizar toda la información clasificada y visualizada por un usuario en nuestra plataforma, ofreciendo una experiencia sencilla y usable para la clasificación de este contenido en listas de películas asociadas al usuario, que podría categorizar como: vistas, pendientes, favoritas y blacklist. Además, la plataforma ofrecerá la búsqueda de películas por su título y la posibilidad de consultar la información detallada de una película, incluidas las puntuaciones o ratings, recogidas de las plataformas de puntuaciones más importantes y conocidas por los usuarios. Palabras clave: móvil, aplicación, página, araña, Django, API, REST, script, Python, Android, Material, nativo, Java, PostgreSQL, película, multimedia, clasificación, puntuación, vista, pendiente, favorita.
! 6 Abstract: Nowadays, the consumption of streaming video on demand is almost at the same level that which concerned to conventional television, only 5 points below this. Also, we have to take into account the large number of existing multimedia platforms to watch those topics. It leads to a great fragmentation of the material that you can watch, the loss of the classified list and the repetition of the topic among a platform and another one. This project tries to fix those problems with the creation of a web and mobile environment for Android and iOS. Its aim is to focus all the classified and watched information by an user in our platform, offering an useful experience for the classification of this topic in film lists associated to the user, which could be: seen, watchlist, favorites and blacklist. Besides, the platform will offer the search of films considering the tittle and the possibility of consulting the detailed information of a film, including ratings, getting from the platforms with the most important ratings and known by the users. Keywords: app, mobile, application, web, scrapper, Django, API, REST, script, Python, Android, Material, native, Java, PostgreSQL, movie, multimedia, film, rating, seen, watchlist, favorites, blacklist.
! 7 Índice general Índice general ............................................................................................................ 7 Capítulo 1. Introducción ........................................................................................... 9 1.1 Motivación .......................................................................................................... 9 1.2 Objetivos .......................................................................................................... 10 1.3 Materiales y tecnologías usadas ...................................................................... 11 1.4 Contenido de la memoria ................................................................................. 11 1.5 División del trabajo ........................................................................................... 12 Capítulo 2. Tecnologías y herramientas utilizadas .............................................. 13 2.1 Python 3 ........................................................................................................... 13 2.2 pip .................................................................................................................... 13 2.3 Django .............................................................................................................. 14 2.4 Django REST Framework ................................................................................ 14 2.5 PostgreSQL ...................................................................................................... 14 2.6 API REST ......................................................................................................... 15 2.7 Android ............................................................................................................. 15 Capítulo 3. Especificación de requisitos .............................................................. 17 3.1 Recopilación de datos ...................................................................................... 17 3.2 Aplicación Mooviest (Android) .......................................................................... 18 3.2.1 Requisitos funcionales ............................................................................... 18 3.2.2 Requisitos no funcionales .......................................................................... 22 Capítulo 4. Análisis y diseño .................................................................................. 23 4.1 Casos de uso ................................................................................................... 23 4.1.1 Aplicación Mooviest (Android) ................................................................... 23 4.2 Arquitectura ...................................................................................................... 37 4.2.1 Arquitectura cliente-servidor. ..................................................................... 37 4.2.2 Arquitectura Modelo Vista Controlador ...................................................... 38 4.2.2.1 Modelo ................................................................................................. 39 4.2.2.2 Vista ..................................................................................................... 40 4.2.2.3 Controlador .......................................................................................... 40 4.3 Base de datos .................................................................................................. 40 4.4 Estructura y diseño .......................................................................................... 42 Capítulo 5. Implementación e instalación ............................................................. 45 5.1 API (Application Programming Interface) ......................................................... 45 5.1.1 Desarrollo .................................................................................................. 45
! 8 5.1.1.1 Autenticación ....................................................................................... 45 5.1.1.2 Permisos .............................................................................................. 45 5.1.1.3 Serializers ............................................................................................ 46 5.1.1.4 Viewsets .............................................................................................. 47 5.1.2 Peticiones .................................................................................................. 49 5.1.2.1 User ..................................................................................................... 49 5.1.2.2 Movie ................................................................................................... 57 5.2 Scripts .............................................................................................................. 62 5.3 Creación e instalación del proyecto ................................................................. 65 5.3.1 Aplicación Mooviest (Servidor Django) ...................................................... 65 5.3.1.1 Creación del proyecto .......................................................................... 66 5.3.1.2 Instalación y configuración del proyecto .............................................. 67 5.3.1.3 Configuración de la base de datos ...................................................... 67 5.3.2 Aplicación Mooviest (Android) ................................................................... 68 5.3.2.1 Creación del proyecto .......................................................................... 69 5.3.2.2 Instalación y configuración del proyecto .............................................. 71 5.3.2.3 GitHub ................................................................................................. 72 5.4 Desarrollo de la aplicación Mooviest (Android) ................................................ 72 5.4.1 Introducción, inicio de sesión y registro ..................................................... 72 5.4.2 Home ......................................................................................................... 75 5.4.3 Menú lateral ............................................................................................... 76 5.4.4 Perfil del usuario ........................................................................................ 77 5.4.5 Editar perfil del usuario .............................................................................. 78 5.4.6 Sistema de clasificación de películas (Swipe) ........................................... 79 5.4.7 Listas del usuario ....................................................................................... 81 5.4.8 Lista completa ............................................................................................ 82 5.4.9 Búsqueda de películas .............................................................................. 83 5.4.10 Detalle de una película ............................................................................ 84 5.4.11 Cliente REST ........................................................................................... 85 5.4.12 Llamadas a la API .................................................................................... 86 Capítulo 6. Conclusiones y trabajo futuro ............................................................ 87 6.1 Conclusiones .................................................................................................... 87 6.2 Trabajo futuro ................................................................................................... 88 Bibliografía ............................................................................................................... 89
! 9 Capítulo 1. Introducción 1.1 Motivación En los últimos años han aparecido grandes novedades tecnológicas como son los smartphones, tablets, Smart TVs… Gracias a ello, muchos de los contenidos e información que consumimos se visualizan a través de estos dispositivos. Hasta ahora, un usuario tenía que esperar a una hora y día concretos de la semana para ver una serie o película. Actualmente, el consumo de contenidos digitales a la carta, está en aumento, a través de plataformas streaming como Netflix, Wuaki, Yomvi… Con estos nuevos servicios, ha cambiado totalmente la forma y demanda del contenido digital en Internet; ahora es el usuario el que decide qué contenido y cuándo lo quiere ver. Según un informe de Ericsson, el consumo de streaming de vídeo bajo demanda está casi al mismo nivel que el de la televisión convencional: 80% frente a 85%, como podemos apreciar en la Figura 1.1. ! Figura 1.1 Porcentaje de personas que ven diferentes tipos de medios de comunicación más de una vez por semana De los usuarios que consumen contenido bajo demanda, lo hacen a través de una gran variedad de plataformas con diferente catálogo, como podemos apreciar en la Figura 1.2. Esto provoca una gran fragmentación del contenido que se visualiza, por lo que las listas de contenido visualizado o preferencias se perderán, provocando que haya contenido repetido y sin clasificar entre una plataforma u otra.
! 16
! 17 Capítulo 3. Especificación de requisitos La especificación de requisitos es una descripción completa del comportamiento del sistema que se va a desarrollar. En ella se describen los servicios que ha de ofrecer el sistema y las restricciones asociadas a su funcionamiento. Esta descripción completa del comportamiento del sistema se clasifica en dos grupos: - Requisitos funcionales: Identifican los servicios que el sistema debe proporcionar, cómo debe reaccionar en respuesta a entradas externas y cómo debe comportarse en situaciones particulares. - Requisitos no funcionales: Estos requisitos nos dicen las restricciones que afectan a los servicios y al correcto funcionamiento del sistema, tales como restricciones de tiempo, estándares, etc. A continuación, se comentará cómo se llevó a cabo la recopilación de los datos de diferentes APIs para, en base a estos, realizar los requisitos. 3.1 Recopilación de datos El primer paso para la ejecución de este proyecto, fue realizar un estudio y búsqueda en Internet, para encontrar los diferentes sitios webs y plataformas que ofrecían APIs gratuitas y así obtener toda la información relacionada de las películas existentes. Para ello estudiábamos qué información ofrecían de una película, si tenían una base de datos completa, etc. Toda esta información era necesaria para saber qué API utilizar y cuál sería el modelado final del sistema. En todo este proceso, se enviaron emails a algunos sitios webs para confirmar si podíamos recopilar sus datos en nuestra base de datos, o preguntar por la extensión de su contenido. Además de esto necesitábamos recopilar la puntuación o rating de las diferentes webs más importantes para las películas. En este proceso, vimos cómo podíamos obtenerlas o si no las proporcionaba alguna plataforma de manera gratuita. Para la recogida de información de películas, finalmente optamos por obtenerla de la API de Tviso, que nos proporcionaba la información en el idioma español; para inglés, escogimos Trakt.tv y, por último, para las puntuaciones de películas, desarrollamos scrappers específicos para las páginas de filmaffinity, IMDb, Metacritic y RottenTomatoes.
! 18 3.2 Aplicación Mooviest (Android) 3.2.1 Requisitos funcionales - RF01 - Registrarse en el sistema: La aplicación debe permitir el registro de un usuario introduciendo la información requerida por el sistema. • RF01.1 - Introducir su nombre de usuario: El usuario podrá introducir su nombre de usuario que servirá como identificador del sistema. • RF01.2 - Introducir su email: El usuario podrá introducir su email que también servirá como identificador del sistema. • RF01.3 - Introducir su contraseña: El usuario podrá introducir su contraseña. • RF01.4 - Volver a introducir su contraseña: El usuario podrá volver a introducir su contraseña. • RF01.5 - Error: Si ha ocurrido algún error, faltan campos obligatorios, no cumple con las validaciones de los campos obligatorios o no hay conexión a Internet, se mostrará un mensaje con dicha información. - RF02 - Iniciar sesión en el sistema: La aplicación debe permitir iniciar sesión en el sistema. • RF02.1 - Introducir su nombre de usuario o correo electrónico: El usuario podrá introducir su nombre de usuario o email. • RF02.2 - Introducir su contraseña: El usuario podrá introducir su contraseña. • RF02.3 - Error: Si ha ocurrido algún error, no hay conexión a Internet, se mostrará un mensaje con dicha información. - RF03 - Cerrar sesión en el sistema: La aplicación permitirá cerrar la sesión del usuario que la había iniciado o se había registrado, borrando así todos sus datos guardados en el dispositivo. - RF04 - Mostrar un menú lateral: La aplicación permitirá abrir un menú lateral con todas las secciones de la aplicación, para poder cambiar de una a otra con facilidad. - RF05 - Mostrar el perfil del usuario: La aplicación permitirá mostrar el perfil del usuario que ha iniciado sesión o se ha registrado. - RF06 - Mostrar ajustes del perfil del usuario: La aplicación permitirá mostrar los ajustes del perfil del usuario. - RF07 - Modificar el perfil del usuario: La aplicación permitirá modificar el perfil del usuario que ha iniciado sesión o se ha registrado.
! 19 • RF07.1 - Modificar la imagen del perfil del usuario: Podrá modificar la imagen del perfil del usuario, accediendo a una imagen guardada en su dispositivo. • RF07.2 - Modificar su nombre de usuario: Podrá modificar su nombre de usuario por otro no existente en el sistema. • RF07.3 - Introducir o modificar su nombre: Podrá introducir o modificar su nombre. • RF07.4 - Introducir o modificar sus apellidos: Podrá introducir o modificar sus apellidos. • RF07.5 - Modificar su email: Podrá modificar su email por otro no existente en el sistema. • RF07.6 - Introducir o modificar su fecha de nacimiento: Podrá introducir o modificar su fecha de nacimiento, mediante un calendario. • RF07.7 - Introducir o modificar su ciudad: Podrá introducir o modificar su ciudad. • RF07.8 - Introducir o modificar su código postal: Podrá introducir o modificar su código postal. • RF07.9 - Error: Si ha ocurrido un error, faltan campos obligatorios, ya existe otro usuario en el sistema con el mismo nombre de usuario o email, se mostrará un mensaje con dicha información. - RF08 - Mostrar un sistema de clasificación de películas: La aplicación permitirá mostrar un sistema de clasificación de películas para el usuario que ya esté registrado o haya iniciado sesión, es decir se mostrará la carátula de una película y unos botones para su clasificación. • RF08.1 - Clasificar una película realizando un gesto o swipe sobre ella: La aplicación permitirá clasificar una película realizando unos determinados gestos. § RF08.1.1 - Clasificar una película a la lista de películas favoritas del usuario: La aplicación permitirá, mediante un gesto hacia arriba sobre la película, clasificarla en la lista de películas favoritas del usuario. § RF08.1.2 - Clasificar una película a la lista de películas pendientes del usuario: La aplicación permitirá, mediante un gesto hacia la izquierda sobre la película, clasificarla en la lista de películas pendientes del usuario. § RF08.1.3 - Clasificar una película a la lista de películas vistas del usuario: La aplicación permitirá, mediante un gesto hacia la derecha sobre la película, clasificarla en la lista de películas vistas del usuario. § RF08.1.4 - Clasificar una película a la lista negra (blacklist) del usuario: La aplicación permitirá, mediante un gesto hacia
! 20 abajo sobre la película, clasificarla en la blacklist de películas del usuario, es decir, en una lista de películas que no le interesan. • RF08.2 - Clasificar una película pulsando uno de los cuatro botones visibles: La aplicación permitirá clasificar una película pulsando uno de los cuatro botones visibles para el usuario. § RF08.1.1 - Clasificar una película a la lista de películas favoritas del usuario: La aplicación permitirá, pulsando un botón con el icono de una estrella, clasificarla en la lista de películas favoritas del usuario. § RF08.1.2 - Clasificar una película a la lista de películas pendientes del usuario: La aplicación permitirá, pulsando un botón con el icono de un marcapáginas, clasificarla en la lista de películas pendientes del usuario. § RF08.1.3 - Clasificar una película a la lista de películas vistas del usuario: La aplicación permitirá, pulsando un botón con el icono de un ojo, clasificarla en la lista de películas vistas del usuario. § RF08.1.4 - Clasificar una película a la lista negra (blacklist) del usuario: La aplicación permitirá, pulsando un botón con el icono de una cruz, clasificarla en la blacklist de películas del usuario. - RF09 - Mostrar la previsualización de las listas de películas clasificadas por el usuario: La aplicación permitirá mostrar una previsualización de 10 películas de cada lista del usuario. - RF10 - Mostrar las listas de películas clasificadas por el usuario: La aplicación permitirá mostrar las listas de películas clasificadas del usuario que ha iniciado sesión o se ha registrado. • RF10.1 - Mostrar la lista de películas vistas por el usuario: La aplicación permitirá mostrar la lista de películas vistas por el usuario. • RF10.2 - Mostrar la lista de películas pendientes del usuario: La aplicación permitirá mostrar la lista de películas pendientes del usuario. • RF10.3 - Mostrar la lista de películas favoritas del usuario: La aplicación permitirá mostrar la lista de películas favoritas del usuario. • RF10.4 - Mostrar la lista negra de películas (blacklist) del usuario: La aplicación permitirá mostrar la blacklist de películas del usuario. - RF11 - Mostar la página de búsqueda de películas: La aplicación permitirá mostrar la página de búsqueda de películas. - RF12 - Buscar películas por su título original o el título del idioma del usuario: La aplicación permitirá buscar películas por su título original o el título del idioma del usuario.
! 21 • RF12.1 - Introducir o modificar el campo de búsqueda de una película: Podrá introducir o modificar el campo de búsqueda de una película. • RF12.2 - Borrar el campo de búsqueda de una película: Podrá, a través de un botón con el icono de una cruz, borrar el contenido del campo de búsqueda de una película. • RF12.3 - Aviso: Si no se ha encontrado ningún resultado o no hay conexión a internet, se mostrará un mensaje con dicha información. - RF13 - Mostrar la información detallada de una película: La aplicación permitirá mostrar la información detallada de una película. • RF13.1 - Puntuaciones o ratings de diferentes páginas web y plataformas: Se mostrarán las puntuaciones de la película de diferentes páginas web y plataformas. § RF13.1.1 - Mostrar la puntuación de la plataforma Tviso: La aplicación permitirá mostrar la puntuación de la película de la plataforma Tviso. § RF13.1.1 - Mostrar la puntuación de la plataforma IMDb: La aplicación permitirá mostrar la puntuación de la película de la plataforma IMDb. § RF13.1.1 - Mostrar la puntuación de nuestra plataforma: La aplicación permitirá mostrar la puntuación de nuestra plataforma, que será la media de las demás puntuaciones. • RF13.2 - Título de la película en el idioma del usuario: Se mostrará el título de la película en el idioma del usuario. • RF13.3 - Sinopsis: Se mostrará la sinopsis de la película. • RF13.4 - Duración: Se mostrará la duración de la película. • RF13.5 - Año: Se mostrará el año de estreno: Se mostrará el año de estreno de la película. • RF13.6 - Géneros: Se mostrará un listado de géneros de la película. • RF13.7 - Reparto: Se mostrará un listado con el reparto de la película. La imagen del actor o director, su nombre real y el del papel en la película. • RF13.8 - Clasificar la película mediante botones: La aplicación permitirá clasificar la película mediante botones, al igual que el requisito RF08.2.
! 22 3.2.2 Requisitos no funcionales - RNF01 - Conexión a Internet: La aplicación necesitará conexión a Internet para el acceso a la base de datos. - RNF02 - Adaptabilidad al dispositivo: Debido a la gran cantidad de dispositivos móviles con diferentes pantallas que existen en el mercado, la interfaz de la aplicación debe ser adaptable (responsive), permitiendo así a cualquier usuario, usar las funciones y disfrutar de su experiencia sin importar el dispositivo que utilice. - RNF03 - Autenticación por token: Para mayor seguridad y evitar que usuarios no autorizados en la aplicación accedan a información del contenido, hemos habilitado la autenticación por token para la realización de peticiones a la API. - RNF04 - Conexiones seguras HTTPS: Para mayor seguridad en las transacciones entre el cliente y el servidor, se han habilitado conexiones seguras HTTPS, para intentar evitar la interceptación de información por parte de terceros en las conexiones. - RNF05 - Claves cifradas: Para mayor seguridad en las cuentas de nuestros usuarios se han cifrado sus claves y así evitar el robo de cuentas. - RNF06 - Soporte multilenguaje: La aplicación se mostrará en el idioma que el usuario tenga configurado su dispositivo, podrá ser inglés o español, en cualquier otro caso, se mostrará en inglés. - RNF07 - La base de datos se mantendrá actualizada con nuevo contenido periódicamente: La aplicación permitirá la actualización e incorporación de contenido, a través de la ejecución automática y periódica de scripts.
! 23 Capítulo 4. Análisis y diseño 4.1 Casos de uso En esta sección se muestran los diferentes casos de uso que derivan de los requisitos funcionales especificados en el Capítulo 3. Especificación de requisitos. 4.1.1 Aplicación Mooviest (Android) En la Figura 4.1, podemos observar el diagrama de casos de uso principal de la aplicación Android. Para que no ocupara demasiado lo hemos dividido y en la Figura 4.2, mostramos el caso de uso CU08 ampliado. ! Figura 4.1 Diagrama de casos de uso principal de la aplicación Android
! 24 ! Figura 4.2 Ampliación del caso de uso CU08 CU01 Registrarse en el sistema Descripción El usuario se registra en el sistema Precondición El usuario ha abierto la aplicación Escenario principal Paso Acción 1 El usuario pulsa el enlace “¿No tiene cuenta todavía? Cree una” 2 El sistema carga la página de registro del usuario 3 El usuario introduce un nombre de usuario 4 El usuario introduce su email 5 El usuario introduce una contraseña 6 El usuario vuelve a introducir su contraseña 7 El usuario pulsa el botón “CREAR CUENTA” 8 El sistema comprueba que los datos son válidos 9 El sistema registra al usuario en el sistema 10 El sistema obtiene los datos del usuario
! 25 11 El sistema obtiene las películas del swipe y de las listas del usuario 12 El sistema carga la página principal de la aplicación (Home) Postcondición El usuario se ha registrado en el sistema Escenario alternativo Paso Acción 8 Si los datos no son válidos E1 El sistema informa que el nombre de usuario debe tener al menos tres caracteres E2 El sistema informa que el email no es válido E3 El sistema informa que la contraseña debe tener al menos seis caracteres alfanuméricos E4 El sistema informa que las contraseñas no coinciden CU02 Iniciar sesión en el sistema Descripción El usuario inicia sesión en el sistema Precondición El usuario ha abierto la aplicación Escenario principal Paso Acción 1 El usuario introduce su email o nombre de usuario 2 El usuario introduce su contraseña 3 El usuario pulsa el botón “INICIAR SESIÓN” 4 El sistema comprueba que es un usuario existente 5 El sistema obtiene los datos del usuario 6 El sistema obtiene las películas del swipe y de las listas del usuario 7 El sistema carga la página principal de la aplicación (Home) Postcondición El usuario ha iniciado sesión en el sistema Escenario alternativo Paso Acción 4 Si el usuario no existe en el sistema E1 El sistema informa que el usuario no existe en el sistema
! 32 Escenario alternativo Paso Acción ! CU10.1 Clasificar una película a la lista de películas favoritas con un botón Descripción El usuario podrá clasificar una película a su lista de películas favoritas, pulsando un botón Precondición CU01 ó CU02 Escenario principal Paso Acción 1 El usuario pulsa el botón con el icono de la estrella 2 El sistema clasifica la película en la lista de películas favoritas del usuario 3 El sistema elimina la película de la vista 4 El sistema carga otra película en la vista Postcondición El usuario ha clasificado una película en su lista de películas favoritas Escenario alternativo Paso Acción CU10.2 Clasificar una película a la lista de películas pendientes con un botón Descripción El usuario podrá clasificar una película a su lista de películas pendientes, pulsando un botón Precondición CU01 ó CU02 Escenario principal Paso Acción 1 El usuario pulsa el botón con el icono de un marcapáginas 2 El sistema clasifica la película en la lista de películas pendientes del usuario 3 El sistema elimina la película de la vista 4 El sistema carga otra película en la vista Postcondición El usuario ha clasificado una película en su lista de películas pendientes
! 33 Escenario alternativo Paso Acción CU10.3 Clasificar una película a la lista de películas vistas con un botón Descripción El usuario podrá clasificar una película a su lista de películas vistas, pulsando un botón Precondición CU01 ó CU02 Escenario principal Paso Acción 1 El usuario pulsa el botón con el icono de un ojo 2 El sistema clasifica la película en la lista de películas vistas del usuario 3 El sistema elimina la película de la vista 4 El sistema carga otra película en la vista Postcondición El usuario ha clasificado una película en su lista de películas vistas Escenario alternativo Paso Acción CU10.4 Clasificar una película a la lista de películas que no le interesan (blacklist) con un botón Descripción El usuario podrá clasificar una película a su blacklist, pulsando un botón Precondición CU01 ó CU02 Escenario principal Paso Acción 1 El usuario pulsa el botón con el icono de una cruz 2 El sistema clasifica la película en la blacklist del usuario 3 El sistema elimina la película de la vista 4 El sistema carga otra película en la vista Postcondición El usuario ha clasificado una película en su blacklist Escenario alternativo Paso Acción
! 34 CU11 Consultar la previsualización de las listas de películas del usuario Descripción El usuario podrá consultar la previsualización de sus listas de películas Precondición CU01 ó CU02 Escenario principal Paso Acción 1 El usuario pulsa sobre el botón de las listas que se encuentra en la barra de tabs 2 El sistema carga la página de previsualización de las listas del usuario Postcondición El usuario ve la previsualización de sus listas de películas Escenario alternativo Paso Acción CU12 Consultar la lista de películas favoritas del usuario Descripción El usuario podrá consultar su lista completa de películas favoritas Precondición CU01 ó CU02 CU11 Escenario principal Paso Acción 1 El usuario pulsa sobre el botón “MÁS” de la lista de películas favoritas 2 El sistema carga la página con la lista de películas favoritas del usuario Postcondición El usuario ve su lista de películas favoritas Escenario alternativo Paso Acción CU13 Consultar la lista de películas pendientes del usuario Descripción El usuario podrá consultar su lista completa de películas pendientes Precondición CU01 ó CU02 CU11
! 35 Escenario principal Paso Acción 1 El usuario pulsa sobre el botón “MÁS” de la lista de películas pendientes 2 El sistema carga la página con la lista de películas pendientes del usuario Postcondición El usuario ve su lista de películas pendientes Escenario alternativo Paso Acción CU14 Consultar la lista de películas vistas del usuario Descripción El usuario podrá consultar su lista completa de películas vistas Precondición CU01 ó CU02 CU11 Escenario principal Paso Acción 1 El usuario pulsa sobre el botón “MÁS” de la lista de películas vistas 2 El sistema carga la página con la lista de películas vistas del usuario Postcondición El usuario ve su lista de películas vistas Escenario alternativo Paso Acción CU15 Consultar la lista de películas que no le interesan (blacklist) del usuario Descripción El usuario podrá consultar su blacklist completa Precondición CU01 ó CU02 CU11 Escenario principal Paso Acción 1 El usuario pulsa sobre el botón “MÁS” de la blacklist 2 El sistema carga la página con la blacklist del usuario Postcondición El usuario ve su blacklist
! 36 Escenario alternativo Paso Acción CU16 Mostrar página de búsqueda de películas Descripción El usuario podrá acceder a la página de búsqueda de películas Precondición CU01 ó CU02 Escenario principal Paso Acción 1 El usuario pulsa sobre el botón con una lupa en la barra superior 2 El sistema carga la página de búsqueda de películas Postcondición El usuario ve la página de búsqueda de películas Escenario alternativo Paso Acción CU17 Buscar películas por su título Descripción El usuario podrá buscar películas por su título Precondición CU01 ó CU02 Escenario principal Paso Acción 1 El usuario introduce un título 2 El sistema busca películas que contengan en su título esa palabra o palabras 3 El sistema muestra la lista de películas encontradas Postcondición El usuario ve el resultado de las películas encontradas Escenario alternativo Paso Acción 2 Si el sistema no ha encontrado resultados E1 El sistema informa que no ha encontrado resultados
! 37 CU18 Consultar la información detallada de una película Descripción El usuario podrá consultar la información detallada de una película Precondición CU01 ó CU02 Escenario principal Paso Acción 1 El usuario pulsa sobre una película del sistema de clasificación 2 El sistema carga la página con la información detallada de la película. Postcondición El usuario ve la página de información detallada de una película Escenario alternativo Paso Acción 4.2 Arquitectura Podríamos distinguir dos tipos de arquitecturas en este proyecto: por un lado, la arquitectura a alto nivel cliente-servidor entre los diferentes subsistemas, en la que los clientes serían las aplicaciones móviles y web y el servidor, en este caso lo hemos desarrollado en el framework Django. Por otro lado, en el caso de la aplicación Android, en cuanto al diseño de la aplicación y la organización de clases y funciones, se ha seguido el patrón Modelo Vista Controlador (MVC). 4.2.1 Arquitectura cliente-servidor. La arquitectura cliente-servidor es un modelo de aplicación distribuida, en el que las tareas se reparten entre los proveedores de recursos o servicios, llamados servidores, y los demandantes, llamados clientes. Esta conexión se realiza cuando un cliente solicita información del servidor; en este momento, el cliente realiza una petición al servidor, y este le devuelve una respuesta. En nuestro caso, nuestro servidor Django a través de Django REST Framework, nos suministrará un servicio: devolvernos la información solicitada por las apps de nuestra base de datos, para que cuando todos los clientes, que se encuentren en ejecución así lo soliciten, siempre tengamos a nuestro servidor respondiendo a todas las peticiones. En la Figura 4.3, podemos ver el diagrama de la arquitectura clienteservidor para nuestro sistema.
! 38 4.2.2 Arquitectura Modelo Vista Controlador En nuestra app Android, seguimos el conocido patrón MVC. Un modelo muy usado en aplicaciones, donde se definen tres capas interconectadas para separar: el funcionamiento o lógica de la aplicación (Controlador), el tratamiento de los datos (Modelo), y la interfaz gráfica (Vista). En la Figura 4.4, podemos observar el diagrama de la arquitectura MVC para nuestra aplicación. SERVIDOR% BASE%DE%DATOS% CLIENTES% % API% Figura 4.3 Diagrama de la arquitectura cliente-servidor de nuestro sistema
! 39 La forma en la que debe trabajar este patrón es conectando la Vista, que es la parte visible para el usuario de la aplicación, con un Controlador, que recibirá los datos de la Vista para tratarlos, o se los mandará a la Vista para mostrarlos al usuario. A su vez, este Controlador será el que se conecte con la capa del Modelo, para poder guardar los datos recibidos desde la Vista, o para solicitar los datos que queremos enviar a la Vista. 4.2.2.1 Modelo Es la capa donde se trabaja con los datos, es decir, contiene los métodos para acceder a la información, en este caso, realizando peticiones a nuestra API, y las clases en Java para poder hacer uso de esta información en nuestra aplicación. Estos datos son los que tenemos almacenados en la base de datos PostgreSQL de nuestro servidor Django. Métodos como getUserProfile() o updateUserProfile(), permiten acceder a la librería Retrofit, que desempeña el rol de modelo dentro de nuestra arquitectura, para realizar las llamadas a la API. Para poder hacer uso de la información obtenida de las respuestas de la API en nuestra aplicación, se han creado las clases necesarias en Java, mediante POJO’s, basándose en el formato de las respuestas del JSON. Gracias a la librería Gson, que se integra en Retrofit, nos permite serializar y deserializar los objetos JSON en clases Java y viceversa. VIEW% USER% CONTROLLER% MODEL% Figura 4.4 Diagrama de la arquitectura MVC de la aplicación Android
! 40 4.2.2.2 Vista La vista en Android se corresponde con archivos XML, que están asociados con Activities que las gestionan, pertenecientes al controlador. Las activities, son las encargadas de proporcionarles a la vista los datos que se mostrarán. 4.2.2.3 Controlador En Android, existe un archivo llamado AndroidManifest.xml, donde se definen las Activities, mencionadas anteriormente, dentro de una clase Application; esta clase es la encargada de gestionar toda la aplicación y controlar las activities. De este modo todo el tráfico entre las vistas y el modelo, pasarán a través de esta clase Application controladora. En las activities, se define la lógica de la aplicación, como hemos dicho antes, son las encargadas de proporcionarles a la vista los datos, además desde ellas, se llaman a otras clases que utilizamos para realizar las llamadas a la API, gestionar funcionalidades de las activities, etc. 4.3 Base de datos Debido a la gran cantidad de información y relaciones posibles entre las tablas del modelo, se ha optado por utilizar una base de datos relacional, concretamente en PostgreSQL, que encaja perfectamente con Django, la tecnología utilizada en el lado del servidor. En este proyecto necesitábamos relaciones entre un usuario y sus colecciones de películas, una película y sus ratings, las participaciones de los actores en esa película, etc. Además, como podemos apreciar en la Figura 4.5, cada tabla sensible de traducción, está relacionada con Lang, para poder realizar el soporte multilenguaje. Es por ello que se complica un poco el modelado y se ha utilizado una base de datos relacional para abordar estas necesidades. La tabla principal de nuestro modelo es Movie, que está relacionada con todas las demás, como por ejemplo Saga, Genre, Movie_lang, Rating… Podríamos destacar la relación de Movie con Celebrity con una relación muchos a muchos, creando así una tabla intermedia llamada Participation, encargada de proporcionar información sobre los premios de una celebrity en una película, su papel o su rol como actor, director, escritor en dicha película.
! 41 Figura 4.5 Diagrama E/R principal de la base de datos ! Figura 4.6 Diagrama E/R del usuario relacionado con el resto del modelo de la base de datos
! 48 . def retrieve(self,request,pk): user = User.objects.get(pk=pk) profile = user.profile lang = Lang.objects.get(pk = profile.lang.id) return Response( { 'user':{ 'id': user.id, 'username': user.username, 'first_name': user.first_name, 'last_name': user.last_name, 'email': user.email, 'profile':{ 'born': profile.born, 'avatar': profile.avatar.url, 'city': profile.city, 'gender': profile.gender, 'postalCode': profile.postalCode, 'lang': { 'code': lang.code }, }, }, 'status':status.HTTP_200_OK, } ) def update(self,request,pk=None): data = request.data http_code = "" errors = None user = get_object_or_404(User,pk=pk) serializer = UserSerializer(instance=user,data=data) if serializer.is_valid(): user = serializer.save() data = serializer.data http_code = status.HTTP_200_OK else: data = None http_code = status.HTTP_400_BAD_REQUEST errors = serializer.errors return Response(
! 49 { 'user': data, 'status': http_code, 'errors': errors } ) En el método retrieve, equivalente a un GET del usuario, realizamos una consulta para obtener un usuario por su id, y lo devolvemos en formato JSON. En el método update, equivalente a un PUT o PATCH, obtenemos también el usuario por su id, llamamos al serializer del usuario UserSerializer el cual nos validará los campos y, si son correctos, guardará esos datos y nos devolverá la respuesta del resultado en formato JSON. 5.1.2 Peticiones Explicaremos las llamadas a la API de nuestro proyecto que hemos necesitado para que las aplicaciones se puedan comunicar con nuestro servidor y, en concreto, con la base de datos. Primero detallaremos las peticiones relacionadas con un usuario, para posteriormente pasar a las relacionadas con una película. 5.1.2.1 User - AUTH - PostLogin: Iniciar sesión en el sistema. (POST) /users/login/ Datos del formulario para x-www-form-urlencoded Campo Tipo Descripción username Text Nombre de usuario o email registrado en el sistema password Text Contraseña asociada al usuario o email introducido Respuesta { "message":"Login successfully", "status":200, "user":{ "username":"movie",
! 50 "profile":{ "avatar":"/media/user/default/no-image.png", "lang":{ "code":"es" } }, "id":9, "email":"
[email protected]" }, "token":"8a0ac632536d7d1ac5db90f6f05338cef2778516" } Obtenemos los datos del usuario y su perfil, además del token, necesario para realizar el resto de peticiones que no son de tipo AUTH (Autorizadas). Error 4xx { "message": "User or password incorrect", "status": 404, "user": null, "token": null } Si no existe un usuario en el sistema con los campos introducidos nos devolverá un mensaje de error. - AUTH - PostSignUp: Registrar un usuario en el sistema. (POST) /users/ Datos del formulario para x-www-form-urlencoded Campo Tipo Descripción username Text Nombre de usuario email Text Email password Text Contraseña profile.lang.code Text Código de idioma. Actualmente los valores posibles son: es: Español en: Inglés
! 51 Respuesta { "status": 201, "user": { "id": 9, "username": "movie", "email": "[email protected]", "password": "pbkdf2_sha256$20000$wiNMenJqVebu$DUTwrahkp1n+teQZbnhGrcWI+e+8t oKN6hC20xz5QgQ=", "profile": { "lang": { "code": "es" }, "avatar": "/media/user/default/no-image.png" } }, "errors": null, "token": "8a0ac632536d7d1ac5db90f6f05338cef2778516" } Obtenemos los datos del usuario y su perfil, además del token, necesario para realizar el resto de peticiones que no son de tipo AUTH (Autorizadas). Error { "status": 400, "user": null, "errors": { "username": [ "Ya existe un usuario con ese nombre de usuario." ], "email": [ "Ya existe un usuario con ese email." ] }, "token": null } Si ya existe un usuario en el sistema con los campos introducidos nos devolverá un campo de error con los mensajes correspondientes. Nota: Para el resto de peticiones se utiliza un método de autenticación por token para dar seguridad a la API y que solo los usuarios registrados puedan realizar peticiones al resto de métodos. Se añadirá un campo de Authorization a la cabecera como el siguiente.
! 52 Authorization: Token {token} Campo Tipo Descripción token Text Token asociado a usuario del sistema - User - GetUserProfile: Obtener el perfil de un usuario. (GET) /users/{id}/ URL Campo Tipo Descripción id Number Id de un usuario registrado en el sistema Respuesta { "status": 200, "user": { "first_name": "", "last_name": "", "username": "movie", "profile": { "born": null, "gender": null, "avatar": "/media/user/default/no-image.png", "lang": { "code": "es" }, "postalCode": null, "city": null }, "id": 9, "email": "[email protected]" } } Obtenemos todos los datos del usuario y su perfil. - User - UpdateUserProfile: Actualizar el perfil de un usuario. (PUT) /users/{id}/ URL Campo Tipo Descripción id Number Id de un usuario registrado en el sistema
! 53 Datos del formulario para multipart/form-data Campo Tipo Descripción username Text Nombre de usuario profile.lang.code Text Código de idioma first_name (opcional) Text Nombre last_name (opcional) Text Apellidos email (opcional) Text Email profile.born (opcional) Date Fecha de nacimiento profile.city (opcional) Text Ciudad profile.postalCode (opcional) Text Código postal image (opcional) Image Imagen del perfil Respuesta Devolvería la misma respuesta que en la petición GetUserProfile - User - GetSwipeList: Obtener la lista de películas para el sistema de clasificación (swipe), es decir, películas que no están clasificadas en ninguna lista del usuario. (GET) /users/{id}/swipelist/ URL Campo Tipo Descripción id Number Id de un usuario registrado en el sistema Respuesta { "count": 10, "next": null, "previous": null, "results": [ { "movie_lang_id": 54625, "image": "EXTERNAL#pelis/HP9YTUHXX6", "title": "Cartas desde la locura", "collection": null,
! 54 "id": 28689, "average": 0 }, { "movie_lang_id": 48254, "image": "EXTERNAL#pelis/VHRDRXK7X4", "title": "Ring of fire II: Sangre y acero", "collection": null, "id": 25274, "average": 0 }, { "movie_lang_id": 7581, "image": "/14/f3/14f30376096122243ff4fe3de8db0cb6.jpg", "title": "Uno de los nuestros", "collection": null, "id": 3813, "average": 0 }, . . . ] } Obtenemos la información necesaria de diez películas para mostrar en el sistema de clasificación (swipe). Nos devuelve su título en el idioma del perfil del usuario, la imagen (cover) de la película, la puntuación de nuestro sistema, su id asociada a nuestra base de datos y su tipo de colección que, en este caso, al ser para el sistema de clasificación y no estar aún clasificada, nos devolverá null. - User - GetCollectionList: Obtener la lista de películas de un tipo concreto. (GET) /users/{id}/collection/?name={name}&page={page} URL Campo Tipo Descripción id Number Id de un usuario registrado en el sistema name Text Nombre de la colección. Los tipos de colección son: seen, favourite, watchlist, blacklist page Number Número de página
! 55 Respuesta { "count": 174, "next": "/users/9/collection/?name=favourite&page=2", "previous": null, "results": [ { "movie_lang_id": 2919, "image": "/a4/9d/a49dd25d5df38f2e97c4bfd7d4875684.jpg", "title": "El Señor de los Anillos: La Comunidad del Anillo", "collection": { "typeMovie": "favourite", "id": 1 }, "id": 1468, "average": 0 }, { "movie_lang_id": 6140, "image": "/1d/8f/1d8f6066a9ad1cb91211bcbfca5915f2.jpg", "title": "Piratas del Caribe: La maldición de la Perla Negra", "collection": { "typeMovie": "favourite", "id": 9 }, "id": 3088, "average": 0 }, { "movie_lang_id": 6136, "image": "/1c/3e/1c3e15050ceaf0e4edb15cdfb3c35867.jpg", "title": "Piratas del Caribe: El cofre del hombre muerto", "collection": { "typeMovie": "favourite", "id": 13 }, "id": 3086, "average": 0 }, . . . ] }
! 56 Obtenemos un resultado paginado con la información necesaria, para poder mostrar una previsualización de las películas. Nos devuelve su título en el idioma del perfil del usuario, la imagen (cover) de la película, la puntuación de nuestro sistema, su id asociada a nuestra base de datos y su tipo de colección. - User - PostMovieCollection: Clasificar una película en la colección de un usuario determinada. (POST) /collection/ Datos del formulario para x-www-form-urlencoded Campo Tipo Descripción user Number Id de un usuario del sistema movie Number Id de una película del sistema typeMovie Text Nombre de la colección. Los tipos de colección son: seen, favourite, watchlist, blacklist Respuesta { "id": 854, "user": 9, "movie": 3088, "typeMovie": "favourite", "pub_date": "2016-11-06T07:44:48.756881Z" } Obtenemos el resultado de haber clasificado una película en una colección determinada; la id de la tabla Collection, la id del usuario, la id de la película, el tipo de la colección y la fecha de clasificación. - User - PatchMovieCollection: Actualizar la colección de una película de un usuario. (PATCH) /collection/{id}/ URL Campo Tipo Descripción id Number Id de la tabla Collection donde está clasificada la película que queremos actualizar
! 57 Datos del formulario para x-www-form-urlencoded Campo Tipo Descripción typeMovie Text Nombre de la colección. Los tipos de colección son: seen, favourite, watchlist, blacklist Respuesta { "id": 854, "user": 9, "movie": 3088, "typeMovie": "seen", "pub_date": "2016-11-06T07:48:47.107361Z" } Obtenemos el resultado de haber actualizado el tipo de colección de una película; la id de la tabla Collection, la id del usuario, la id de la película, el tipo de la colección y la fecha de clasificación. 5.1.2.2 Movie - Movie - GetMovieDetail: Obtener la información detallada de una película. (GET) /movie/{id}/?movie_lang_id={movie_lang_id}&user_id={user_id} URL Campo Tipo Descripción id Number Id de la película a obtener movie_lang_id Number Id de la tabla Movie_lang que identifica a la película en un idioma. user_id Number Id del usuario que obtiene la película. Respuesta { "title": "Piratas del Caribe: La maldición de la Perla Negra", "participations": [ {
! 64 Otra parte sería la recogida de información que no nos ofrecen APIs externas, como son las puntuaciones de películas de algunas plataformas. Para ello hemos utilizado scrappers o arañas web. Los scrappers son herramientas software que rastrean y extraen cierta información de una página concreta. Haciendo uso de la librería BeautifulSoup, podemos obtener el código HTML de una página concreta, y mediante métodos de la librería como find() buscar la etiqueta HTML donde se encuentra la información que necesitemos. # get_audience_rating(soup), get audience rating of Rotten Tomatoes, # Params # - soup, page from BeautifulSoup def get_audience_rating(soup): # AUDIENCE SCORE - RottenTomatoesAudience # Div with RottenTomatoesAudience lista = soup.find_all("div", {"class":"audiencepanel"})[0] # Rating rating = lista.find_all("div", {"class":"audience-score meter"})[0].find("span","superPageFontColor").get_text() rating = int(rating.replace("%", "")) # Count count = lista.find("div","audienceinfo").find_all("div")[1].get_text() count = int(count.replace("User Ratings:", "").strip().replace(",","")) return rating, count Figura 5.2 Estructura de carpetas de los scripts utilizados para la plataforma Tviso Figura 5.3 Estructura de carpetas de los scripts utilizados para la plataforma Trakt.tv
! 65 Como vemos en el fragmento de código anterior, buscamos la puntuación y el número de votos de una película en la página RottenTomatoes; en concreto la puntuación de la audiencia. Dependiendo de la estructura del código HTML, será más o menos complejo obtener una información concreta. En este caso se acceden a varias clases y etiquetas. Por un lado, extraemos la puntuación de la clase audience-score-meter y por otro, el número de votos de la clase audience-info, que finalmente se devuelven como resultado de la función get_audience_rating(). 5.3 Creación e instalación del proyecto La creación e instalación del proyecto se podría separar en el proyecto del servidor y el de la aplicación o cliente Android. Mostraremos cómo sería la creación de un proyecto desde cero, y cómo hacerlo a partir de nuestro repositorio privado en GitHub o teniendo ya la carpeta del mismo. 5.3.1 Aplicación Mooviest (Servidor Django) En esta sección se explicará el proceso a realizar para crear e instalar el proyecto del servidor. Para ambos partimos de unos requisitos previos que veremos a continuación. Los comandos expuestos son para la instalación en un ordenador con sistema operativo MacOS o Linux. Más concretamente, para el desarrollo de este proyecto se ha utilizado un entorno MacOS. En primer lugar, debemos tener instalado Python y su gestor de paquetes de pip. Una vez instalado, procederemos a preparar nuestro entorno virtual (virtualenv), que nos permite aislar la configuración de los paquetes instalados para cada proyecto, por lo que podemos trabajar, por ejemplo, con diferentes versiones de Django o Python en proyectos diferentes y en el mismo ordenador. Para la instalación de virtualenv ejecutaremos el siguiente comando: pip3 install virtualenv Después crearemos nuestro directorio, donde instalaremos el entorno virtualenv. mkdir mooviest cd mooviest python3 –m venv venv # Creación entorno virtual source venv/bin/actívate # Activar entorno virtual Como vemos en el fragmento anterior creamos y activamos el entorno virtual en el directorio del proyecto y, a partir de ahora, todo lo que instalemos mediante el comando pip, estará instalado de forma aislada en él.
! 66 5.3.1.1 Creación del proyecto Una vez instalados todos los requisitos, podemos centrarnos en crear e instalar nuestro proyecto Django y añadir todos los módulos y paquetes necesarios. En primer lugar, instalaremos la versión de Django y crearemos nuestro proyecto. pip install django==1.8 django-admin startproject mysite El último comando nos crea la estructura de un proyecto Django con sus archivos y directorios por defecto, que podemos ampliar creando nuestras propias apps como haremos a continuación para comenzar el proyecto. python3 manage.py startapp movie ! Figura 5.4 Estructura de carpetas de un proyecto Django En Django se desarrolla creando aplicaciones en su propio directorio como podemos ver en la Figura 5.4, donde acabamos de crear la aplicación movie. Además, tenemos un subdirectorio del raíz, mooviest, igual que el proyecto, que es donde se coordinan todas las apps mediante el archivo urls.py y donde se configuran sus
! 67 ajustes en settings.py; es por ello que al crear una app debemos añadirla a este archivo. 5.3.1.2 Instalación y configuración del proyecto En este apartado veremos cómo instalar y configurar nuestro proyecto, desde nuestro repositorio privado en GitHub. git clone https://github.com/JoseAntpr/mooviest.git En este momento, ya tendremos nuestro proyecto en el directorio mooviest y nuestro entorno virtual como explicamos anteriormente. Tan solo debemos instalar las dependencias que existen en el archivo requirements.txt, mediante el siguiente comando. pip3 install –r requirements.txt 5.3.1.3 Configuración de la base de datos Para el correcto funcionamiento de nuestro proyecto, debemos tener instalada y configurada nuestra base de datos en PostgreSQL. Para ello comenzamos instalando postgresql con el siguiente comando: brew install postgresql Una vez hecho, procedemos a crear nuestra base de datos y un usuario: createdb mooviest createuser –P psql >> GRANT ALL PRIVILEGES ON DATABASE mooviest TO root; Como comentamos anteriormente, la configuración del proyecto Django está en el archivo settings.py, entonces, una vez creamos la base de datos, debemos añadir su configuración a este archivo. DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'mooviest', 'USER': 'root', 'PASSWORD': 'root', 'HOST': 'localhost', 'PORT': '5432', } }
! 68 Por último, ejecutamos las migraciones de la base de datos y las ejecutamos con los siguientes comandos: python3 manage.py makemigrations #Creación de las migraciones python3 manage.py migrate #Ejecución de las migraciones Cada vez que queramos realizar una modificación en el modelo de nuestra base de datos o crearlo por primera vez, debemos ejecutar las migraciones del proyecto. Las migraciones, son el código necesario que Django utiliza para realizar el cambio del modelo de nuestra base de datos, como, por ejemplo, la modificación del tipo de un atributo, la creación de una nueva tabla y sus atributos, etc. Se crean, si Django detecta que hay un cambio entre su modelo interno y el de nuestra base de datos. Una vez han sido generadas con el primer comando del código anterior, se ejecutan con el segundo comando, en este momento, Django se encarga de ejecutar las sentencias necesarias en PostgreSQL para realizar la modificación en la base de datos. Finalmente, para iniciar nuestro proyecto, ejecutaremos el siguiente comando: python3 manage.py runserver 0.0.0.0:8000 Al introducir la IP 0.0.0.0, podremos recibir peticiones en nuestra máquina desde cualquier IP de la red local. 5.3.2 Aplicación Mooviest (Android) En este apartado se explicará el proceso a realizar para instalar y configurar nuestro proyecto Android. Los requisitos previos son los siguientes: - IDE de desarrollo Android Studio. Tener instalado el IDE de desarrollo Android Studio, que podemos descargarlo desde su página oficial (https://developer.android.com/studio/index.html). - Android SDK. Tener instalado el conjunto de herramientas de desarrollo de aplicaciones Android. Es recomendable descargar el paquete de Android Studio donde viene integrado el Android SDK. - Min SDK 19. Instalar el SDK 19 que se ha establecido como la mínima versión requerida para poder ejecutar el proyecto en un dispositivo Android. En este caso el SDK 19 corresponde a la versión 4.4 de Android también llamada Kit Kat.
! 69 ! Figura 5.5 Porcentaje de dispositivos activos en Google Play Store que utilizan una versión de Android determinada Se ha escogido esta versión como la mínima requerida porque, como podemos apreciar en la Figura 5.5, el 73,9% de los dispositivos actuales podrían ejecutarla. Con lo cual abarca a un gran número de usuarios. - JDK (Java Development Kit). Tener instalado el conjunto de herramientas de desarrollo para la creación de programas en Java, accesible desde la página de Oracle (https://www.oracle.com/index.html). 5.3.2.1 Creación del proyecto Una vez cumplidos los requisitos anteriormente citados, procederemos a crear nuestro proyecto Android. En primer lugar, abrimos nuestro IDE de desarrollo Android Studio y seleccionamos la opción de empezar un nuevo proyecto Android Studio. Seleccionamos el nombre de la aplicación, en este caso Mooviest y el SDK mínimo para la ejecución del proyecto, elegimos API 19 Android 4.4 (KitKat). Creamos la actividad principal MainActivity y pulsamos en finalizar. En este momento tendremos la estructura del proyecto y los archivos y directorios por defecto que nos crea Android Studio, como podemos apreciar en la Figura 5.6.
! 70 ! Figura 5.6 Estructura de carpetas de un proyecto Android A continuación, explicaremos cómo funciona una aplicación Android y la estructura de carpetas de un proyecto. Una aplicación Android está basada en una serie de actividades. Las actividades (activities), representan el componente principal de la interfaz gráfica de una aplicación Android. Se puede pensar en una actividad, como el elemento análogo a una ventana o pantalla en cualquier otro lenguaje visual. Al fin y al cabo, una actividad se trata de una clase Java, que contiene todo el código que se quiere implementar en la aplicación. Una activity, siempre está relacionada con un fichero XML, que contiene el layout, vista o parte gráfica. La configuración del proyecto Android se encuentra en el archivo AndroidManifest.xml, que contiene las versiones de la aplicación, la definición de las activities, y los permisos de la aplicación para acceder al dispositivo como, por ejemplo, el acceso a Internet, a la lectura y escritura de la memoria interna del dispositivo, el acceso a la cámara… Una ventaja de utilizar Android Studio frente a otros IDE es la de poder hacer uso de gradle, que nos permite manejar fácilmente el uso de dependencias del proyecto o tener varios entornos para un mismo proyecto entre otros. En un proyecto con gradle nos encontramos dos archivos importantes que son settings.gradle y
! 71 build.gradle. El primero organiza los módulos de nuestra aplicación, tanto el proyecto principal, conocido como app, como las librerías que hubiésemos creado. El segundo describe el proceso del build del módulo: las versiones del SDK que se deben usar, las dependencias, la forma de generar nuestro apk, las especificaciones del lenguaje… Cada módulo de un proyecto cuenta con su propio archivo build.gradle. Por último, veremos cómo está organizado y para qué sirven las carpetas más importantes de un proyecto Android: - Carpeta /src/: esta carpeta contendrá todo el código fuente de la aplicación, todos los recursos XML, activities, clases auxiliares… - Carpeta /java/: contiene todas las activities y clases auxiliares de nuestro proyecto, organizado en subcarpetas dependiendo de la funcionalidad. - Carpeta /res/: contiene todos los ficheros de recursos necesarios para el proyecto: imágenes, traducciones, interfaces gráficas XML… - /res/drawable/: contiene las imágenes o iconos de nuestra aplicación en varias calidades y resoluciones. - /res/layout/: contiene los ficheros de definición XML de las diferentes pantallas de la interfaz gráfica. - /res/values/strings: contiene todas las cadenas de texto y traducciones de la aplicación. 5.3.2.2 Instalación y configuración del proyecto ! En este apartado, veremos cómo instalar el proyecto desde nuestro repositorio privado en GitHub. git clone https://github.com/JoseAntpr/mooviest_android.git En este momento tendremos la carpeta del proyecto, que solo tendremos que abrir con nuestro IDE de desarrollo Android Studio y poder ejecutarlo en el emulador que nos proporciona el IDE, o en un dispositivo Android con el modo “Depuración USB” habilitado y conectado mediante un cable USB.
! 72 5.3.2.3 GitHub ! Figura 5.7 Proyecto mooviest de la aplicación Android en la plataforma GitHub A lo largo de la realización del proyecto se ha utilizado junto con Git como VCS (sistema de control de versiones) y, de esta forma, tener así controladas todas las features (pequeñas funcionalidades del desarrollo del proyecto) y versiones del proyecto. 5.4 Desarrollo de la aplicación Mooviest (Android) 5.4.1 Introducción, inicio de sesión y registro ! Figura 5.8 Pantalla inicial (Splash screen) Figura 5.9 Pantalla para iniciar sesión en el sistema Figura 5.10 Pantalla para registrarse en el sistema
! 73 Nada más abrimos la aplicación veríamos la pantalla de introducción o splash screen correspondiente a la Figura 5.8. Esta pantalla corresponde a la activity SplashScreenActivity.java de nuestro proyecto Android.! En esta activity se realizan las primeras decisiones de la aplicación. En primer lugar, se obtienen o inicializan las variables que se guardarán en la memoria del dispositivo del usuario, dependiendo de si es el primer inicio de la app o no. SharedPreferences app_prefs = getSharedPreferences("APP_PREFS", Context.MODE_PRIVATE); boolean tutorial = app_prefs.getBoolean("tutorial", false); boolean logged = app_prefs.getBoolean("logged", false); SharedPreferences user_prefs = getSharedPreferences("USER_PREFS", Context.MODE_PRIVATE); user_prefs.getBoolean("default_avatar", true); user_prefs.getString("avatar_image", ""); user_prefs.getInt("id", 0); user_prefs.getString("username", ""); user_prefs.getString("email", ""); user_prefs.getString("token", ""); Como vemos en este fragmento de código, las identificamos como APP_PREFS o preferencias de la aplicación, para saber si un usuario ya ha completado el tutorial de la aplicación o ya ha iniciado sesión en ella. USER_PREFS o preferencias de usuario, se emplea para guardar en la memoria del dispositivo la id del usuario que ha iniciado sesión, su nombre de usuario, email, token, etc. Este último necesario para realizar las peticiones a la API de manera autenticada una vez el usuario está identificado en la app. Una vez obtenidas o inicializadas estas variables, comprobamos si el usuario ya ha iniciado sesión en la app. En caso afirmativo, comprobamos si el dispositivo tiene conexión a internet y se redirige automáticamente a la pantalla home o principal de la app Figura 5.18. En caso de que no haya iniciado sesión en la app se le redirigirá a la pantalla para iniciar sesión Figura 5.9. En la pantalla para iniciar sesión, podremos introducir el email o nombre de un usuario registrado en el sistema, y su contraseña asociada, para así, iniciar sesión en el sistema. Además, disponemos de un enlace a la pantalla para que un usuario pueda registrarse en el sistema, correspondiente a la Figura 5.10. Contendrá los campos obligatorios como son nombre de usuario, email, contraseña y otro campo para volver a introducir la contraseña, que nos servirá para validarla con la anterior, antes de enviar al sistema el registro. De esta forma, el usuario tendrá menos posibilidad de
! 80 De este modo, hemos definido un array inicial de diez películas a modo de buffer, que sería lo que nos devuelve la petición de la API para obtener las películas del swipe. En el adapter no introducimos el array completo de películas, sino que vamos introduciendo de dos en dos, es decir, en la vista habría siempre dos películas, la que se ve y una detrás de ésta. Cuando clasificamos la película que vemos en el swipe, esta desaparece de la vista dando paso a la siguiente, en este momento se introduce en el adapter la siguiente película del buffer, y de este modo siempre tendríamos dos películas en el adapter y en concreto en la vista. Además de añadir una nueva, comprobamos si el buffer de películas tiene menos de seis. En este caso, añadiríamos al array diez películas más, llamando a la petición de la API. En este momento tendríamos las cinco películas que había, más otras diez nuevas que hemos añadido y, de esta forma, continuamente cargaríamos películas en esta vista. adapter = new SwipeDeckAdapter(movies_swipe, getContext(), this); cardStack.setAdapter(adapter); cardstack.setEventCallback(new SwipeDeck.SwipeEventCallback() { @Override public void cardSwipedLeft(int position) { movieCollectionTask("watchlist", adapter.getItem(0)); checkMoviesSwipe(); removeAndAddMovieToAdapter(0, 1); } @Override public void cardSwipedRight(int position) { movieCollectionTask("seen", adapter.getItem(0)); checkMoviesSwipe(); removeAndAddMovieToAdapter(0, 1); } @Override public void cardSwipedUp(int position) { movieCollectionTask("favourite", adapter.getItem(0)); checkMoviesSwipe(); removeAndAddMovieToAdapter(0, 1); } @Override public void cardSwipedDown(int position) { movieCollectionTask("blacklist", adapter.getItem(0)); checkMoviesSwipe(); removeAndAddMovieToAdapter(0, 1); } @Override public void cardRemove(int position) {
! 81 checkMoviesSwipe(); removeAndAddMovieToAdapter(0, 1); } }); En este fragmento vemos cómo sería el proceso para clasificar una película: 1. Realizamos la petición a la API que nos añade la película a la colección seleccionada, con el método movieCollectionTask(). 2. Comprobamos si el buffer de películas para el swipe tiene menos de seis para añadir más a éste. 3. Eliminamos del adapter la película que acabamos de clasificar y añadimos una nueva detrás de la última. 5.4.7 Listas del usuario En esta vista Figura 5.19 y Figura 5.20, mostramos las cuatro listas posibles en las que el usuario puede clasificar una película. A modo de visión general, muestra cuáles son las últimas películas que el usuario ha clasificado en estas listas. Además, el usuario tiene la opción de pulsar sobre una y ser redirigido a la vista de detalle, o ver una lista completa pulsando sobre el botón ‘MÁS’. Para su desarrollo hemos utilizado el mismo sistema que para la vista del swipe, es decir, cuatro adapters que contienen un array de diez películas cada uno. En esta vista el adapter lo hemos configurado para que permita realizar un scroll horizontal, y Figura!5.19!Listas!clasificadas!por!el!usuario Figura!5.20!Listas!clasificadas!por!el!usuario
! 82 así ver las demás portadas sin que ocupe demasiado espacio en la pantalla verticalmente. 5.4.8 Lista completa ! Figura 5.21 Lista completa de películas favoritas de un usuario Como hemos dicho anteriormente, el usuario tiene la opción de ver una lista completa de entre sus cuatro posibles, como podemos ver en la Figura 5.21, la lista de películas favoritas. En ella podemos pulsar sobre una y ser redirigidos a su vista de detalle. Vamos mostrando de diez en diez y tres películas por fila y, si el usuario quiere seguir viendo más, cuando vaya haciendo scroll hacia abajo, se cargarán otras diez por la cola, siempre que haya más películas en esta lista. Se realiza llamando a la petición de la API para obtener una colección concreta de forma paginada, es decir por medio de páginas. Para su desarrollo hemos utilizado un adapter, como los mencionados anteriormente, en concreto un GridView, que nos permite especificar el número de ítems o elementos que aparecerán por cada fila.
! 83 5.4.9 Búsqueda de películas En esta vista correspondiente a la Figura 5.22, tenemos la opción de buscar una película por su título original o en el idioma que tenga el usuario. Al igual que en las listas de un usuario se cargan máximo diez películas; si hubiera más resultados y se hace scroll hacia abajo, se volvería a realizar una petición a la API por la siguiente página. Una vez tenemos el listado también podemos seleccionar una y ser redirigidos a su vista de detalle. Para su desarrollo se ha utilizado el mismo sistema que para una lista completa de películas del usuario. Figura 5.22 Página para la búsqueda de películas Figura 5.23 Resultado de una búsqueda
! 84 5.4.10 Detalle de una película Esta vista corresponde a la activity MovieDetailActivity.java, y en ella mostramos la información detallada de una película como su carátula, sinopsis, título, duración, géneros, reparto, etc. Además, mostramos las puntuaciones de varias plataformas como son IMDb y Tviso y la opción de clasificar la película en una lista o cambiarla si ya la teníamos clasificada. Todo ello lo podemos ver en la Figura 5.24, la Figura 5.25 y la Figura 5.26. Esto último lo hacemos mediante un botón flotante en Android FloatingButton. Como hemos comentado aparecería un icono de ‘+’, si la película aún no está clasificada, o el icono correspondiente a la lista en la lo está. Al pulsarlo nos aparecería un menú con las posibles opciones y, al pulsar en una que no sea en la que está actualmente, cambiaría de lista. Sobre el desarrollo al clasificar una película llamaríamos a las funciones de la API para añadirla a una colección o actualizar la que tenía, PostMovieCollection o PatchMovieCollection respectivamente. La vista si divide en tabs como en la pantalla de Home (punto 5.4.2) y se ha desarrollado de la misma forma con ViewPager. El reparto se ha realizado de la misma forma que una lista de películas, es decir, mediante un adapter, pero esta vez mostrando la foto del personaje, si nombre real y su nombre de reparto. De la misma forma, las puntuaciones de las plataformas se han realizado como las listas de películas del Home con scroll horizontal. Figura 5.24 Vista de detalle de una película Figura 5.25 Botones de clasificación en la vista de detalle Figura 5.26 Reparto de la película
! 85 5.4.11 Cliente REST En este apartado se va a explicar cómo se ha creado el cliente REST, encargado de realizar las peticiones a la API. Hemos utilizado una librería externa llamada Retrofit, que nos permite realizar este cometido; en concreto, Retrofit, adapta una interfaz Java, definiendo los endpoints mediante métodos y uso de anotaciones, para las llamadas HTTP a la API. Un ejemplo de un método o endpoint sería el siguiente, en el que se realiza una petición GET para obtener el perfil de un usuario mediante su ID. public interface MooviestApiInterface { @GET("users/{id}/") Call<UserProfileResponse> getUserProfile(@Path("id") int id); . . . } Para la utilización de esta interfaz, debemos crear un objeto Retrofit Builder, como vemos en el siguiente fragmento de código: String baseAPIUrl = "http://localhost:8000/api/"; public static final Retrofit retrofit = new Retrofit.Builder() .baseUrl(baseAPIUrl) .addConverterFactory(GsonConverterFactory.create()) .client(okHttpClient) .build(); A este objeto le pasamos la URL base de la API, además del tipo de conversión para obtener las respuestas a las peticiones, que en este caso usamos Gson, una librería para convertir objetos Java en JSON y viceversa. Además, le pasamos un cliente HTTP, que creamos con la librería OkHttp, pasándole las cabeceras y la autorización por token en el caso de que sea necesario para la llamada a la API.
! 86 5.4.12 Llamadas a la API Para la realización de una llamada a la API haremos uso del cliente creado en el punto anterior. Veremos un ejemplo una llamada a la API, para el mismo caso de obtener el perfil de un usuario. MooviestApiInterface apiInterface = MooviestApiInterface.retrofit.create(MooviestApiInterface.class); Call<UserProfileResponse> call = apiInterface.getUserProfile(userId); UserProfileResponse result = call.execute().body(); Creamos la implementación de la interfaz API, instanciamos un objeto Call con el método concreto de la interfaz y lo ejecutamos para que nos devuelva el resultado de la petición. Todo este proceso lo realizamos en un AsyncTask, es decir, una hebra distinta a la del hilo principal, para que no se bloquee nuestra actividad principal donde se encuentra el usuario.
! 87 Capítulo 6. Conclusiones y trabajo futuro 6.1 Conclusiones En este trabajo de fin de grado se han descrito todas las fases para poder desarrollar una aplicación multiplataforma funcional desde cero, con tecnologías de servidor, como Django, y móvil, Android. Hoy en día, podemos ver el gran uso de los dispositivos móviles como herramienta de trabajo diario, y lo fundamental para cualquier usuario, es poder tener toda la información en la palma de su mano, esté donde esté. En esta app, permitimos al usuario acceder y mantener una biblioteca y lista de tus películas clasificadas, así como acceder a toda la información asociada a ella. Se ha explicado en detalle cómo se iba a solucionar este problema, al tener toda la información distribuida entre multitud de plataformas y muy fragmentada en Internet. Se han detallado todas las fases de análisis y recopilación de información y de la estructura de la aplicación. Una vez definido esto, pudimos comenzar todo el desarrollo necesario para llevarlo a cabo. Haciendo uso de tecnologías de servidor como Django y Django REST Framework para crear la API, pudimos construir, junto con una base datos en PostgreSQL, una arquitectura de servidor bastante completa, para dar respuesta a nuestros clientes en Android e iOS. Para conseguir rendimientos superiores y mayor escalabilidad en un futuro, pensamos que la mejor opción era desarrollar nuestras aplicaciones en sus plataformas nativas, en mi caso en Android. De esta forma no depender de otras tecnologías web emergentes, como el caso de Ionic, que, aunque te faciliten el desarrollo inicial, al poder desarrollar en varias plataformas, puede llegar a ser tedioso adaptar tus apps a nuevas actualizaciones de los sistemas y plataformas. La realización de este proyecto, ha supuesto un aumento de mis conocimientos en muchas áreas del desarrollo software, ya que hemos desarrollado aplicaciones de servidor, una API, el desarrollo de scripts y scrappers para la recogida de información de APIs y páginas webs, una base de datos y una aplicación móvil nativa. Sobre todo, hemos aprendido a afrontar un problema real y su solución, usando una metodología ágil, tecnologías colaborativas para trabajar en grupo, gestionar un software mediante control de versiones y muchos otros aspectos del desarrollo software. En resumen, he disfrutado al formar parte de este proyecto en equipo y al desarrollar una aplicación totalmente funcional y usable desde su primera versión. Consideramos también que el resultado es una aplicación útil para el público aficionado al cine, que necesitan clasificar su contenido, para posteriormente tenerlo accesible rápidamente.
! 88 6.2 Trabajo futuro - Sistema de recomendaciones: Para atraer a un mayor público en el uso de nuestra aplicación, se había pensado en el desarrollo de un sistema de recomendaciones, basado en técnicas de Aprendizaje Automático (Machine Learning). Es decir, nuestra app aprenderá del usuario y del contenido que ya hemos clasificado, para así recomendar nuevas películas y contenidos. De esta forma, cuanto más se use la aplicación y se clasifiquen las películas, nuestro sistema será más fiable en sus recomendaciones. - Iniciar sesión con redes sociales: Muchos usuarios ven bastante tedioso registrarte en una web o aplicación por medio de un formulario, y muchos de ellos, no llegan a utilizarlas por este inconveniente. Las redes sociales nos facilitan esta tarea, obteniendo nuestra información de ellas y así, no tener que volver a introducirla en nuevas aplicaciones. - Recuperar contraseña: Una buena manera de hacer que los usuarios confíen en nuestra plataforma, es poder implementar esta función para poder recuperar su contraseña, si ésta se ha olvidado. - Confirmar cuenta por email: Desarrollaremos este paso para validar que los usuarios que se registren, sean usuarios reales de nuestra aplicación y evitar que bots o máquinas creen usuarios falsos. - Consultar las películas en las que ha participado una celebrity: Además de nuestro sistema de recomendaciones, sería de gran utilidad, consultar las películas en las que ha participado tu actor o director favorito. De esta forma tendríamos una funcionalidad extra, para que el usuario vea su contenido favorito. - Buscar y seguir usuarios y celebrities: Además de buscar películas y clasificarlas, añadiremos la función de buscar usuarios y celebrities, y seguirlos. Esta información aparecerá en nuestro perfil con los apartados: siguiendo, seguidores y celebrities. - Consultar el perfil y las listas de usuarios: También añadiremos la funcionalidad de consultar las listas de películas clasificadas de un usuario.
! 89 Bibliografía [1] Alliance, G. a. (2012-2016). Android Developers. Obtenido de Developer Android: https://developer.android.com/index.html [2] Bahit, E. (2012). Curso: Python para principiantes. Buenos Aires, Argentina: Safe Creative. [3] Christie, T. ( de de 2011-2016). Django REST Framework. Obtenido de Django REST Framework: http://www.django-rest-framework.org/ [4] Consumerlab, T. M. (2014). Ericsson. Obtenido de Ericsson: https://www.ericsson.com/res/docs/2014/consumerlab/tv-media-2014ericsson-consumerlab.pdf [5] Foundation, D. S. (2005-2016). The Web framework for perfectionists with deadlines | Django. Obtenido de Django Project: https://www.djangoproject.com/ [6] GitHub, I. (2008-2016). GitHub. Obtenido de GitHub: https://github.com/ [7] Google. (2014-2016). Introduction - Material design - Material design guidelines. Obtenido de Material Google: https://material.google.com/ [8] Lequerica, J. R. (2016). Desarrollo De Aplicaciones Para Android - Edición 2017 (Manuales Imprescindibles). ANAYA. [9] PosgreSQL, R. M. (2009-2016). postgresql.org.es. Obtenido de PostgreSQL: http://www.postgresql.org.es/ [10] Richardson, C. a. ( de de 2004-2016). Beautiful Soup: We called him Tortoise because he taught us. Obtenido de Beautiful Soup: https://www.crummy.com/software/BeautifulSoup/ [11] Square. (2013-2016). Picasso. Obtenido de Picasso: http://square.github.io/picasso/ [12] Square. (2013-2016). Retrofit. Obtenido de Retrofit: https://square.github.io/retrofit/ [13] W3C. (1996-2016). World Wide Web Consortium (W3C). Obtenido de W3: https://www.w3.org/ !