Full text
Aplicaci´on web para la gesti´on y b´usqueda de eventos. Universidad complutense de Madrid Trabajo de Fin de Grado del Grado en Ingenier ´ ıa Inform´ atica Facultad de Inform´ atica Autor: Rafael G´omez Bermejo Tutor: Manuel Montenegro Montes 8 de junio de 2018
2
´ Indice general Agradecimientos 7 Resumen 9 Palabras clave 11 Abstract 13 Keywords 15 1. Introducci´on 17 1.1. Motivaci´on..................................... 17 1.2. Objetivos ..................................... 18 1.3. Plandetrabajo.................................. 18 1.3.1. Estudio de las herramientas y tecnolog´ıas . . . . . . . . . . . . . . . . 18 1.3.2. Proceso de desarrollo para cada funcionalidad . . . . . . . . . . . . . 19 1.3.3. Implementaci´on de la creaci´on de cuentas y su gesti´on . . . . . . . . . 19 1.3.4. Implementaci´on de la gesti´on de los eventos . . . . . . . . . . . . . . 19 1.3.5. Implementaci´on de un buscador y herramientas de filtrado . . . . . . 20 1.3.6. Integraci´on de un sistema de recomendaciones . . . . . . . . . . . . . 20 3
2. Introduction 21 2.1. Motivation..................................... 21 2.2. Objectives..................................... 22 2.3. Workplan ..................................... 22 2.3.1. Study of tools and technologies . . . . . . . . . . . . . . . . . . . . . 22 2.3.2. Development process for each functionality . . . . . . . . . . . . . . . 23 2.3.3. Implementation of account creation and management . . . . . . . . . 23 2.3.4. Implementation of event management . . . . . . . . . . . . . . . . . . 23 2.3.5. Implementation of a search engine and filtering tools . . . . . . . . . 24 2.3.6. Integration of a system of recommendations . . . . . . . . . . . . . . 24 3. Selecci´on de herramientas y tecnolog´ıas 25 3.1. Herramientas y tecnolog´ıas usadas . . . . . . . . . . . . . . . . . . . . . . . . 25 3.1.1. Python................................... 25 3.1.2. Django................................... 26 3.1.3. DjangoORM ............................... 28 3.1.4. Bootstrap ................................. 28 3.1.5. PostgreSQL................................ 29 3.1.6. GoogleMapsAPI............................. 29 3.1.7. SublimeText ............................... 30 3.1.8. Git/Github ................................ 30 3.1.9. L A T EX ................................... 30 3.2. Herramientas y tecnolog´ıas descartadas . . . . . . . . . . . . . . . . . . . . . 31 3.2.1. AngularJS................................. 31 4
3.2.2. Spring ................................... 31 3.2.3. MongoDB................................. 32 4. Arquitectura del sistema 33 4.1. MVCdeDjango.................................. 33 4.2. DjangoORM ................................... 36 4.3. Gesti´on de los usuarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 4.4. Gesti´ondeloseventos .............................. 39 4.5. Estructura de las plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 5. Los usuarios y su relaci´on con los eventos 45 5.1. Interacciones directas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 5.1.1. Creaci´on de eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 5.1.2. Modificaci´on de eventos . . . . . . . . . . . . . . . . . . . . . . . . . 48 5.2. Intereses y asistencia a eventos . . . . . . . . . . . . . . . . . . . . . . . . . . 50 5.3. Comentarios.................................... 52 6. B´usqueda avanzada de eventos 57 6.1. Realizaci´on de la consulta . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 6.2. Tratamiento de la lista en la plantilla . . . . . . . . . . . . . . . . . . . . . . 61 7. Recomendaci´on de eventos 65 7.1. Estructurab´asica................................. 65 7.2. Algoritmoutilizado................................ 66 8. Conclusiones y trabajo futuro 69 5
8.1. Objetivosalcanzados............................... 69 8.2. Trabajofuturo .................................. 70 9. Conclusions and future work 73 9.1. Achievedgoals .................................. 73 9.2. Futurework.................................... 74 6
Agradecimientos Agradecer a Manuel Montenegro su dedicaci´on y disposici´on en todo momento a ayudarme y aconsejarme a lo largo de todo el proyecto, haci´endolo posible. Especial menci´on a Ana Mar´ıa Bermejo Ares por el dise˜no del logo empleado para la web. 7
8
Resumen El objetivo principal de este trabajo de fin de grado es desarrollar un portal web que permita una b´usqueda de eventos y actividades de ocio teniendo en cuenta determinados criterios tales como la proximidad geogr´afica y el coste del mismo, entre otros. Este proyecto nace para cubrir una necesidad de aquellos usuarios que deseen, en un determinado momento, asistir a alguna actividad de ocio (concierto, exposici´on, etc.) que se realice en un lugar cercano a aquel en el que ellos se encuentren. Se pretende ofrecer una herramienta que ayude a un usuario a gestionar las actividades a realizar en su tiempo libre, aportando facilidades para su organizaci´on en funci´on del perfil de cada usuario y sus capacidades. Para poder llevar a cabo este prop´osito se investigaron los factores m´as cr´ıticos para favorecer el aprovechamiento del tiempo y se desarroll´o una herramienta web, haciendo uso de la tecnolog´ıa Django, donde aquellas personas que busquen sacarle el m´aximo provecho a su tiempo libre acudan para gestionar todas sus actividades. En particular, la aplicaci´on est´a enfocada a la b´usqueda de actividades y eventos de ocio, poniendo ´enfasis en la posibilidad de asistir a dicha actividad en ese mismo momento. Para ello se tienen en cuenta factores como la cercan´ıa f´ısica, la hora de comienzo, el precio de la misma, etc. El resultado final ha cumplido satisfactoriamente con el objetivo buscado, pudi´endose lanzar al mercado competitivo. Adem´as cuenta con un gran potencial de mejora para adaptarse a las necesidades futuras. El c´odigo fuente de este proyecto se encuentra disponible en la siguiente direcci´on: https://github.com/RafaelGB/Eventies 9
16
Cap´ıtulo 1 Introducci´on El objetivo de este trabajo de fin de grado es el desarrollo de una aplicaci´on de web social centrada en eventos y actividades de ocio (conciertos, pel´ıculas, exposiciones, etc.). En este cap´ıtulo se expondr´a la motivaci´on de llevar a cabo una p´agina web para la gesti´on y b´usqueda de eventos as´ı como los objetivos que se pretenden lograr con su implementaci´on. Para concluir se explicar´a el plan de trabajo que se ha seguido, comentando qu´e objetivo se debe cumplir en cada una de las fases del desarrollo. 1.1. Motivaci´on Con una sociedad de gustos cada vez m´as heterog´eneos, existen numerosos tipos de actividades que cubren todo tipo de motivaciones, siendo estas las que nos definen como personas. Por toda la red hay disponibles un sinf´ın de herramientas para descubrir el evento que mejor concuerde con las preferencias de un usuario, pero este proyecto trata de acercar de una manera sencilla su creaci´on y organizaci´on, adem´as de las funcionalidades que ya se ofrecen en el resto de aplicaciones. Dado que el mundo del ocio es un mercado en constante evoluci´on, una aplicaci´on que pretenda gestionarlo necesitar´a adaptarse r´apidamente sin un excesivo tiempo de desarrollo, por lo que otro objetivo de este proyecto es crear una estructura suficientemente modularizable como para seguir este exigente ritmo evolutivo. 17
1.2. Objetivos A continuaci´on se enumerar´an y describir´an los objetivos del TFG: Implementaci´on de la creaci´on de cuentas de usuario y su gesti´on Implementar la funcionalidad de crear una cuenta, recuperar la contrase˜na de una cuenta y gestionar los datos respecto a dicha cuenta (nombre de usuario, email, avatar, etc.) usando las herramientas de que ofrece Django para ofrecer una seguridad razonable. Implementaci´on de la gesti´on de los eventos Implementar la funcionalidad de crear y editar eventos para usuarios registrados en la aplicaci´on y la funcionalidad de mostrar el contenido de un evento para cualquier usuario. Implementaci´on de un buscador y herramientas de filtrado Desarrollar una interfaz donde poder buscar los eventos deseados en una lista y aplicar filtros para facilitar dicha tarea. Integrar un sistema de recomendaciones Crear un sistema de recomendaciones basado en las interacciones del usuario con la p´agina web y sus gustos. 1.3. Plan de trabajo En esta secci´on se detalla la organizaci´on seguida para el desarrollo del proyecto. 1.3.1. Estudio de las herramientas y tecnolog´ıas Durante el primer per´ıodo de las fases de desarrollo se investigar´an las diferentes opciones para llevar a cabo una p´agina web. La primera decisi´on ser´a elegir un framework sobre el que cimentar toda la implementaci´on, ya que esta elecci´on condiciona el resto de fases. Se contemplar´a la decisi´on de usar Django frente a otros frameworks como Spring, sobre el que el autor ten´ıa ya experiencia, para poder aprender otras tecnolog´ıas y lenguajes, ya que Django est´a desarrollado en Python, un lenguaje cada vez m´as utilizado por su versatilidad y sobre el cual no hab´ıa cursado ninguna asignatura durante mis estudios en el grado. Tambi´en se utilizar´a Django ya que el proyecto se concibi´o inicialmente como un sistema recomendador 18
de eventos, lo cual justificar´ıa el uso de Python junto con las librer´ıas de machine learning, como Sklearn. Para llevar a cabo la interfaz web se estudiar´a el uso de la librer´ıa de Bootstrap debido a su gran presencia en el mercado de desarrollo de p´aginas web. Como entorno de desarrollo elegir´e Sublime text tras probar diferentes opciones. En el cap´ıtulo 3 se describen con m´as detalle cada una de las tecnolog´ıas usadas durante el proyecto. 1.3.2. Proceso de desarrollo para cada funcionalidad Primero se dise˜nar´a una base sobre la cual enlazar las diferentes p´aginas web. Una vez se identifican los elementos en com´un que definir´an la interfaz, se pasa a una segunda fase donde se trabajaba cada plantilla HTML por separado, terminando por pulir los fallos. Este proceso se repetir´a con cada caracter´ıstica b´asica de la web. En paralelo se desarrolla la parte del lado del servidor haciendo uso de Django para tratar la informaci´on recibida. De este modo se define c´omo deben relacionarse las diferentes plantillas entre s´ı. Al ser necesario guardar la informaci´on recogida y que esta perdure en el tiempo, se utilizar´a una base de datos para almacenar y gestionar la informaci´on. 1.3.3. Implementaci´on de la creaci´on de cuentas y su gesti´on El desarrollo partir´a de la implementaci´on de las cuentas de usuario donde poder autenticarse en la aplicaci´on. Para ello su usar´a el sistema de autenticaci´on ofrecido por Django y se ampliar´a con las caracter´ısticas extra que requieran los usuarios de la web. Para que el usuario, en caso de olvido de su contrase˜na, pude recuperarla mediante su correo electr´onico, se usar´a un middleware que escuche los correos enviados y se muestren por terminal. De este modo se podr´a probar su funcionamiento. 1.3.4. Implementaci´on de la gesti´on de los eventos Una vez se dispone de una estructura con usuarios, pasar´e a implementar la creaci´on y edici´on de los eventos a trav´es de formularios (con la condici´on de estar registrados) y la interacci´on con la base de datos. Para que otros usuarios puedan acceder a la informaci´on de los eventos creados por 19
los dem´as, pasar´e a a˜nadir una interfaz donde el usuario podr´a acceder a la informaci´on de dichos eventos e interactuar con ellos a˜nadiendo comentarios e indicando si asistir´a al evento, le gusta o no le gusta. 1.3.5. Implementaci´on de un buscador y herramientas de filtrado Tras tener una base donde se gestionan los eventos pasar´e a desarrollar un sistema de b´usqueda de eventos que despliegue los resultados de b´usqueda en una lista y me permita filtrar por diversos criterios. 1.3.6. Integraci´on de un sistema de recomendaciones Tras pulir todas las caracter´ısticas principales de la web se dispondr´a de una base sobre la que usar la informaci´on recogida para informar al usuario sobre otros eventos que pod´ıan interesarle. Tras descartar por falta de tiempo y coherencia el uso de las librer´ıas de machine learning, implement´e un algoritmo donde se ten´ıa en cuenta los eventos a los que el usuario hab´ıa asistido y buscaba entre los dem´as asistentes otros eventos afines. 20
Cap´ıtulo 2 Introduction The objective of this final degree project is the development of a social web application oriented to events and leisure activities (concerts, films, exhibitions, etc.). In this chapter we will describe the motivation to carry out a web page for the management and search of events as well as the objectives that are intended to be achieved with its implementation. To conclude, the work plan that has been followed will be explained, highlighting the objectives to be met in each of the phases of development. 2.1. Motivation In a society of increasingly heterogeneous tastes, there are several types of activities that cover all kinds of motivations, these being what define us as people. All over the internet there is an endless number of tools available to discover the event that best matches the preferences of a user, but this project tries to bring its creation and organization in a simple way, in addition to the functionalities already offered by the rest of applications. Given that the world of entertainment is a market in constant evolution, an application that intends to manage leisure activities will need to adapt quickly without excessive development time, so another objective of this project is to create a sufficiently modular structure to follow this demanding evolutionary rhythm. 21
2.2. Objectives The objectives of the TFG will be listed and described below: Implementation of account creation and management Implement the functionality of creating an account, recovering the password of an account and managing the data regarding that account (username, email, avatar, etc.) using the tools offered by Django to offer a sensible level of security. Implementation of event management Implement the functionality of creating and editing events for users registered in the application and the functionality of displaying the content of an event for any user. Implementation of a search engine and filtering tools Develop an interface where one can search the desired events in a list and apply filters to facilitate this task. Integrate a system of recommendations Create a recommendation system based on the user’s interactions with the website and their tastes. 2.3. Workplan In this section is detailed the organization followed for the development of the project. 2.3.1. Study of tools and technologies During the first period of the development phases, we will survey the different options to carry out the development of web page. The first decision will be to choose a framework on which to base the entire implementation, since this choice determines the rest of the phases. We will contemplate the decision to use Django instead of other frameworks like Spring, (on which I already had experience) to be able to learn other technologies and languages, since Django is developed in Python, a language increasingly used for its versatility and which i had not been taught in my degree studies so far. I will also make the decision to use Django due to the original idea of giving more weight to the recommendation system and make use of the machine learning libraries that Python offers as Sklearn. 22
To develop the web interface, I will opt for the Bootstrap library due to its great presence in the web development market. As a development environment I will choose Sublime text after trying different options. 2.3.2. Development process for each functionality First, a base will be designed on which to link the different web pages. Once the common elements that will define the interface are found, a second phase will start, where each HTML template was worked on separately, finishing by polishing the errors. This process will be repeated for each basic feature of the web. In parallel, the server-side part will be developed using Django to process the received information, thus defining how the different templates should relate to each other. As it is necessary to keep the information collected so it persist over time, each information treatment will be integrated into the database. 2.3.3. Implementation of account creation and management The development will start with the implementation of user accounts where one can authenticate in the application. In order to do this, the user will use the authentication system offered by Django, which will be expanded with the extra features required by web users. In case the user has forgotten their password, the system will allow them to recover it via e-mail. A middleware will be used to capture the event of an email being sent, in order to log this event. 2.3.4. Implementation of event management Once the functionality involving user management is available, I will start to implement the creation and edition of the events through forms, which will be available provided the user has been registered before. In order that other users can access the information of the events created by others, I will add an interface where one can access the information of these events and interact with them by adding comments and by specifying whether he is going to attend the event, and whether he is interested in it or not. 23
2.3.5. Implementation of a search engine and filtering tools After having a base where the events are managed, I will continue by developing an event search system that displays the results in a list and allows the user to filter them according to several criteria. 2.3.6. Integration of a system of recommendations After polishing all the main features of the web there will be a basis on which to use the information collected in order to inform the user about other events that could interest him. After discarding the use of machine learning libraries due to lack of time and coherence, I implemented an algorithm that took the events that the user had attended into account, and searched for other related events from the other attendees. 24
Cap´ıtulo 3 Selecci´on de herramientas y tecnolog´ıas En este cap´ıtulo se detallar´an tanto las herramientas usadas en el proyecto como las descartadas, su funci´on y c´omo se han empleado o se pretend´ıa emplear. 3.1. Herramientas y tecnolog´ıas usadas 3.1.1. Python [5] Python es un lenguaje de programaci´on de alto nivel orientado a objetos, din´amicamente tipado, con una sint´axis indentada, es decir, basada en la tabulaci´on. Es un lenguaje dise˜nado para optimizar la productividad y disminuir el tiempo requerido para su mantenimiento. Su desarrollo est´a bajo una licencia open-source con un gran n´umero de librer´ıas tanto propias como third-party muy f´aciles de integrar. Se ha utilizado el lenguaje para toda la funcionalidad del lado del servidor, denominada back-end, que ofrece el framework Django del que hablar´e m´as adelante y para hacer uso de sus librer´ıas de machine learning, teniendo todo el tratamiento interno de la informaci´on bajo la misma estructura. Se ha usado la versi´on 3.6.3, siendo esta la m´as actualizada. 25
3.2.3. MongoDB MongoDB es el sistema de bases de datos NoSQL m´as popular. Guarda la informaci´on en ficheros BSON (codificaci´on binaria de ficheros JSON) en lugar de tablas como una base de datos SQL relacional. Este tipo de bases de datos son ideales para el uso de t´ecnicas big data y an´alisis de la informaci´on que aprovechan su estructura y rapidez de acceso. Esta opci´on de almacenamiento fue descartada ya en fase de desarrollo al carecer de suficientes datos analizables que fueran aprovechables por las librer´ıas de machine learning y sustituy´endose por la creaci´on din´amica de ficheros CSV (ficheros de texto en los que los valores est´an separados por comas) donde se tratar´a la informaci´on. Adem´as, el tipo de informaci´on que se maneja en el proyecto se adecua mejor a un modelo relacional basado en tablas que a uno basado en documentos. 32
Cap´ıtulo 4 Arquitectura del sistema En este cap´ıtulo se profundizar´a en el funcionamiento de las herramientas desde un enfoque t´ecnico y su integraci´on en el proyecto. 4.1. MVC de Django Django sigue el esquema de MVC (modelo, vista, controlador), pero con una interpretaci´on propia distinta al modelo MVC cl´asico. A continuaci´on se pasar´a a explicar en qu´e consiste este esquema y qu´e matices a˜nade Django al MVC. El patr´on MVC separa en tres claros conjuntos la arquitectura de un programa: Modelo: Es la representaci´on de los datos usados en el programa junto con la interfaz que permite su acceso desde los otros componentes de la aplicaci´on. Se usa el modelo para comunicar la base de datos con el programa abstrayendo al desarrollador de las complejidades del tratamiento de la informaci´on que requiere la base de datos subyacente, por lo que se puede tratar independientemente el modelo de la base de datos, incluso trabajar con bases de datos diferentes simult´aneamente. Vista: Es conocida como la capa de presentaci´on que expone al modelo. La vista engloba todo aquello que puede ver el usuario final de la aplicaci´on e interact´ua con este. Controlador: Sirve de capa intermedia que comunica el modelo con la vista y viceversa. El controlador determina qu´e informaci´on se obtiene de la base de la base de datos para actualizar el modelo y qu´e informaci´on pasa a la vista. 33
Pese a que su arquitectura se asemeja a este modelo en capas, Django mantiene claras diferencias en su implementaci´on, ya la parte del controlador queda definida por el propio Django, dejando al desarrollador un esquema de modelos, vistas y plantillas (MVP). A continuaci´on se explica en m´as detalle dicho esquema: Modelo: es la capa de acceso a los datos y todo lo que concierne a estos (su l´ogica de acceso, su validaci´on, su comportamiento y las relaciones entre ellos). El siguiente fragmento de c´odigo muestra la definici´on de un modelo y qu´e contiene: from django.db import models class MyModel(models.Model): # La clase hereda de models.Model # Se debe definir el tipo de dato y sus atributos texto = models.CharField(max_length=30) fecha = models.DateTimeField(default=datetime.now, blank=True) # Define la representacion del modelo como cadena de texto. def __str__(self): return self.texto # Metodos que permiten acceder a los componentes de un modelo @classmethod def metodo(self, entrada): # Tratar los parametros return resultado # opcional Vista: en esta capa se encuentra la l´ogica que accede a los modelos e invoca a las plantillas. Cuando se llama a una ruta en el navegador, la vista asociada es la encargada de llamar a la plantilla cargando los datos pertinentes. En el siguiente ejemplo se detalla la configuraci´on de una vista de tipo ListView implementada por Django que permite cargar y mostrar una lista de un modelo: from django.views.generic import ListView from .models import MyModel class Mi_vista(ListView): model = MyModel context_object_name = ’objetos_del_modelo’ template_name = ’mi_plantilla.html’ """ ---------------------------------------------- Funciones de la clase ---------------------------------------------- """ def get_context_data(self, **kwargs): 34
context = super(Mi_vista, self).get_context_data(**kwargs) # Se puede incorporar todos los datos que se deseen al contexto return context Plantilla: es la capa de presentaci´on. La plantilla es la encargada de determinar los elementos que deben aparecer en la p´agina e interact´uan con el usuario. Para su desarrollo se usan los lenguajes web HTML5, hojas de estilo y Javascript. A continuaci´on se muestra un ejemplo de plantilla que dispone de una lista sobre un modelo cargado por la vista: {% extends ’base.html’ %} <!-- Las plantillas pueden heredarse -- > { % load propiedades1 %} { % load propiedades2 %} { % block stylesheet %}<!-- opcion de estilo --> <link rel =" stylesheet " href="{ % propiedades1 ’ruta de hoja de estilo ’ %}"> { % endblock %} { % block content %} <div > <h1 >Titulo</h1 > </div > { % for objeto in objetos_del_modelo %} <div > <p>{{ objeto . texto }} </p> <p>{{ objeto . fecha }} </p> </div > { % endfor %} { % endblock %} Las diferencias m´as destacables de la interpretaci´on de Django del MVC cl´asico se resumen en que la vista de Django en este caso complementa las funciones del controlador del MVC cl´asico y las plantillas hacen la funci´on de la vista en el MVC cl´asico al tratarse de p´aginas web con marcadores. 35
4.2. Django ORM En esta secci´on se detallar´a el funcionamiento del ORM (Object Relational Mapper) de Django presentado en el cap´ıtulo anterior. Todos los modelos de Django son subclases de una clase preparada para proporcionar una API de acceso a la base de datos generada autom´aticamente. Si como ejemplo se toma el siguiente modelo: from django.db import models """ En todas las clases que implementan ’models’ se inserta automaticamente una clave primaria de la forma id = models.AutoField(primary_key=True) si no se especifica ninguna por defecto """ class Chef(models.Model): nombre = models.CharField(max_length=50) apellidos = models.CharField(max_length=50) def __str__(self): return self.nombre class Receta(models.Model): creador = models.ForeignKey(Chef, on_delete=models.CASCADE) nombre = models.CharField(max_length=100) duracion = models.DurationField() calificacion = models.IntegerField() def __str__(self): return self.nombre Django ORM proporciona opciones de creaci´on, modificaci´on, borrado y consulta. En el siguiente ejemplo se crean objetos de los modelos definidos anteriormente: """ Se pueden incluir todos los valores dentro de la llamada a la funcion ’create’ o indicarse por separado antes de guardarse """ # Se crea el objeto indicando los valores en la funcion 36
Peter = Chef.objects.create(nombre="Peter",apellidos="Garcia") Peter.save() # Se crea el objeto indicando los valores por separado Marta = Chef.objects.create(nombre="Marta") Marta.apellidos = "Lopez" Marta.save() # Una vez guardado el objeto dispondra de una clave primaria clave_primaria = Marta.pk # Se llama a la clave externa usando la variable asociada al modelo Receta.objects.create(creador=Peter,nombre="pure de patatas") # Se llama a la clave externa adquiriendo primero el modelo getChef_marta = Chef.objects.get(pk=clave_primaria) Receta.objects.create(creador=getChef_marta,nombre="arroz a la cubana") Django ORM dispone de un sistema de consultas y filtros para acceder a la informaci´on de los modelos. A continuaci´on se detallan breves ejemplos que explican su funcionamiento: todo = Chef.objects.all() # devuelve toda la informacion almacenada en el modelo #<QuerySet [<Chef: Peter>,<Chef: Marta>]> filter = Chef.objects.filter(nombre="Peter")# Filtra por parametro-valor #<QuerySet [<Chef: Peter>]> for object in todo: # Se puede iterar sobre una consulta Para modificar un dato en el modelo se guarda la nueva informaci´on sustituyendo el valor y guardando el objeto de nuevo, como se ve en el siguiente ejemplo: Marta = Chef.objects.get(nombre="Marta") Marta.apellidos = "Rodriguez" Marta.save() Para eliminar un objeto del modelo se hace uso de la funci´on delete(): Peter = Chef.objects.get(nombre="Peter") Peter.delete() # Si se consultan todos los elementos se observa que ha sido borrado Chef.objects.all() #<QuerySet [<Chef: Marta>]> # Del mismo modo borra en cascada las recetas cuyo chef se ha eliminado Receta.objects.all() #<QuerySet [<Receta: arroz a la cubana>]> 37
4.3. Gesti´on de los usuarios Django proporciona una herramienta integrada para la gesti´on de los usuarios, donde se incluye un modelo de usuario, un modelo de grupos de usuario, formularios incluyendo su validaci´on y un sistema de autenticaci´on. Para cada grupo de usuarios se pueden delimitar los permisos que uno desee, dando o quitando acceso a las partes de la aplicaci´on que el desarrollador crea oportunas. Por defecto un usuario contar´a con los siguientes campos: username password email first name last name No obstante, este modelo predefinido permite la posibilidad de a˜nadir campos nuevos si la aplicaci´on lo requiere. Para ello es necesario la creaci´on de un modelo nuevo que implemente la clase django.contrib.auth.models.AbstractUser. A continuaci´on se muestra un ejemplo de gesti´on de usuarios con el modelo por defecto: from django.contrib.auth.models import User usuario = User.objects.create_user(’Rafa’,’[email protected]’,’password’) # Pese a estar guardado ya en la base de datos # es posible seguir integrando/cambiando valores usuario.last_name = ’Gomez Bermejo’ usuario.save() #------------------------------------------- from django.contrib.auth import authenticate usuario = authenticate(username=’Rafa’, password=’password’) if usuario is not None: # C´odigo para un usuario autenticado else: # C´odigo para un usuario sin autenticar Para el ejemplo de gesti´on de usuarios con un modelo personalizado se mostrar´a el modelo usado en el proyecto, a˜nadiendo campos como una foto de perfil: 38
from django.db import models from django.contrib.auth.models import AbstractUser def get_image_filename(instance, filename): primaryKey = instance.pk return "Users/ %s/ %s" % (str(primaryKey), filename) class User(AbstractUser): bio = models.TextField(max_length=500, blank=True,default="") location = models.CharField(max_length=30, blank=True,default="") birth_date = models.DateField(null=True, blank=True) avatar = models.ImageField( upload_to =get_image_filename, default = ’none/icon_user.png’, ) def __str__(self): return self.username 4.4. Gesti´on de los eventos La aplicaci´on de los eventos (events) en el proyecto supone el grueso del desarrollo del lado del servidor. El modelo principal que contiene toda la informaci´on b´asica es Event. class Event(models.Model): """ Informacion basica del evento --------------------------------------------------------- """ title = models.CharField(max_length=30) description = models.TextField(max_length=5000) summary = models.CharField(max_length=50) # precio estimado budget = models.DecimalField(null=True, max_digits=5, decimal_places=2, validators=[MinValueValidator(Decimal(’0.00’))], default=Decimal(0.00)) # duracion estimada duration = models.DurationField() """ Contadores creados por defecto --------------------------------------------------------- """ # numero de visitas recibidas 39
views = models.PositiveIntegerField(default=0) """ Fechas --------------------------------------------------------- """ # fecha en la que se celebraria el evento date = models.DateTimeField(default=datetime.now, blank=True) # fecha en la que el organizador crea el evento created_at = models.DateTimeField(auto_now_add=True) # fecha de la ultima modificacion llevada a cabo updated_at = models.DateTimeField(null=True) """ Relaciones uno a muchos --------------------------------------------------------- """ # define quien crea el evento created_by = models.ForeignKey(User, related_name=’events’) """ Relaciones uno a uno --------------------------------------------------------- """ # define la ubicacion donde se llevara a cabo el evento. # es tratada a parte debido al uso de un tipo modelo que admite calculos geometricos geopos_at = models.OneToOneField( Geolocation, on_delete=models.CASCADE, null=True, ) """ Relaciones muchos a muchos --------------------------------------------------------- """ # lista de usuarios que estan interesados interested_in = models.ManyToManyField(User,related_name=’users_interested’) # lista de usuarios que no estan interesados not_interested_in = models.ManyToManyField(User,related_name=’users_not_interested’) # lista de usuarios que van a asistir signed_up = models.ManyToManyField(User,related_name=’users_assistants’) """ ========================================================== 40
Servicios de la clase ========================================================== """ def __str__(self): return self.title # resto de la clase . . . Al modelo principal se han a˜nadido cinco modelos complementarios que modularizan todos los datos que ofrece un evento: Tag: a˜nade palabras clave que se relacionan con un evento. Category: a˜nade categor´ıas a un evento. Photo: a˜nade fotograf´ıas a un evento. Geolocation: a˜nade la localicaci´on del evento. Este modelo usa una API diferente para poder operar con datos geom´etricos llamada django.contrib.gis.db.models. Comments: a˜nade comentarios hechos por los usuarios en un evento. Tanto en el modelo principal como en los complementarios se han a˜nadido funciones de clase para consultas frecuentes o at´ıpicas, dejando las consultas directas ofrecidas por la API implementadas en la vista. class Event(models.Model): #... @classmethod # Busca los eventos que contengan una cadena dada en diferentes campos def search_string(self,string): return Event.objects.filter( Q(title__icontains=string) | Q(summary__icontains=string)) # De este modo, en la vista se podr´a usar esta consulta de manera m´as intuitiva queryset = Event.search_string(contenido_a_buscar) En la vista de los eventos se han usado clases predefinidas que ofrece Django para facilitar la creaci´on, la modificaci´on, el listado y el visionado de los detalles (Figura 4.1). El siguiente fragmento de c´odigo define la p´agina de filtrado de eventos, incluyendo la plantilla, el modelo a listar, el paginado y la consulta a la base de datos con las diferentes condiciones: 41
# Inicializaci´on de formularios return render( # Datos para la plantilla ) 5.1.2. Modificaci´on de eventos El formulario de edici´on (figura 5.2) muestra una estructura id´entica al formulario de creaci´on descrito en la secci´on anterior con los campos inicializados usando la informaci´on del evento a modificar. Los modelos Tag,Category yPhoto requieren una estructura de listado. Su modificaci´on puede implicar la eliminaci´on de elementos, por lo que esta estructura queda contemplada en la l´ogica de la vista. Para su implementaci´on, como se comenta en la descripci´on de la secci´on, se ha utilizado la vista UpdateView de Django, ya que facilita la inicializaci´on de los elementos del modelo para los m´ultiples formularios. A continuaci´on se detalla la implementaci´on de la vista (los formularios utilizados son los mismos que para la creaci´on del evento): @method_decorator(login_required, name=’dispatch’) # Controla que un usuario solo pueda modificar sus propios eventos @method_decorator(user_is_event_author, name=’dispatch’) class EventUpdateView(UpdateView): model = Event template_name = ’update_event.html’ context_object_name = ’event’ form_class = EventForm formGeo_class = GeolocationForm """ ---------------------------------------------------------- Funciones de la clase ---------------------------------------------------------- """ def get_context_data(self, **kwargs): # Recuperamos argumentos ya inicializados context = super(EventUpdateView, self).get_context_data(**kwargs) if not ’errors’ in context: context[’form’] = self.form_class(instance=self.object) # Se incorpora al contexto todos los formularios inicializados 48
return context def get(self, request, *args, **kwargs): super(EventUpdateView, self).get(request, *args, **kwargs) form = self.form_class # Resto de formularios return self.render_to_response(self.get_context_data( object=self.object, form=form ,formGeo=formGeo ,formset=formset)) def post(self, request, **kwargs): self.object = self.get_object() form = self.form_class(request.POST) formGeo = self.formGeo_class(request.POST) PhotoFormSet = modelformset_factory(model=Photo, form=PhotoForm, formset=BasePhotoFormSet, can_delete=False) formset = PhotoFormSet(request.POST or None, request.FILES or None) if all([form.is_valid(),formGeo.is_valid(),formset.is_valid()]): """ Tratamiento de Event ......................................................... """ self.object.title = form.cleaned_data[’title’] #... """ Tratamiento de Geolocation ......................................................... """ updateGeo = Geolocation.objects.get( pk=self.object.geopos_at.pk ) updateGeo.coordinates = formGeo.cleaned_data[’coordinates’] updateGeo.save() # Una vez guardado se guarda el evento self.object.save() """ Tratamiento de Tags ......................................................... """ primalTags = list( Tag.objects.filter( events_tags=self.object ).values_list( ’name_tag’, flat=True ) ) myTags = request.POST["myTags"] arrayTags = myTags.split(’,’) for tag in arrayTags: newTag = None 49
tagExist = Tag.objects.filter(name_tag=tag).exists() if not tagExist: # Crear nuevo tag elif tag not in primalTags: # Se agrega a la lista del evento else: # Se descarta ya que no hay cambios # Los tags no descartados han sido eliminados y se borran de la lista for tag in primalTags: removeTag = Tag.objects.get(name_tag=tag) if removeTag.events_tags.count() == 1: # Este tag solo se usaba na vez y queda borrado else: # Este tag existe en otros eventos por lo que # Solo se elimina de este evento # Resto de modelos con una estructura de lista else: return self.render_to_response( self.get_context_data( errors=True, form=form, formGeo=formGeo, formset=formset )) 5.2. Intereses y asistencia a eventos Para un evento, todo usuario (menos su creador) puede elegir entre tres opciones (figura 5.3) que marcan su relaci´on con el evento, siendo excluyentes entre s´ı: Confirmar asistencia: Indica su intenci´on de ir al evento. Estar interesado: Indica un inter´es por el evento, aunque no la asistencia definitiva al mismo. Puede utilizarse a modo de marcador por si desea asistir m´as adelante. No estar interesado: Indica un rechazo al evento evitando que vuelva a salir en el buscador y no molestar con esta informaci´on al usuario. Esta opci´on se puede deshacer filtrando por los eventos no deseados y desmarcando la casilla. Al hacer clic en una de estas opciones se mandar´a una petici´on AJAX para actualizar en la base de datos la preferencia del usuario identificado, por lo que no ser´a necesario recargar la p´agina. El bot´on seleccionado cambiar´a en funci´on de su estado anterior y se comprobar´a si otra opci´on hab´ıa sido seleccionada antes para modificarla. Para ello se hace uso de AJAX y Javascript que permite cambios din´amicos en la p´agina actualmente cargada en el navegador. 50
Fragmento de c´odigo que env´ıa la petici´on AJAX: <script> // ... function ajax_vote (tipo){ $. ajax ({ type: " POST", url: ’/eventFlowControl/’+tipo +’/’, data: { ’event_pk ’:’{{ object . pk }} ’, ’csrfmiddlewaretoken’ :’{{ csrf_token }} ’ }, dataType: ’json ’, success: function ( data ) { // tratamiento de la respuesta del servidor //en caso de no haber habido errores } }); } </script> 51
A continuaci´on se muestra fragmento de c´odigo que procesa la petici´on AJAX y devuelve una respuesta al usuario: @login_required def EventFlowControl(request,**kwargs): if request.method == ’POST’: id_event = request.POST[’event_pk’] user = request.user event = Event.objects.get(pk=id_event) option = None tipo = kwargs[’type’] remove_add = None if tipo == "interested": #... elif tipo == "attendant": #... elif tipo == "not_interested": #... else: #... data = { ’remove_add’ : remove_add } else: data = {} return JsonResponse(data) 5.3. Comentarios Si el usuario se encuentra identificado en la p´agina de cada evento se mostrar´a la opci´on de a˜nadir un comentario mediante un editor de texto y una lista de todos los comentarios ya publicados ordenados por fecha de publicaci´on (figura 5.4). Al pulsar la opci´on de publicar se enviar´a una petici´on AJAX al servidor para guardar en la base de datos la informaci´on pertinente al comentario. Al mismo tiempo, sin recargar la p´agina, se mostrar´a con una r´apida animaci´on en la parte superior de la lista de comentarios el comentario que se acaba de publicar y un mensaje de ´exito. La aplicaci´on permite a un usuario borrar cualquier comentario que este haya publicado previamente. Se har´a uso de la misma petici´on AJAX con diferentes par´ametros de entrada y borrar´a a continuaci´on de la base de datos dicho comentario. 52
Fragmento de c´odigo que env´ıa la petici´on AJAX. <script> function ajax_comment (tipo , messageID , commentID ){ if( tipo =="remove" && ! confirm (" Estas seguro que deseas borrar el comentario ?")){ return; } $. ajax ({ type: " POST", url: ’/eventCommentsControl/’+tipo +’/’, data: { ’event_pk ’:’{{ object . pk }} ’, ’pk_comment ’: commentID , ’message’: simplemde . value () , ’csrfmiddlewaretoken’:’{{ csrf_token }} ’ }, dataType: ’json ’, success: function ( data ) { // tratamiento de la respuesta del servidor //en caso de no haber habido errores } }); } </script> Fragmento de c´odigo que procesa la petici´on AJAX y devuelve una respuesta al usuario: @login_required def EventCommentsontrol(request,**kwargs): if request.method == ’POST’: id_event = request.POST[’event_pk’] message = request.POST[’message’] user = request.user newComment = Comments() if Event.objects.filter(pk=id_event).exists(): event = Event.objects.get(pk=id_event) option = None tipo = kwargs[’type’] remove_add = None if tipo == "create": #... elif tipo == "remove": #... else: feedback=’error’ 53
data = { ’feedback’ : feedback, ’message’ : message, ’time’ :’ ahora mismo’, ’type’ : tipo, ’comment_pk’: newComment.pk } else: print("evento NO existe") data = {} else: data = {} return JsonResponse(data) 54
Figura 5.2: Capturas de pantalla del formulario de edici´on de un evento Figura 5.3: Captura de pantalla de los botones antes y despu´es de seleccionar una opci´on 55
Figura 5.4: Captura de pantalla de un comentario antes y despu´es de ser publicado 56
Cap´ıtulo 6 B´usqueda avanzada de eventos En este cap´ıtulo se contar´a en profundidad la herramienta de b´usqueda y filtrado de eventos implementada para la web (Figura 6.1). 6.1. Realizaci´on de la consulta Para consultar los eventos usando filtros diferentes se hace uso de la API que ofrece Django mediante las queryset. Una queryset es una abstracci´on de una consulta sobre la BD. Puede construirse directamente a partir de una tabla, estableciendo condiciones de b´usqueda sobre la misma, o bien a partir de otro queryset, estableciendo condiciones de b´usqueda adicionales a los ya descritos por el mismo. Esto es ideal para componer diferentes filtros dependientes entre s´ı de manera modular. Para su implementaci´on en el proyecto, en primer lugar se vuelca en la queryset una consulta m´as gen´erica en funci´on del par´ametro de entrada type definido en la URL. Este par´ametro de entrada decidir´a si se consulta sobre los propios eventos del usuario autenticado o si se hace sobre todos los eventos de la base de datos. Una vez se ha definido el queryset, se comprueban las variables de tipo GET en el request recibidas desde el navegador y se tratan para establecer condiciones adicionales sobre lo ya filtrado hasta obtener la consulta que el usuario ha solicitado. A continuaci´on se detallar´a cada filtro y su implementaci´on definida en la aplicaci´on events/views.py en la clase EventFilterView: class EventFilterView(ListView): # Resto de la clase 57
64
Cap´ıtulo 7 Recomendaci´on de eventos En este cap´ıtulo se detallar´a la funcionalidad de recomendaci´on de eventos implementada para usuarios autenticados. Las recomendaciones se muestran en funci´on de los eventos a los que los usuarios eligieron asistir. 7.1. Estructura b´asica Para llevar a cabo un sistema de recomendaciones se necesita de informaci´on relevante que represente los gustos de cada usuario. Para ello se ha creado el modelo recommender, el cual contiene una lista de eventos considerados relevantes para un determinado usuario. A continuaci´on se muestra el modelo del recomendador: class MyRecommender(models.Model): user = models.IntegerField(primary_key=True) id_events = ArrayField(models.IntegerField(blank=True),default=list, null=True) def __str__(self): return str(self.user) Para poder obtener la lista de eventos recomendados de cada usuario se ha tenido en cuenta los eventos en los que seleccionaron ’asistir´e’ para, con ello, averiguar qu´e usuarios eligieron de la misma manera. Una vez hallados los usuarios con las mismas elecciones, se buscan aquellos eventos a los que estos ´ultimos van a asistir, pero el usuario al cual se est´a recomendando a´un no lo ha hecho, y agruparlos en una lista sin repeticiones. 65
Figura 7.1: Captura de pantalla de recomendaciones para un usuario 7.2. Algoritmo utilizado Para hallar la lista de recomendaciones se ha creado un archivo de tipo .CSV con tres columnas: clave primaria de eventos (evento con asistentes), clave primaria de usuarios (usuario que asiste) y visitas del eventos. A continuaci´on se muestra el c´odigo con la instrucci´on a la base de datos para crear el archivo: from django.db import connection # e parametro de entrada path indica en que ruta se desea guardar def restart_csv(path): with connection.cursor() as cursor: cursor.execute("copy (SELECT ev.id,signedup.user_id,ev.views FROM \"public\".\"events_event\" AS ev LEFT JOIN \"public\".\"events_event_signed_up\" AS signedup ON ev.id=signedup.event_id ORDER BY -ev.views) TO ’"+path+"’ DELIMITER ’,’ CSV") En dicho archivo se guarda toda la informaci´on sobre a asistencia a los eventos de todos los usuarios de la base de datos. De este modo puede manejarse a modo de matriz a gran escala. Para poder tratar los datos en un tiempo razonable con la matriz generada, se crea una submatriz a partir del dataframe entero usando la librer´ıa cross validation de sklearn de Python, que recoger´ıa un porcentaje inferior al total de las filas del archivo previamente volcado en el dataframe utilizando la librer´ıa pandas. 66
import pandas as pd header = [’event_id’,’user_signedUp_id’,’views’] df = pd.read_csv(self.csv_path, sep=’,’, names=header) A continuaci´on se muestra el fragmento de c´odigo donde queda implementada la l´ogica descrita en la secci´on 7.1: class Recomender(CronJobBase): #... # Diccionario con el resultado final de todas las listas # asociando una a cada usuario dictRecommenders = {} for xin unique_user_ids: dictRecommenders[x]=[] """ Se trata cada evento individualmente con la matriz para hallar eventos relevantes en cualquier usuario """ for event_id in unique_event_ids: event_pos = event_id_pos[event_id] row = train_data_matrix[event_id_pos[event_id],:] pos_similar_users = [] recommended_events=[] # Se buscan coincidencias entre usuarios mirando cada fila for index,elem in enumerate(row): if int(elem)>0: pos_similar_users.append(index) recommended_events.append(int(elem)) if len(pos_similar_users)>1: for index,(posUser,idEvent) in enumerate(zip(pos_similar_users,recommended_events)): pos_other_similar_users = pos_similar_users pos_other_similar_users.remove(posUser) # Para cada usuario con alguna coincidencia for jin pos_other_similar_users: # Se comprueban eventos recomendables # en la columna de la matriz col = train_data_matrix[:,j] for yin col: if y>0 and y != event_id and ynot in dictRecommenders[unique_user_ids[posUser]]: dictRecommenders[unique_user_ids[posUser]].append(int(y)) 67
Una vez conseguido el diccionario con todas las listas de recomendaciones se vuelcan en la base de datos: for key, value in .items(): pos = user_signedUp_id_pos[key] col = train_data_matrix[:,pos] for iin col: if int(i) in value: value.remove(i) tmpRecommender = MyRecommender(user=int(key),id_events=value) tmpRecommender.save() Este proceso se repetir´a autom´aticamente de manera peri´odica para ir actualizando las recomendaciones de los usuarios. Dichas recomendaciones se mostrar´an en la p´agina principal de la web como se muestra en la figura 7.1. En caso de no existir ninguna recomendaci´on se mostrar´a un mensaje informativo ( Figura 7.2). Figura 7.2: Captura de pantalla de recomendaciones para un usuario 68
Cap´ıtulo 8 Conclusiones y trabajo futuro En este cap´ıtulo se discutir´an hasta qu´e punto se han llevado a cabo los objetivos propuestos por el trabajo, las dificultades que han supuesto y qu´e resultar´ıa interesante incorporar. 8.1. Objetivos alcanzados La idea b´asica del trabajo de llevar a cabo una p´agina web funcional sobre la gesti´on de eventos se ha llevado a cabo superando las expectativas, las cuales se ve´ıan limitadas por el aprendizaje de una nueva tecnolog´ıa y el tiempo de adaptaci´on que supone. Tras el aprendizaje de Django y al ver su potencial se fueron a˜nadiendo funcionalidades que no hab´ıan sido pensadas en un principio y que aportan valor a la web (por ejemplo, comentarios o palabras clave en los eventos), y otras muchas que no se han llevado a cabo por limitaciones de tiempo y que ser´ıan de utilidad (por ejemplo, un calendario donde gestionar todas las actividades). El dise˜no de la interfaz trajo dificultades al a˜nadir a los formularios dise˜nos de terceros que facilitaban el introducir una direcci´on en el mapa o a˜nadir una fecha al evento a trav´es de un calendario interactivo. Esto requer´ıa aprender nueva documentaci´on sobre su uso, pero la mayor dificultad fue aprender el funcionamiento de los formset en Django. Un formset permite disponer de formularios din´amicos, en tanto que su n´umero de componentes puede variar a medida que el usuario introduce los datos. En particular, el formset ha sido utilzado para gestionar m´ultiples fotograf´ıas en un mismo formulario. El uso de los formset no dispon´ıa de mucha documentaci´on y supuso un par´on en el desarrollo de dicho formulario optando por continuar con el resto de funcionalidades hasta adquirir m´as experiencia con la tecnolog´ıa. 69
La elaboraci´on del servidor, una vez dominado el framework, ha llevado a un proyecto organizado, sin graves problemas de funcionamiento y con una escalabilidad potencial, siendo la parte m´as elaborada la parte de gesti´on de la base de datos. Encontr´e diversos problemas a la hora de filtrar por distancia al usuario un evento, ya que toda la documentaci´on que encontr´e sobre el tratamiento de datos geom´etricos con Django ORM estaba enfocada al uso de un tipo de datos concreto dentro de un sistema gestor de bases de datos concreto (PostgreSQL). El usado hasta el momento (SQLite) utilizaba una representaci´on de datos distinta, por lo que se adapt´o el mecanismo de almacenamiento al est´andar que requer´ıa la herramienta. Para concluir, el ´ultimo objetivo era implementar un sistema de recomendaciones coherente con cada usuario. Tras indagar en el funcionamiento de las librer´ıas de machine learning y dado que el tipo de informaci´on que se guarda en la base de datos es suficientemente esclarecedor para hallar los gustos de cada usuario registrado de una manera m´as directa, se tom´o la decisi´on de usar un algoritmo propio para llevar a cabo dichas recomendaciones. Este algoritmo se podr´ıa mejorar si se tienen en cuenta otros factores como las categor´ıas u otros atributos que se a˜nadieran en un futuro a la web. 8.2. Trabajo futuro La p´agina web admite m´ultiples a˜nadidos. A continuaci´on se enumerar´an las siguientes propuestas: P´agina de calendario Para facilitar la organizaci´on al usuario, mejorar´ıa mucho la aplicaci´on tras la incorporaci´on de una interfaz con un calendario donde poder ver los eventos a los que va a asistir con un acceso directo a cada uno de ellos. En dicho calendario los eventos dispondr´ıan de opciones r´apidas de cancelaci´on o cambio de opini´on. Creaci´on de cuenta a trav´es de terceros A˜nadir la opci´on de crear cuenta usando la API de Facebook yGoogle de tal manera que, con un solo click, el usuario tendr´ıa acceso a todas las funcionalidades m´as c´omodamente sin tener que crear una cuenta en la aplicaci´on web.. Incorporaci´on de redes sociales A˜nadir la opci´on de compartir los eventos en las redes a trav´es de diferentes APIs como Twitter oFacebook dar´ıa mucha m´as visibilidad a la web. 70
Sistema de puntuaci´on Incorporar la posibilidad de votar un evento dar´ıa una informaci´on muy valiosa para mejorar las recomendaciones personalizadas. Tambi´en se usar´ıa para a˜nadir una opci´on de filtrado m´as. Subida del proyecto a un Docker Subir a una platafoma de despliegue (como Docker) la web para que el proyecto pueda ser usado por cualquier persona. Una vez se haya subido cambiar la forma de almacenar el contenido multimedia (en este caso, las fotograf´ıas de cada evento y el avatar de cada usuario) en un sistema de almacenamiento en la nube como Amazon S3 usando un web service. Mejora en el sistema de recomendaciones Por falta de tiempo, no se ha ahondado en la funcionalidad de recomendaciones que dispone la pagina web, por lo que ser´ıa interesante investigar nuevas alternativas al algoritmo ya implementado o a˜nadir nuevos condicionantes con la informaci´on que ya se dispone de cada usuario como sus categor´ıas preferidas. Crear una versi´on para m´ovil Llevar la aplicaci´on a una versi´on m´ovil multi-plataforma adaptando la interfaz web a dispositivos m´oviles dar´ıa mucho m´as valor al proyecto. 71
72
Cap´ıtulo 9 Conclusions and future work In this chapter we will discuss to what extent the objectives introduced in this work have been carried out, the difficulties they have entailed and what it would be interesting to incorporate. 9.1. Achieved goals The main goal of developing a web page for event management has been carried out successfully. The initial expectations, which were limited due to the necessity of learning a new technology (with the amount of time it involves) have been surpassed. After I learnt and discovered the potential of Django, I was able to incorporate a few features that had not been planned in advance (such as comments and keywords in events), and identify several other features that, although they would be useful, have not been implemented due to time constraints (for example, a calendar to display and manage all the activities). The design of the interface involved some issues when integrating third-party components into forms. It turns out that these components make the task of entering an address (via a map) or selecting a given date (through an interactive calendar) easier. but the addition of these components required studying further documentation. Nevertheless, the biggest issue was found when dealing with Django’s formset. A formset allows a web page to dynamically change the number of form components. This has been used in the form that manages the pictures attached to an event. The scarcity of suitable formset’s documentation implied a break in the development of this functionality until I had acquired more experience with this technology. Once the learning curve of the framework has been overcome, the development of the server’s functionality has led to an well-structured project, without serious operational issues 73