EscuelaTécnicaSuperiorde*Ingenieríade*SistemasInformático...

78
UNIVERSIDAD POLITÉCNICA DE MADRID Escuela Técnica Superior de Ingeniería de Sistemas Informáticos MÁSTER EN INGENIERÍA WEB Proyecto Fin de Máster Análisis y Desarrollo de un Sistema de Perfeccionamiento de Idiomas Autor Jorge Rábanos Peña Tutor Santiago Alonso Villaverde Junio de 2016

Transcript of EscuelaTécnicaSuperiorde*Ingenieríade*SistemasInformático...

 

 

 

UNIVERSIDAD  POLITÉCNICA  DE  MADRID  Escuela  Técnica  Superior  de  Ingeniería  de  Sistemas  Informáticos  

   

MÁSTER  EN  INGENIERÍA  WEB      Proyecto  Fin  de  Máster    

 

Análisis  y  Desarrollo  de  un  Sistema  de  Perfeccionamiento  de  Idiomas  

         

Autor  Jorge  Rábanos  Peña  

   

Tutor  Santiago  Alonso  Villaverde  

       

Junio  de  2016

2    

1.          INTRODUCCIÓN   3  1.1.  RESUMEN  DEL  PROYECTO   3  1.2.  ABSTRACT   4  1.3.  OBJETIVOS   5  

2.   HERRAMIENTA  DE  INTERCAMBIO  DE  IDIOMAS   6  2.1.  ESTRUCTURA  DE  LA  RED  SOCIAL   6  2.2.  AFINIDAD  DE  USUARIOS   6  2.3.  CÓMO  SE  USA   6  

3.   PLANIFICACIÓN  DEL  PROYECTO   7  3.1.  METODOLOGÍA   7  3.2.  TECNOLOGÍAS   7  3.3.  FUNCIONALIDADES   8  3.4.  CASOS  DE  USO   9  3.5.  MODELO  DE  DATOS   19  3.5.1.  TABLAS   20  3.6.  DISEÑO  DE  INTERFAZ   22  3.7.  PLAN  DE  PRUEBAS   32  3.8.  ITERACIONES   33  

4.   DESARROLLO  DEL  PROYECTO   34  4.1.  CONCEPTOS  IMPORTANTES   34  4.1.1.  API  REST   34  4.1.2.  WEBSOCKET   35  4.2.  ARQUITECTURA   36  4.3.  SERVIDOR  Y  API  REST   38  4.3.1.  HERRAMIENTAS   38  4.3.2.  API  REST  Y  RECURSOS   39  4.3.3.  IMPLEMENTACIÓN  DE  LA  API   40  4.3.4.  PRUEBAS   44  4.4.  SERVIDOR  DE  CHAT   45  4.4.1.  DJANGO  CHANNELS   45  4.4.2.  IMPLEMENTACIÓN   46  4.4.3.  ARQUITECTURA  FINAL   47  4.5.  CLIENTE  WEB   52  4.5.1.  HERRAMIENTAS   52  4.5.2.  ESTRUCTURA   55  4.5.3.  IMPLEMENTACIÓN  DE  LA  INTERFAZ   55  3.5.4.  WEBSOCKETS  /  CHAT   62  

5.   CONCLUSIONES   64  

6.   FUTURAS  AMPLIACIONES   65  

7.   BIBLIOGRAFÍA   66  

8.   APÉNDICE   67  8.1.  ESPECIFICACIÓN  API  REST   67      

3    

1. Introducción  

1.1. Resumen del proyecto  La  necesidad  de  aprender  y  dominar  más  de  una  lengua  es  algo  que  se  observa  de  forma   clara   en   la   actualidad.  Ya   sea  para   acceder   a  un  puesto  de   trabajo,   buscar  información,  viajar  o  incluso  ver  películas;  saber  idiomas  es  siempre  beneficioso  y  su  desconocimiento  significa,  en  muchos  casos,  quedarse  atrás.    Además  de  la  comprensión  y  la  gramática,  hay  un  aspecto  en  el  que  se  debe  hacer  cierto  énfasis  si  realmente  se  quiere  dominar  un  idioma:  la  práctica.  Los  múltiples  libros  y  cursos  de  idiomas  a  los  que  tenemos  acceso  pueden  dar  una  base  consistente  y  un  vocabulario  amplio,  pero  no  aportan  ese  punto   importante  que   todos   tratamos   de   alcanzar,   practicar   una   lengua   hasta   poder   hablarla   con  soltura.    La   idea  de  este  proyecto  es   la  de   resolver  esa  necesidad  de  practicar  una   lengua  para   afianzar   los   conocimientos   mediante   una   herramienta   de   intercambio   de  idiomas.    Se   trata   de   poner   en   contacto   a   personas   deseosas   de   practicar   idiomas   para  realizar   un   intercambio:   “Si   te   parece,   hablamos   un   rato   en   inglés   y   después   en  español”.    Es   también   el   objetivo   de   este   proyecto   ofrecer   a   los   usuarios   la   posibilidad   de  organizar  quedadas  grupales  en  lugares  cercanos  para  practicar  en  persona  y  no  a  través  de  Internet.                                      

4    

1.2. Abstract  The  need  to  learn  and  master  more  than  one  language  is  something  that  is  seen  clearly  nowadays.  Whether  to  apply  for  a  job,  find  information,  travel  or  even  watch  movies;  learning  languages  is  always  beneficial  and  to  ignore  them  means,  in  most  cases,  to  stay  behind.    In  addition  to  comprehension  and  grammar,  there  is  one  aspect  in  which  some  emphasis  should  be  done  if  you  really  want  to  master  a  language:  practice.  Multiple  books  and  language  courses  we  can  access  give  a  consistent  basis  and  a  wide  vocabulary,  but  do  not  provide  this  important  point  we  all  try  to  reach,  practice  a  language  to  be  able  to  speak  it  fluently.    The  idea  of  this  project  is  to  solve  this  need  to  practice  a  language  to  strengthen  understanding  through  a  language  exchange  tool.    It  is  bringing  together  people  eager  to  practice  languages  for  an  exchange:  "If  you  agree,  we  talk  for  a  while  in  English  and  then  in  Spanish."    It  is  also  the  purpose  of  this  project  to  provide  users  the  ability  to  organize  group  hangouts  nearby  places  to  go  in  person  and  not  via  the  Internet.                                                          

5    

1.3. Objetivos  El  objetivo  principal  del  proyecto  es  el  desarrollo  de  una  red  social  de  intercambio  de  idiomas.  Esta   red   social   servirá   principalmente   como   medio   para   poner   en   contacto,   a  través   de   Internet,   a   usuarios   que   deseen   practicar   idiomas   y   para   organizar  quedadas.    Para  llevarlo  a  cabo,  se  debe  definir  cómo  va  a  ser  la  red,  qué  funcionalidades  debe  cumplir  y  cómo  se  va  a  usar.  Después  se  organizará  una  gestión  de  usuarios  y  un  diseño  de  base  de  datos  del  sistema.    Una  vez  definido  lo  anterior,  el  siguiente  objetivo  y  parte  central  del  proyecto  es  el  desarrollo   de   una   aplicación   Web   que   ofrezca   un   servicio   cumpliendo   las  funcionalidades  deseadas.  Para  ello,  se  deberán  desarrollar  los  siguientes  elementos:  

-­‐ Aplicación   de   servidor   en   la   que   se   desarrolle   la   lógica   del   negocio.   Ésta  debe  ofrecer  una  API  REST  para  ser  usada  desde  aplicaciones  de  cliente.  

-­‐ Una   aplicación   de   cliente   Web   que   haga   uso   de   la   API   anteriormente  definida.  

-­‐ Un  módulo  complementario  a  los  anteriores  que  permita  a  dos  usuarios  del  sistema  mantener  conversaciones  en  tiempo  real.  

                                           

6    

2. Herramienta de intercambio de idiomas

 

2.1. Estructura de la red social  La   aplicación   sobre   la   que   versa   el   proyecto   es   una   herramienta   que   facilita   la  relación  y   comunicación  entre  personas  a   través  de   Internet,   lo  que  entendemos  por  red  social.    A  diferencia  de  otras  tantas  que  existen  a  día  de  hoy,  no  se  plantea  el  concepto  de  amistad  o  seguimiento.  Simplemente  se  trata  de  poner  en  contacto  a  personas  sin  comprometer  ni  manifestar  ese  contacto.    Es  por  ello  que   los  usuarios  podrán  ver,  a   forma  de   listado,   los  perfiles    de  otros  usuarios  afines  a  sus  necesidades  en  lo  que  a  práctica  de  idiomas  se  refiere.    Entre   dos   usuarios   cualesquiera,   la   relación   dentro   de   la   aplicación   vendrá  representada   en   forma   de   sala   de   chat,   en   la   que   podrán   mantener   una  conversación.    

2.2. Afinidad de usuarios  Para   relacionar   usuarios   por   afinidad,   el   sistema   dispone   de   dos   tipos   de   datos  cruciales:  qué  idiomas  desea  practicar  el  usuario,  y  cuáles  habla  o  domina.    En   función   de   esto,   será   más   afín   a   un   usuario   quien   más   idiomas   pueda  intercambiar  y  será  menos  afín    quien  no  tenga  ningún  punto  en  común.    La  necesidad  de  separar   idiomas  hablados  e   idiomas  practicados  surge  de  que  se  quiere  enfatizar  la  importancia  del  intercambio:  lo  ideal  es  que  si  un  usuario  habla  inglés  y  quiere  practicar  español  se  ponga  en  contacto  con  otro  que  hable  español  y  desee  practicar  inglés.    

2.3. Cómo se usa  Como   se   ha   explicado   anteriormente,   la   parte   en   la   que   se   pone   en   contacto   a  usuarios  tiene  un  flujo  muy  sencillo:  el  usuario  visualiza  perfiles  de  otros  usuario  afines  a  él.  Una  vez  decide  con  quién  desea  practicar  idiomas,  puede  abrir  una  sala  de  chat  en  la  que  conversar.    Respecto  a  la  organización  de  quedadas,  la  aplicación  ofrece  tanto  búsqueda  como  servicio  de  creación  de  éstas,  de   forma  que  se  puedan  establecer  y  visualizar   las  quedadas  desde  la  aplicación  con  cierta  facilidad.      

7    

3. Planificación del proyecto  

3.1. Metodología  El  proyecto   se  ha  dividido  en  varias   iteraciones,  que  han  permitido   separarlo  en  partes  bien  diferenciadas   y   funcionales  para   ser  probado  y  modificado  de   forma  incremental.    Las  iteraciones  que  se  iban  a  llevar  a  cabo  se  decidieron  una  vez  definidas  las  funcionalidades  que  iba  a  tener  la  herramienta.    Cada  una  de  estas  iteraciones  tenía  una  serie  de  tareas  asignadas  en  principio,  pero  a   partir   de   las   pruebas   y   los   problemas   que   han   ido   surgiendo,   se   han   podido  añadir  más  tareas,  mejoras  y  funcionalidades.    Para  llevar  un  control  de  las  tareas  y  las  prioridades,  se  ha  adoptado  parte  de  la  metodología  Kanban.      Kanban  es  una  metodología  que  permite  gestionar  y  organizar  el  trabajo.  Su  uso  en  el  desarrollo  de  software  implica  el  uso  de  tarjetas  que  representan  elementos  de  trabajo.  Estas  tarjetas  permiten  mostrar  el  proceso  de  desarrollo  colocadas  en  un  tablero  dividido  en  columnas,  representando  cada  una  de  ellas  un  estado  del  flujo  de   trabajo   (análisis-­‐desarrollo-­‐test-­‐producción,   o   por   hacer-­‐en   proceso-­‐hecho,  etc.)      En  este  proyecto  se  ha  utilizado  para   tener  un  control  y  una  visión  general  de   la  progresión   del   mismo.   Cada   tarea   se   ha   correspondido   con   una   tarjeta   Kanban,  para  así  poder  tenerlas  identificadas  junto  a  su  estado  y  prioridad.    

3.2. Tecnologías  Estas  son  las  principales  tecnologías  que  han  establecido  el  entorno  de  trabajo  en  el  que  se  ha  desarrollado  el  proyecto:    

-­‐ Git:   es   un   software   de   control   de   versiones   que   permite   realizar   un  seguimiento  de  cambios  en  archivos  y  restauración  de  versiones  anteriores.  Se  ha  utilizado  mediante  la  herramienta  SourceTree  de  Atlassian  haciendo  uso  de  un  repositorio  remoto  alojado  en  GitHub.  

-­‐ Taiga:  plataforma  de  gestión  de  proyectos.  Se  ha  utilizado  para  definir   las  funcionalidades,   llevar   un   registro   del   desarrollo   de   las   mismas   y  documentar  la  API  REST.  

-­‐ OmniGraffle:   una   aplicación   de   The   Omni   Group   para   la   creación   de  diagramas.   Se   ha   usado   tanto   para   diseñar   la   interfaz   como   para   crear  diagramas  explicativos  de  esta  memoria.  

   

8    

3.3. Funcionalidades  Las  funcionalidades  que  debe  cumplir  la  herramienta  son  las  siguientes:    Registro  y  acceso   Cualquier  persona  debe  poder  acceder  

al  sistema  mediante  un  registro  y  posterior  acceso  con  credenciales.  

Indicar  idiomas  hablados  y  practicados  

El  usuario  debe  ser  capaz  de  gestionar  qué  idiomas  desea  practicar    y  cuáles  domina.  

Edición  de  perfil  de  usuario   La  herramienta  debe  permitir  la  edición  de  los  datos  de  usuario  y  avatar  por  el  mismo.  

Búsqueda  de  usuarios  afines   Se  debe  proporcionar  al  usuario  información  sobre  otros  usuarios  afines  a  sus  idiomas.  

Visualización  de  perfiles   Cada  usuario  puede  ver  el  perfil  público  del  resto  de  usuarios  del  sistema.  

Abrir  conversaciones   El  usuario  debe  poder  abrir  una  conversación  con  otro  usuario  para  poder  hablar  en  tiempo  real.  

Buscar  eventos   Se  debe  ofrecer  la  posibilidad  de  visualizar  eventos  organizados  cerca  de  la  posición  del  usuario.  

Crear  eventos   Cada  usuario  debe  tener  la  capacidad  de  organizar  un  nuevo  evento  en  una  fecha  futura  desde  la  aplicación.  

                                     

9    

3.4. Casos de uso  Una   vez   definidas   las   funcionalidades   básicas,   se   plantean   una   serie   de   casos   de  uso  que  debe  cumplir  el  sistema.    

3.4.1. Registro de usuario    Caso  de  uso   Registro  de  usuario  Actor   Usuario  Resumen   El  usuario  se  da  de  alta  en  el  sistema  a  

través  de  un  formulario  Precondiciones   Ninguna  

Curso  típico  de  eventos  Usuario   Sistema  

1.  Accede  a  la  Web  de  la  aplicación    2.  Introduce  sus  datos:  email,  nombre  y  contraseña  

 

3.  Solicita  alta  a  través  de  un  botón       4.  Comprueba  la  validez  de  los  datos     5.  Crea  un  nuevo  usuario  del  sistema  

con  los  datos  proporcionados     6.  Muestra  por  pantalla  una  

confirmación  de  registro  Flujo  alternativo  1  

    4.  [Los  datos  proporcionados  no  son  válidos]  

  5.  Muestra  por  pantalla  un  mensaje  de  error  

                               

10    

3.4.2. Acceso al sistema    Caso  de  uso   Acceso  al  sistema  Actor   Usuario  Resumen   El  usuario  accede  al  sistema  haciendo  

uso  de  sus  credenciales  Precondiciones   El  usuario  se  ha  dado  de  alta  

Curso  típico  de  eventos  Usuario   Sistema  

1.  Accede  a  la  Web  de  acceso    2.  Introduce  sus  datos  en  el  formulario  de  acceso  

 

3.  Solicita  acceso  a  través  de  un  botón       4.  Comprueba  la  validez  de  los  datos     5.  Crea  una  sesión  de  usuario     6.  Muestra  la  aplicación  

Flujo  alternativo  1       4.  [Los  datos  proporcionados  no  son  

válidos]     5.  Muestra  por  pantalla  un  mensaje  de  

error    

3.4.3. Salir del sistema    Caso  de  uso   Salir  del  sistema  Actor   Usuario  Resumen   Se  cierra  la  sesión  a  petición  del  usuario  Precondiciones   El  usuario  debe  estar  autenticado  

Curso  típico  de  eventos  Usuario   Sistema  

1.  Solicita  salir  del  sistema       2.  Cierra  la  sesión  del  usuario     3.  Redirige  a  la  página  de  autenticación                      

11    

3.4.4. Mostrar lista de usuarios afines    Caso  de  uso   Lista  de  usuarios  afines  Actor   Usuario  Resumen   Se  muestra  al  usuario  una  lista  de  otros  

usuarios  afines  a  su  perfil  Precondiciones   Ninguna  

Curso  típico  de  eventos  Usuario   Sistema  

1.  Accede  a  la  sección  de  usuarios  afines       2.  Recupera  una  lista  de  usuarios  

ordenados  por  afinidad  en  función  de  los  idiomas  del  usuario  

  3.  Muestra  por  pantalla  los  datos  de  los  usuarios  

 

3.4.5. Visualizar perfil de usuario    Caso  de  uso   Visualizar  perfil  de  usuario  Actor   Usuario  Resumen   Se  muestran  los  datos  de  perfil  de  un  

usuario  en  concreto  Precondiciones   Ninguna  

Curso  típico  de  eventos  Usuario   Sistema  

1.  Accede  al  perfil  de  un  usuario       2.  Muestra  por  pantalla  los  datos  del  

usuario:  nombre,  edad,  género,  descripción,  idiomas  que  habla,  idiomas  que  practica  y  avatar  

  3.  Muestra  por  pantalla  los  datos  de  los  usuarios  

                       

12    

3.4.6. Actualizar datos de perfil    Caso  de  uso   Actualizar  datos  de  perfil  Actor   Usuario  Resumen   El  usuario  debe  poder  actualizar  sus  

datos  públicos  de  perfil  Precondiciones   Ninguna  

Curso  típico  de  eventos  Usuario   Sistema  

1.  Accede  a  la  sección  de  perfil  propio    2.  Modifica  alguno  de  los  datos  de  perfil  (descripción,  género,  fecha  de  nacimiento)  

 

3.  Solicita  guardar  los  cambios       4.  Actualiza  el  perfil  del  usuario     5.  Muestra  un  mensaje  de  éxito    

3.4.7. Cambiar imagen de perfil    Caso  de  uso   Cambiar  imagen  de  perfil  Actor   Usuario  Resumen   El  usuario  actualiza  o  establece  su  

avatar  o  imagen  de  perfil  Precondiciones   Ninguna  

Curso  típico  de  eventos  Usuario   Sistema  

1.  Accede  a  la  sección  de  perfil  propio    2.  Selecciona  una  imagen    3.  Solicita  actualizar  avatar       4.  Actualiza  el  avatar  del  usuario     5.  Muestra  un  mensaje  de  éxito  

Flujo  alternativo  1       4.  [El  formato  de  imagen  no  es  

soportado  en  el  sistema]     5.  Muestra  por  pantalla  un  mensaje  de  

error                    

13    

3.4.8. Añadir idioma hablado    Caso  de  uso   Añadir  idioma  hablado  Actor   Usuario  Resumen   El  usuario  añade  un  idioma  a  su  lista  de    

idiomas  que  habla  o  domina  Precondiciones   Ninguna  

Curso  típico  de  eventos  Usuario   Sistema  

1.  Accede  a  la  sección  de  idiomas  propios  

 

  2.  Muestra  una  lista  de  idiomas  posibles  para  añadir  

3.  Selecciona  un  idioma  de  la  lista    4.  Solicita  añadir  idioma  hablado       5.  Se  añade  el  idioma  a  la  lista  de  

idiomas  hablados  del  usuario     6.  Muestra  una  mensaje  de  éxito    

3.4.9. Añadir idioma en práctica    Caso  de  uso   Añadir  idioma  en  práctica  Actor   Usuario  Resumen   El  usuario  añade  un  idioma  a  su  lista  de    

idiomas  que  desea  practicar  Precondiciones   Ninguna  

Curso  típico  de  eventos  Usuario   Sistema  

1.  Accede  a  la  sección  de  idiomas  propios  

 

  2.  Muestra  una  lista  de  idiomas  posibles  para  añadir  

3.  Selecciona  un  idioma  de  la  lista    4.  Solicita  añadir  idioma  en  práctica       5.  Se  añade  el  idioma  a  la  lista  de  

idiomas  practicados  del  usuario     6.  Muestra  una  mensaje  de  éxito              

14    

3.4.10. Eliminar idioma hablado    Caso  de  uso   Eliminar  idioma  hablado  Actor   Usuario  Resumen   El  usuario  elimina  un  idioma  de  su  lista  

de  idiomas  hablados  Precondiciones   Debe  existir  algún  idioma  en  la  lista  de  

idiomas  hablados  del  usuario  Curso  típico  de  eventos  

Usuario   Sistema  1.  Accede  a  la  sección  de  idiomas  propios  

 

2.  Selecciona  un  idioma  de  la  lista  de  idiomas  hablados  del  usuario  

 

3.  Solicita  eliminar  el  idioma       4.  Elimina  el  idioma  de  la  lista  de  

idiomas  hablados  del  usuario     5.  Muestra  una  mensaje  de  éxito      

3.4.11. Eliminar idioma practicado    Caso  de  uso   Eliminar  idioma  practicado  Actor   Usuario  Resumen   El  usuario  elimina  un  idioma  de  su  lista  

de  idiomas  practicados  Precondiciones   Debe  existir  algún  idioma  en  la  lista  de  

idiomas  practicados  del  usuario  Curso  típico  de  eventos  

Usuario   Sistema  1.  Accede  a  la  sección  de  idiomas  propios  

 

2.  Selecciona  un  idioma  de  la  lista  de  idiomas  practicados  del  usuario  

 

3.  Solicita  eliminar  el  idioma       4.  Elimina  el  idioma  de  la  lista  de  

idiomas  practicados  del  usuario     5.  Muestra  una  mensaje  de  éxito            

15    

3.4.12. Iniciar conversación con otro usuario    Caso  de  uso   Iniciar  conversación  Actor   Usuario  Resumen   El  usuario  comienza  una  conversación  

en  tiempo  real  con  otro  usuario  Precondiciones   Ninguna  

Curso  típico  de  eventos  Usuario   Sistema  

1.  Accede  al  perfil  de  un  usuario    2.  Solicita  iniciar  una  conversación       3.  Muestra  la  sección  de  chat       4.  Muestra  mensajes  antiguos  en  caso  

de  existir    

3.4.13. Enviar un mensaje    Caso  de  uso   Enviar  mensaje  Actor   Usuario  Resumen   Un  usuario  envía  un  mensaje  a  otro  en  

una  conversación  Precondiciones   Debe  haber  una  conversación  abierta  

Curso  típico  de  eventos  Usuario   Sistema  

1.  Abre  una  conversación  con  otro  usuario  

 

2.  Escribe  un  mensaje  en  la  conversación  

 

3.  Solicita  enviar  el  mensaje       4.  Muestra  el  mensaje  como  enviado  y  

aparece  en  pantalla  a  ambos  usuarios                          

16    

3.4.14. Mostrar conversaciones abiertas    Caso  de  uso   Mostrar  conversaciones  abiertas  Actor   Usuario  Resumen   El  usuario  solicita  visualizar  un  listado  

de  conversaciones  que  tiene  abiertas  Precondiciones   Ninguna  

Curso  típico  de  eventos  Usuario   Sistema  

1.  Accede  a  la  sección  de  conversaciones  

 

  2.  Muestra  un  listado  de  conversaciones  en  las  que  el  usuario  participa  

 

3.4.15. Organizar un evento    Caso  de  uso   Organizar  evento  Actor   Usuario  Resumen   El  usuario  crea  un  nuevo  evento  a  una  

hora  y  en  un  lugar  determinado  Precondiciones   Ninguna  

Curso  típico  de  eventos  Usuario   Sistema  

1.  Accede  a  la  sección  de  creación  de  evento  

 

2.  Selecciona  un  lugar,  un  título  y  una  fecha  para  el  evento  

 

3.  Solicita  crear  el  evento       4.  Valida  los  datos  introducidos  por  el  

usuario  y  crea  el  evento     5.  Muestra  el  detalle  del  evento  creado  

Flujo  alternativo  1     4.  [La  fecha  proporcionada  por  el  

usuario  es  una  fecha  ya  pasada]     5.  Muestra  un  mensaje  de  error                  

17    

3.4.16. Mostrar eventos cercanos    Caso  de  uso   Mostrar  eventos  cercanos  Actor   Usuario  Resumen   Se  muestran  los  eventos  aun  no  

celebrados  cercanos  a  la  posición  del  usuario  

Precondiciones   Ninguna  Curso  típico  de  eventos  

Usuario   Sistema  1.  Accede  a  la  sección  de  eventos    2.  Selecciona  un  rango  de  distancia  en  el  que  buscar  

 

  3.  Muestra  todos  los  eventos  no  celebrados  dentro  del  rango  seleccionado  

 

3.4.17. Mostrar evento    Caso  de  uso   Mostrar  evento  Actor   Usuario  Resumen   Visualizar  el  detalle  de  un  evento  Precondiciones   Ninguna  

Curso  típico  de  eventos  Usuario   Sistema  

1.  Accede  al  detalle  de  un  evento       2.  Se  muestran  los  datos  del  evento  por  

pantalla:  lugar,  fecha,  título  y  número  de  asistentes  

 

3.4.18. Apuntarse a un evento  Caso  de  uso   Apuntarse  a  un  evento  Actor   Usuario  Resumen   El  usuario  se  apunta  a  un  evento  Precondiciones   Ninguna  

Curso  típico  de  eventos  Usuario   Sistema  

1.  Accede  al  detalle  de  un  evento    2.  Solicita  apuntarse       3.  Añade  el  evento  a  la  lista  de  eventos  a  

los  que  asiste  el  usuario     4.  Muestra  la  lista  de  eventos  a  los  que  

asiste  el  usuario  

18    

3.4.19. Mostrar eventos apuntados    Caso  de  uso   Eventos  apuntados  Actor   Usuario  Resumen   Lista  de  los  eventos  a  los  que  el  usuario  

se  ha  apuntado  Precondiciones   Ninguna  

Curso  típico  de  eventos  Usuario   Sistema  

1.  Accede  a  la  sección  de  eventos  apuntados  

 

  2.  Muestra  una  lista  de  todos  los  eventos,  pasados  y  futuros,  a  los  que  el  usuario  se  ha  apuntado  

                                                               

19    

3.5. Modelo de datos  A  partir  de  los  casos  de  uso,  se  decidió  un  modelo  de  datos  para  crear  la  base  de  datos  con  la  que  debe  funcionar  el  sistema.  La  representación  gráfica  es  la  siguiente:    

 

                                 

20    

3.5.1. Tablas  A   continuación   se   definirán   las   tablas   que   componen   la   base   de   datos   y   sus  columnas.  Se  ha  obviado  en  todas  el  campo  de  identificación  única  (id).    Nombre   User  Columnas   username,  email,  password  Claves  foráneas   No  tiene  Descripción   Representa  al  usuario  del  sistema.  Su  gestión  y  creación  es  

responsabilidad  de  Django  (tecnología  de  servidor  que  se  ha  utilizado  y  que  se  detallará  más  adelante)  por  lo  que  existe  de  forma  obligatoria  y  permite  controlar  los  usuarios  y  las  sesiones.  

 Nombre   Profile  Columnas   picture,  description,  genre,  born_date  Claves  foráneas   user  Descripción   Es  una  ampliación  de  la  tabla  User.  Como  se  ha  comentado,  la  

tabla  User  la  proporciona  el  entorno  de  Django  y  ampliarla  supone  reescribir  su  código,  lo  cual  no  es  recomendable  ya  que  podría  generar  errores.    Almacena  datos  relevantes  del  usuario  como  si  descripción,  género,  fecha  de  nacimiento  y  la  URL  de  su  avatar.  

 Nombre   Language  Columnas   code,  flag,  name  Claves  foráneas   No  tiene  Descripción   Almacena  todos  los  idiomas  disponibles  en  el  sistema,  con  un  

código  internacional,  un  nombre  y  la  URL  de  la  imagen  de  la  bandera  que  lo  representa.  En  principio  es  una  tabla  únicamente  de  consulta,  no  debería  ser  alterada  tras  la  carga  inicial  de  datos.  

 Nombre   User_language  Columnas   type  Claves  foráneas   user,  language  Descripción   Es  una  tabla  intermedia  que  representa  los  idiomas  hablados  

y  practicados  por  un  usuario.  La  diferencia  entre  hablado  y  practicado  se  denota  por  la  columna  type.  Los  campos  user,  language  y  type  deben  ser  únicos  juntos.  

             

21    

Nombre   Meeting  Columnas   title,  time,  position  Claves  foráneas   creator  Descripción   Almacena  los  eventos  o  quedadas  propuestos  por  los  

usuarios.  En  cada  evento  se  guarda  su  título,  fecha  de  celebración  y  coordenadas  del  lugar  de  encuentro.  La  clave  foránea  creator  hace  referencia  al  usuario  que  ha  organizado  el  evento.  

     Nombre   User_attends_meeting  Columnas   No  tiene  campos  propios  Claves  foráneas   meeting,  user  Descripción   Esta  tabla  representa  las  asistencias  de  los  usuarios  a  los  

eventos.  Deben  ser  únicas  juntas  sus  dos  claves  foráneas:  meeting  y  user.  

 Nombre   Chat  Columnas   label  Claves  foráneas   user_from,  user_to  Descripción   Almacena  las  conversaciones  abiertas  entre  dos  usuarios  del  

sistema.    Nombre   Message  Columnas   message,  timestamp  Claves  foráneas   user,  chat  Descripción   Se  utiliza  para  guardar  un  registro  de  los  mensajes  que  envía  

cada  usuario  en  una  conversación.                                    

22    

3.6. Diseño de interfaz  Seguidamente  se  detallan   los  diseños   iniciales  de   interfaz  de  usuario,  basados  en  los  casos  de  uso  definidos,  que  se  crearon  para  tener  una  idea  más  clara  de  cómo  se  iba  a  implementar  la  herramienta  y  una  mejor  visión  de  sus  funcionalidades.    

3.6.1. Registro    

     Formulario  de  registro  al  sistema  con  los  campos  requeridos  para  dar  de  alta  a  un  usuario.                          

23    

3.6.2. Login    

     Esta   vista   muestra   la   pantalla   de   acceso   al   sistema   en   la   que   se   requieren   las  credenciales  de  usuario.                                        

24    

3.6.3. Perfil de usuario    

     Esta   vista   contiene   la   información   pública   de   un   usuario.   Como   se   ve,   en   la  izquierda   se   incluye   una   barra   lateral   para   que   actúe   a  modo   de  menú.   Esto   es  constante  en  todas  las  vistas  del  sistema.                            

25    

3.6.4. Perfiles afines    

     Se  trata  de  un  listado  de  perfiles  afines  al  usuario.                                    

26    

3.6.5. Edición de perfil    

     Esta  vista  es  similar  al  detalle  del  perfil  con  la  particularidad  de  que  los  datos,  que  son  los  propios  del  usuario,  son  editables.                          

27    

3.6.6. Detalle evento    

     Muestra   los   datos   relativos   a   un   evento   concreto.   Ofrece   la   posibilidad   de  apuntarse  a  la  asistencia  del  mismo.                              

28    

3.6.7. Listado de eventos    

     Se  muestra  una  lista  de  los  eventos  a  los  que  el  usuario  está  apuntado  o  ha  asistido.                                  

29    

3.6.8. Creación de evento    

     Esta   vista  muestra   un   formulario   que   recoge   los   datos   necesarios   para   crear   un  evento.  El  mapa  debe  permitir  señalar  un  lugar  de  celebración  para  el  evento.                              

30    

3.6.9. Eventos cercanos    

     En   esta   página   se   muestran   al   usuario   eventos   aun   no   celebrados   que   se  encuentran  cerca  de  su  posición.  Debe  permitir  acceder  al  detalle  de  cada  uno  de  los  mismos.                            

31    

3.6.10. Conversación    

     En  esta  vista  se  muestra  cómo  debe  ser  el  chat  entre  dos  usuarios.                                      

32    

3.7. Plan de pruebas  A  continuación  se  definen  los  distintos  conjuntos  de  pruebas  que  se  deben  realizar  en  el  proyecto  así  como  los  puntos  importantes  a  tener  en  cuanta  durante  el  desarrollo.    

3.7.1. Pruebas unitarias  Se  realizarán  a  lo  largo  del  desarrollo  del  back-­‐end  o  servidor.  Dentro  del  framework  Django  con  el  que  se  realizará  esta  parte,  se  hará  uso  del  módulo  unittest  de  Python  que  permite  escribir  pruebas  unitarias  sobre  la  aplicación.  Se  deberán  probar  las  funcionalidades  desarrolladas  y  hacer  énfasis  en  los  servicios  ofrecidos  por  la  API,  ya  que  es  una  pieza  fundamental  para  que  el  sistema  funcione.    

3.7.2. Pruebas de integración  Se  realizarán  en  la  finalización  del  desarrollo  de  cada  módulo  de  la  aplicación.  Deberán  probar  que  la  inclusión  de  un  nuevo  módulo,  ya  sea  un  servicio,  una  funcionalidad  específica,  o  un  API  endpoint;  no  afecta  a  los  anteriores,  y  todos  pueden  funcionar  e  integrarse  sin  afectar  al  resto  del  código.    

3.7.3. Pruebas de aceptación  Se  deben  realizar  desde  el  momento  en  que  la  aplicación  sea  usable,  aunque  no  ofrezca  el  total  de  sus  funcionalidades.  Un  usuario  debe  hacer  uso  de  la  aplicación  en  busca  de  errores  y  mejoras  de  la  misma.    

3.7.4. Puntos críticos  Los  puntos  críticos  son  aquellas  partes  de  la  aplicación  que  se  consideran  propensas  a  producir  errores  o  cuyo  desarrollo  abarca  una  cierta  cantidad  de  incertidumbre.  En  el  caso  de  este  proyecto  se  ha  considerado  tener  en  cuenta:    

-­‐ La  gestión  de  datos:  como  se  detalla  más  adelante,  el  motor  de  base  de  datos  escogido  es  PostgreSQL,  el  cual  nunca  antes  había  utilizado,  y  supone  una  gran  incertidumbre  a  la  hora  de  trabajar  en  integrar  con  la  aplicación.  

-­‐ Implementación  de  búsqueda  por  geo-­‐localización:  calcular  posiciones  cercanas  a  un  punto  dentro  de  un  radio  mientras  se  trabaja  con  numerosas  coordenadas  puede  resultar  un  procedimiento  muy  pesado  y  costoso,  por  lo  que  su  desarrollo  debe  cuidarse  especialmente.  

-­‐ Desarrollo  de  chat:  tanto  la  elección  de  tecnologías  para  llevar  a  cabo  este  módulo  como  el  problema  de  arquitectura  que  plantea,  suponen  una  incertidumbre  altísima.  

33    

3.8. Iteraciones  Con   las   funcionalidades   y   requisitos   definidos,   se   plantea   un   plan   de   iteraciones  para  realizar  el  desarrollo  del  proyecto  de  forma  incremental.  Todas  las  iteraciones  de  desarrollo  incluyen  documentar  y  la  realización  pruebas.  El  orden  de  planificación  refleja  una  aplicación  que  aumente  de  forma  incremental  sus   funcionalidades   de   forma   que   se   pueda   ir   probando   a   medida   que   éstas   se  añaden.   El   flujo   de   trabajo   es:   primero   servidor,   después   cliente   y   por   último   el  módulo  de  chat,  ya  que  no  es  imprescindible  para  que  el  resto  del  sistema  funcione  y  se  podría  interpretar  como  una  aplicación  aparte.    Las  iteraciones  del  proyecto  han  sido:    

-­‐ Creación  del  proyecto  en  Django  -­‐ Creación  del  modelo  de  datos  y  configuración  de  la  base  de  datos  -­‐ Implementar  funciones  de  usuario:  autenticación,  registro  y  salida  -­‐ API  endpoints  de  Profile  -­‐ API  endpoints  de  Language  -­‐ API  endpoints  de  Meeting  -­‐ API  endpoints  de  People  -­‐ Creación  proyecto  front-­‐end  -­‐ Interfaz  de  registro  y  acceso  -­‐ Interfaz  general  de  aplicación  e  implementar  menú  global  -­‐ Interfaz  de  perfil  de  usuario  -­‐ Interfaz  de  edición  de  perfil  -­‐ Interfaz  listado  de  perfiles  afines  -­‐ Interfaz  listado  y  detalle  de  quedadas  -­‐ Interfaz  creación  de  quedadas  -­‐ Interfaz  búsqueda  de  quedadas  por  geo-­‐localización  -­‐ Implementar  módulo  de  chat  en  cliente  y  servidor  

                                     

34    

4. Desarrollo del proyecto  

4.1. Conceptos importantes  A   continuación   se   detallan   una   serie   de   conceptos   que   son   esenciales   para  comprender  el  problema  de  la  arquitectura  del  proyecto  y  su  solución.    

4.1.1. API REST  El   término   REST   (REpresentational   State   Transfer)   da   nombre   a   un   estilo   de  arquitectura  de  desarrollo  Web  apoyado  en  el  estándar  HTTP.  Define  una  serie  de  principios  para  crear  y  trabajar  la  comunicación  del  cliente  con  el  servidor.    Permite  crear  aplicaciones  para  cualquier  cliente  que  entienda  el  protocolo  HTTP  y  es  actualmente  el  modelo  predominante  en  el  desarrollo  Web.  La  arquitectura  REST  describe  una  serie  de  puntos:  

-­‐ Cliente/servidor:   es   lo   que   define   la   arquitectura.   Cliente   y   servidor   son  dos   agentes   independientes   que   aportan   la   separación   entre   la   lógica   del  negocio  y  la  lógica  de  presentación.  

-­‐ Sin   estado:   el   servidor  no  mantiene  un  estado   relacionado  con  el   cliente,  por   lo   que   cada   petición   es   independiente   de   las   demás.   Aunque   esta  capacidad   es   muchas   veces   una   limitación,   y   existen   mecanismos   para  suplirla  (cookies).  

-­‐ Caché:   las   respuestas   que   recibe   el   cliente   del   servidor   se   pueden  almacenar.   Por   ello,   el   cliente   puede  utilizar   una   respuesta   y   apoyarse   en  ella  para  una  próxima  petición.  

-­‐ Operaciones:   se   establece   una   relación   entre   los   métodos   del   estándar  HTTP   (POST,   GET,   PUT,   DELETE)   y   las   operaciones   CRUD   (Create,   Read,  Update,  Delete;  Crear,  Leer,  Actualizar  y  Eliminar).  

-­‐ URI   y   recurso:   se  entiende  un  recurso  como  un  elemento  de   información  que  tiene  asociado  un  identificador  único  (URI,  Uniform  Resource  Identifier)  que  sigue  una  sintaxis  universal  e  intuitiva.  

-­‐ XML   y   JSON:   se   usa   alguno   de   estos   dos   formatos   para   transferir   la  información.  

 Por  otra  parte,  el  concepto  de  API  (interfaz  de  programación  de  aplicaciones)  es,  según  Wikipedia:   “el  conjunto  de  subrutinas,  funciones  y  procedimientos  que  ofrece  cierta  biblioteca  para  ser  utilizado  por  otro  software  como  una  capa  de  abstracción”.    Podemos  concluir  que  una  API  REST  es  una  librería  de  funciones  a  la  que  se  accede  a  través  del  protocolo  HTTP  mediante  URLs  en  las  que  se  envían  los  datos  de  una  consulta  y  se  recibe  información  en  formato  XML  o  JSON.        

35    

4.1.2. Websocket  La  arquitectura  cliente-­‐servidor  o  petición-­‐respuesta  sobre   la  que  funciona  HTTP  presenta   una   serie   de   inconvenientes   en   el   desarrollo   de   cierto   tipo   de  aplicaciones.  La   conexión   dirigida   por   el   cliente   no   permite   desarrollar   aplicaciones   de   baja  latencia,   que   necesiten   una   comunicación   casi   constante   e   instantánea   entre   el  cliente  y  el  servidor.    Para  cubrir  esa  necesidad  se  puede  hacer  uso  de  websockets.    Websocket   es   una   tecnología   que   permite   abrir   un   canal   de   comunicación  bidireccional   entre   el   cliente   y   el   servidor.   El   cliente   puede   enviar   mensajes   y  recibir  respuestas  controladas  por  eventos  sin  tener  que  consultar  al  servidor.  Esto  es:  una  conexión  persistente  entre  ambas  partes  en  la  que  se  pueden  enviar  y  recibir  datos  en  cualquier  momento.    Esta  tecnología  es  una  evolución  necesaria  de  técnicas  como  polling  y  long  polling.  Long  polling   es   un  modelo  de   aplicación  Web  en   el   que   se  mantiene   abierta  una  petición  HTTP  por  parte  del   cliente  en  el   servidor  a   la  espera  de  que  el   servidor  necesite  enviar  información  al  cliente.  Una  vez  la  envíe,  el  cliente  volverá  a  hacer  una  petición  de  nueva  información  y  quedará  a  la  espera.      Polling   es   una   técnica   de   consulta   constante:   cada   cierta   cantidad   de   tiempo   el  cliente   pregunta   al   servidor   por   nueva   información.   Y   éste   responde   con   nueva  información  en  caso  de  existir  o  con  una  respuesta  vacía.  Ambos  casos,  sobre  todo  polling,  suponen  una  gran  cantidad  de  peticiones  HTTP,  lo  cual   supone   un   gasto   muchas   veces   innecesario   de   recursos,   una   posible  sobrecarga  de  conexiones  y  da  pie  a  problemas  de  seguridad:  cada  petición  es  una  conexión  TCP  por   lo   que  hay  mayor   probabilidad  de   recibir   ataques  man-­‐in-­‐the-­‐middle.    Websocket  soluciona  estos  problemas:  una  vez  se  realiza  la  conexión  entre  cliente  y  servidor,  ésta  es  estable  y  es  más  difícil  de  interceptar.  Ésta  conexión  no  es  HTTP,  solamente  lo  es  el  proceso  de  handshake  o  negociación.      En   éste   proyecto   se   ha   optado   por   el   uso   de   la   tecnología   websocket   por   las  ventajas   que   ofrece:   información   push   desde   el   servidor   sin   necesidad   de  requerirla,  necesita  de  una  sola  conexión  y  su  sencilla   implementación  en  el   lado  del  cliente,  que  está  incorporada  en  el  estándar  de  HTML5.  Además,  una  conexión  por  websocket  requiere  un  menor  tráfico  de  datos,  ya  que  las  cabeceras  de  los  mensajes  son  mucho  menores  que  las  de  una  petición  HTTP,  lo  cual   es   muy   importante   teniendo   en   cuenta   que   se     espera   una   cantidad   de  mensajes  elevada.                  

36    

 

4.2. Arquitectura  Al   tratarse   de   un   proyecto   orientado   a   la  Web,   se   puede   dividir   en   dos   grandes  bloques:  servidor  y  cliente.  El  servidor  se  encargará  de  toda  la   lógica  de  negocio  mientras  que  el  cliente  sólo  debe  interactuar  con  el  servidor  y  presentar  los  datos.    

   Tenemos   por   un   lado   un   cliente   Web   que   se   ejecuta   en   los   navegadores   de  cualquier   dispositivo,   ya   sea   ordenador,   móvil   o   tablet;   y   éste   consulta   datos   e  interacciona  con  el  servidor.    En  el  servidor  realmente  está  incluida  toda  la  lógica  de  negocio,  la  exposición  de  la  API  REST  y  la  base  de  datos.  En   el   caso   de   este   proyecto,   existe   la   dificultad   de   desarrollar   un   chat   o  conversación   en   tiempo   real.   La   necesidad   de   una   conexión   no   dirigida   por   el  cliente  es  la  que  impulsa  la  idea  de  utilizar  una  tecnología  como  los  websockets,  que  comunicará  al  cliente  con  el  servidor  de  forma  complementaria  a  la  conexión  HTTP  que  hace  uso  de  la  API.    

37    

       Ahora   se   puede   visualizar   una   estructura  más   clara   de   la   forma   de   trabajar   con  websockets  y  peticiones  HTTP  en  paralelo.  Esta  arquitectura  es  válida,  por  ejemplo,  para  el  cliente,  que  se  puede  abstraer  de  lo  que  ocurre  en  el  servidor.  Pero,  como  se  verá  más  adelante,  en  la  implementación  del  Chat,  no  es  definitiva,  ya   que   la   inclusión  de  websockets   añade   cierta   complejidad   a   la   arquitectura  del  servidor.                                  

38    

4.3. Servidor y API REST  

4.3.1. Herramientas  Se   procede   a   detallar   las   herramientas   de   software   que   han   sido   utilizadas   para  llevar  a  cabo  el  desarrollo  de  la  parte  de  servidor  y  la  API  REST:    Django   es   la  pieza   fundamental   del   desarrollo  del   servidor.  Es  un   framework   de  desarrollo   de   aplicaciones   Web   escrito   en   Python   y   mantenido   por   Django  Software  Corporation.    Permite   construir   aplicaciones   escalables     respetando   el   patrón   modelo-­‐vista-­‐controlador.  Como  otros   frameworks  de  desarrollo  Web,  permite  al  programador  abstraerse  de   ciertos   aspectos   como   las   conexiones  de   red   o   base  de  datos   para  centrarse  únicamente  en  el  desarrollo  del  negocio.  Django  provee  un  ORM    (Object-­‐Relational   Mapping,   mapeo   objeto-­‐relacional)   que   permite   usar   los   datos  persistentes  en  la  base  de  datos  como  objetos  tipados  en  Python.  En  este  proyecto  se  ha  utilizado  en  su  versión  1.9.7,  la  más  reciente  al  momento  de  iniciar  el  proyecto.    Django-­‐Rest-­‐Framework   es   una   librería   escrita   en   Python   y   pensada   para  incluirse  en  proyectos  de  Django.  Esta   librería   se  ha  utilizado  para  desarrollar   la  API  REST.      Cabe   destacar   que   Django,   al   igual   que   muchos   frameworks,   está   pensado   para  recibir   una   petición   HTTP   y     renderizar   y   devolver   una   plantilla   HTML   cuyo  contenido   se   decide   en   función   de   la   petición.   Pero   hoy   en   día   esa   forma   de  trabajar  es  cada  vez  menos  usada  por  lo  pesada  que  resulta.      En  cambio,  la  Web  está  más  orientada  a  las  Single  Page  Applications  (aplicaciones  de  una  sola  página):  una  sola  plantilla  HTML  que  contiene  la  lógica  necesaria  para  interactuar  con  el  usuario,  pedir  al  servidor  a  través  de  una  API  REST  los  datos  que  vaya  necesitando  y  repintar  la  vista  cuando  los  reciba.  De  esta  manera  sólo  hay  una  carga  inicial  de  archivos  estáticos  HTML,  CSS  y  JavaScript.    Esta  ha  sido  precisamente  la  manera  de  trabajar  en  el  proyecto:  una  petición  inicial  a   través  del   sistema  de  plantillas   de  Django   en   la   que   se   devuelve   la   página   y,   a  partir  de  ahí,  el  cliente  solo  interactúa  con  el  servidor  pidiendo  o  enviando  datos  a  través  de  la  API.    La  base  de  datos  que  se  ha  utilizado  es  PostgreSQL,  un  sistema  de  gestión  de  bases  de  datos  relacionales  distribuido  bajo  licencia  BSD.    El   framework   Django   posee   un   módulo   propio   llamado   GeoDjango   que   actúa  como   API   geográfica   dentro   del   modelo   de   clases   de   Django.   Es   decir,   permite  trabajar   con  objetos   situados  en  un  punto  del   espacio  de  manera  muy  abstracta.  Esta  funcionalidad  ayudaría  a  resolver  el  problema  de  la  geolocalización  que  se  ha  planteado  en  los  requisitos.    

39    

El  uso  de  GeoDjango  es  el  que  ha  impulsado  la  elección  de  PostgreSQL  como  motor  de   base   de   datos,   ya   que   el   desarrollo   de   esta   librería   se   hizo   basándose   en  PostGis,  una  extensión  de  PostgreSQL  que  añade  soporte  para  objetos  geográficos  y  permite  consultas  de  localización.    Añadir   que   el   desarrollo   de   esta   parte   del   proyecto   se   ha   realizado   con   la  herramienta   PyCharm   de   JetBrains,   diseñada   para   gestionar   el   desarrollo   de  proyectos  en  Python.    

4.3.2. API REST y recursos  A  partir  de  las  funcionalidades  definidas  anteriormente  y  el  modelo  de  datos  que  conforma  el  proyecto,  se  han  decidido  una  serie  de  recursos  que  componen  la  API  REST:    Profile   Maneja  operaciones  GET  y  PUT/PATCH.  Representa  la  unión  

de  las  tablas  User  y  Profile.  Se  hará  uso  del  recurso  tanto  para  recuperar  información  sobre  perfiles  como  para  su  edición.    

Login,  logout,  registration  

Estos  recursos  no  están  directamente  asociados  a  un  dato  o  tabla  de  la  base  de  datos.  Se  utilizan  para  el  manejo  de  sesiones  de  cada  usuario  por  lo  que  no  representan  una  entidad  única  sino  que  el  resultado  de  su  uso  puede  variar  en  función  del  cliente  y  su  estado.  

Me   Al  igual  que  los  anteriores,  el  uso  de  este  recurso  no  representa  una  entidad  única  y  depende  del  cliente  que  lo  use.  Su  función  es  devolver  datos  de  las  tablas  User  y  Profile  que  pertenezcan  únicamente  al  usuario  que  realiza  la  petición.  

Language   Representa  las  filas  de  la  tabla  Language.  Son  cada  una  de  las  lenguas  que  hay  almacenadas  en  el  sistema  y  trabaja  únicamente  como  recurso  de  consulta  (operaciones  GET).  

Languages-­‐speak,  languages-­‐practice  

Este  recurso  va  asociado  a  la  relación  que  existe  entre  User  y  Language.  Es  decir,  los  idiomas  que  habla  y  practica  cada  usuario.  A  través  de  este  recurso  se  pueden  crear,  consultar  y  eliminar  estas  relaciones.  

Meeting   Representa  los  eventos  de  la  tabla  Meeting.  Se  utiliza  este  recurso  para  consultar  y  crear  eventos  desde  el  cliente.  

Attendance   Son  las  asistencias  de  los  usuarios  a  un  evento,  la  tabla  intermedia  entre  Meeting  y  User.  

Chat   Este  recurso  representa  las  conversaciones  o  chats  entre  usuarios  y  su  uso  es  de  consulta.  

Related   Este  recurso  tampoco  representa  una  entidad  única  en  la  base  de  datos.  Se  utiliza  para  devolver  datos  de  la  tabla  Profile,  que  sean  los  perfiles  que  el  sistema  considere  afines  al  usuario  y  que  son  variables.  

   

40    

La  implementación  de  la  API  REST  se  ha  complementado  con  una  documentación  intuitiva  en  forma  de  tablas  para  hacer  uso  de  los  recursos.  Esta  documentación  se  ha  incluido  como  un  apéndice  al  final  del  documento  debido  a  su  extensión.  La  forma  de  la  misma  es  la  siguiente:    REQUEST  URL   /api/1.0/example/  REQUEST  TYPE   POST  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   None  EXAMPLE   {“name”:  “example”}    

-­‐ Request  URL:  es  la  dirección  o  URI  sobre  la  que  se  realiza  la  petición.  -­‐ Request  type:  el  tipo  de  operación  que  se  puede  realizar  sobre  la  URL  para  

el  ejemplo  explicado.  -­‐ Request  format:  tipo  de  formato  en  que  se  envían  y  reciben  los  datos.  -­‐ Request   authentication:   tipo   de   autenticación   necesaria   para   realizar   la  

petición  con  éxito.  Normalmente  se  pedirá  tener  una  sesión  activa.  -­‐ Example:  un  ejemplo  de  datos  a  enviar  para  realizar  la  petición.  

 La   petición   debe   devolver   una   respuesta   con   un   código   HTTP   y   la   información  requerida  en  formato  JSON:    HTTP  201  CREATED  {“id”:  1,  “name”:  “example”}      

4.3.3. Implementación de la API  A   modo   de   ejemplo   práctico,   se   va   a   explicar   una   pequeña   parte   de   la  implementación  de   la  API  REST,  para  enseñar   la   forma  de   trabajar   con  Django  y  Django-­‐Rest-­‐Framework.    Nos   vamos   a   centrar   en   el   recurso   Meeting,   que   hace   referencia   a   los   eventos  organizados  por  los  usuarios.    Lo   primero   de   todo   es   mostrar   cómo   se   representa   este   dato   en   el   sistema,   en  forma  de  objeto  Python  a  través  del  ORM  que  proporciona  Django:      class  Meeting(models.Model):            

title  =  models.CharField(max_length=100,  blank=False,  null=False)          

position  =  models.PointField(null=False,  blank=False)            

time  =  models.DateTimeField(null=False,  blank=False)            

creator  =  models.ForeignKey(User,  related_name='creator',  null=True)            

objects  =  models.GeoManager()              

 

41    

Esta  definición  de  la  clase  Meeting  vemos  que  hereda  de  Model  (una  clase  provista  por  el  ORM  de  Django).  Esto  es  lo  que  lo  convierte  en  un  objeto  mapeable  para  el  ORM  que  tiene  que  cumplir  una  serie  de  características  en  sus  atributos.  La  sintaxis  es  muy  sencilla  y  se  identifican  fácilmente  los  campos  que  componen  la  tabla:  title,  position,  time  y  creator.  El  atributo  objects  no  forma  parte  de  la  tabla  y  no  se  suele  usar  si  no  es  necesario:  está  diciéndole  al  manejador  del  ORM  que  es  un  objeto   que   contiene   atributos   de   geolocalización   y   que,   por   tanto,   va   a   tener   un  comportamiento  especial.    Vista   la   definición   de   Meeting,   ahora   podemos   entender   mejor   cómo   se   ha  construido  la  API.  Se   debe   definir   una   vista   de   Django   (que   es   realmente   un   controlador)   que  represente   el   endpoint   que   queremos   construir   en   torno   a   un   recurso,   de   la  siguiente  manera:    class  MeetingViewSet(MultipleSerializersViewSet,  RetrieveModelMixin,  

CreateModelMixin,  ListModelMixin,  DestroyModelMixin):            

  queryset  =  Meeting.objects.all()            

  serializer_class  =  MeetingSerializer            

  permission_classes  =  (IsAuthenticated,  MeetingPermission)  

 Esta  clase  representa  ese  endpoint.    Haciendo  caso  primero  a  las  clases  de  las  que  hereda:  

-­‐ MultipleSerializerViewSet:   es   una   clase   propia   que   realmente   hereda   de  GenericViewSet,  la  cual  es  proporcionada  por  Django-­‐Rest-­‐Framework  y  que  provee  el  comportamiento  base  necesario  para  construir  un  ViewSet,  o  una  vista    (controlador  de  Django)  orientada  a  API.  

-­‐ RetrieveModelMixin,   CreateModelMixin,   ListModelMixin,   DestroyModelMixin:  estas  clases  sirven  para  implementar  los  métodos  que  se  van  a  permitir  en  la   API   de   forma   funcional.   En   este   caso   son   creación,   detalle,   listado   y  eliminación.   Proveen   los  métodos   create,   retrieve,   list   y   destroy   que,   si   es  estrictamente   necesario,   se   pueden   sobrescribir   para   cambiar   su  comportamiento  por  defecto,  como  veremos  más  adelante.    

 Siguiendo  con  los  atributos  de  la  clase,  queryset  hace  referencia  a  la  clase/modelo  que  se  ha  definido  anteriormente  (Meeting),  relacionando  con  qué  objetos  del  ORM  se  va  a  trabajar  en  este  endpoint.    

42    

En   serializer_class   se   establece   qué   clase   se   debe   utilizar   para   serializar   los  objetos.   En   ésta   clase   se   define   la   transformación   del   objeto   JSON   a   Python   o  viceversa.  También  se  especifica  la  validación  y  los  campos  que  se  deben  incluir.  El  serializador  es  el  responsable  de  convertir  los  datos  entrantes  a  un  formato  acorde  con  el  ORM  y  de  validar  los  datos  de  acuerdo  a  las  restricciones  del  modelo.    Como  ejemplo,  ya  que  el  serializador  de  Meeting  es  más  complejo  de  lo  habitual  y  no   resultaría   didáctico,   vamos   a   ver   un   serializador   sencillo   encargado   de   los  objetos  User:    class  UserSerializer(serializers.ModelSerializer):            

  class  Meta:                    

    model  =  User                    

    fields  =  ('id',  'email',  'username')      En   esta   implementación   se   observa   que   se   trata   de   una   clase   que   hereda   de  ModelSerializer  y  en  la  que  se  especifica  un  modelo:  User.  Con  estos  datos,  no  hace  falta   definir   más,   ya   que   ModelSerializer   permite   que   se   mapeen   los   campos  entrantes,  en  función  de  su  nombre,  con  los  atributos  de  la  tabla  o  modelo  User,  y  validarlo  en  función  de  las  restricciones  con  que  se  haya  definido.  Además  se  especifica  qué  campos  deben   ir   incluidos  para  el  uso  que  se   le  de  (en  este   caso   es   de   solo   lectura,   lo   que   significa   que   devolvería   un   JSON   con  únicamente  los  campos  id,  email  y  username).    El  último  aspecto  de   la  vista  a  explicar  es  el  atributo  permission_classes,  el  cual  hace  referencia  a  en  qué  clases  se  debe  delegar  el  manejo  de  permisos  para  hacer  uso  del  endpoint  que  se  está  definiendo.  La  primera,  IsAuthenticated,  es  propia  de  la   librería  y   sirve  apara  asegurarse  que  el  usuario  que  hace   la  petición  mantiene  una  sesión  activa  en  el  sistema.  Veamos  la  otra  clase,  que  es  propia  de  este  sistema:    class  MeetingPermission(CustomActionPermissions):              

  def  has_object_permission(self,  request,  view,  obj):                    

    if  request.user.is_superuser:                            

      return  True                    

    if  view.action  ==  'destroy':                            

      return  obj.creator  ==  request.user                    

    return  True    En   esta   clase   se   definen   los   permisos   de   tal   manera   que   cualquier   acción   está  permitida   en   caso   de   que   el   usuario   sea   administrador   del   sistema.   Y   cualquier  operación  está  admitida  a  excepción  de   la  de  eliminación:  sólo  se  puede  eliminar  un  recurso  (en  este  caso  un  evento  o  Meeting)  si  el  usuario  que  realiza  la  petición  es  el  creador  de  ese  objeto.    

43    

Por  último,  mostrar  un  de  los  métodos  del  API  Endpoint  que  se  está  construyendo:  el   de   crear.   Como   ya   se   ha   explicado,   no   suele   ser   necesario   sobrescribir   estos  métodos,   pero   en   este   caso   se   ha   necesitado   debido   al   distinto   comportamiento  que  tienen  los  objetos  con  atributos  de  geo-­‐localización.  En  cuanto  al  método  en  sí,  no  aporta  ninguna  lógica  extra  al  comportamiento  de  la  creación,  por  lo  que  sirve  bien  de  ejemplo  para  ver  cómo  funciona:    def  create(self,  request,  *args,  **kwargs):                    

serializer  =  self.get_serializer(data=request.data)                  

serializer.is_valid(raise_exception=True)                      

meeting  =  Meeting.objects.create(  

title=serializer.validated_data.get('title'),                                                                                    

position=serializer.validated_data.get('position'),                                                                                    

time=serializer.validated_data.get('time'),  creator=request.user)        

 

serializer  =  MeetingSerializer(meeting)                    

  headers  =  self.get_success_headers(serializer.data)                    

  return  Response(serializer.data,  status=status.HTTP_201_CREATED,          

headers=headers)      El  curso  de  acciones  es:  se  recuperan  los  datos  enviados  por  el  cliente  y  se  delegan  en  el  serializador  correspondiente;  se  comprueba  que  sean  válidos  y  se  espera  una  excepción  en  caso  contrario;  se  instancia  un  objeto  Meeting  haciendo  uso  del  ORM  que   lo   almacena   en   la   base   de   datos   (Meeting.objects.create).   Se   construye   un  serializador  con  los  datos  del  objeto  creado;  se  definen  las  cabeceras  de  respuesta  y,  por  último,  se  devuelve  una  respuesta  HTTP  con  cabeceras  de  éxito  y  los  datos  del  serializador  que  se  ha  construido.        Con   todo   lo   anterior   se   puede   dar   casi   por   finalizada   la   construcción   de   un   API  Endpoint.  Sólo  falta  unir  las  piezas:  asignar  una  URL  que  va  a  ser  pública.    En   un   archivo   api_urls.py   se   define   un   Router   que   registra   el   recurso   que   se   ha  creado:    router  =  SimpleRouter()    

router.register(r'meetings',  MeetingViewSet,  base_name='meetings')  

urlpatterns  =  router.urls  

 

 

 

 

44    

Por  último,   en   el   archivo  urls.py  del  proyecto,   que   es  donde   se  definen   las  URLs  que  Django  debe  servir,  incluimos:    from  meeting  import  api_urls  as  meeting_api_urls      

urlpatterns  =  [          url(r'^api/1.0/',  include(meeting_api_urls)),  ]  

 Con  lo  que  ya  es  usable  el  endpoint,  realizando  peticiones  a  la  URL:        host/api/1.0/meetings/            

4.3.4. Pruebas    Para   la   implementación   de   la  API  REST   se   han   ido   realizando  pruebas   de   forma  incremental  a  medida  que  se  implementaban  funcionalidades.  Los   tests   se   han   centrado   en   probar   todos   los   aspectos   de   cada   API   Enpoint:  permisos,   autenticación,   datos  mal   enviados,   datos   inválidos,   persistencia   de   las  acciones…    El   módulo   unittest   de   Python   permite   la   realización   de   tests   unitarios   en   una  aplicación  mediante   la   instanciación   de   clases   TestCase   y  métodos   de   la   clase   a  modo  de  test.    Un  ejemplo  de  implementación  de  estas  pruebas  es  el  siguiente:    class  TestMeetingCreationAPI(TestCase):            

  urls  =  'meeting.api_urls'      

def  test_user_not_authenticated(self):                    

self.create_user(username='username',  password='password')                    

client  =  APIClient()                      

time  =  datetime.datetime.now()  +  datetime.timedelta(days=5)                    

data  =  {"title":  "Meeting",  "position":  "POINT(40.383333  -­‐3.716667)",                                                      

"time":  time.strftime("%Y-­‐%m-­‐%dT%H:%M")}                      

response  =  client.post("/meetings/",  data,  format='json')                  

self.assertEqual(response.status_code,  status.HTTP_403_FORBIDDEN)    El  test  recrea  una  petición  de  tipo  POST  al  recurso  Meeting,  pero  lo  hace  mediante  un   cliente  no   autenticado,   por   lo   que   espera  una   respuesta  HTTP  de   código  403  (prohibido).      

45    

4.4. Servidor de chat  En  esta  sección  se  trata  de  explicar  cómo  se  ha  solucionado  la  implementación  de  un  chat  en  tiempo  real  y  cómo  ha  afectado  a  la  arquitectura  del  sistema.    

4.4.1. Django Channels  La   mayoría   de   los   frameworks   de   desarrollo   Web,     entre   ellos   Django,   están  construidos   en   torno   al   paradigma   de   la   filosofía   HTTP:   cliente   pide   –   servidor  responde.   Esto   plantea   un   claro   problema   a   la   hora   de   desarrollar   aplicaciones  complejas  que  necesiten  de  una  baja   latencia  o  de  una   comunicación  no  dirigida  por  el  cliente.    Channels   es   un   módulo   desarrollado   para   Django   que   trata   de   solucionar   ese  problema:  provee  un  marco  de  trabajo  en  el  que  se  pueden  gestionar  conexiones  con  Websockets  y  HTTP2  (conexiones  permanentes)  así  como  el  manejo  de  tareas  asíncronas.  Esto   se   suma   al   manejo   de   HTTP   que   siempre   ha   ofrecido   Django   y   cuyo  comportamiento  no  se  ve  variado  con  la  inclusión  de  Channels.    Algunos   de   los   conceptos   que   se   manejan   en   Channels   y   que   son   clave   para  entender  su  implementación  son:    Producer   (productor):  son  eventos  que  deben  ser  escuchados  y  que  transportan  un   mensaje.   Estos   eventos   suelen   ser   una   conexión   desde   un   Websocket,   un  mensaje  desde  esa  conexión  o  una  desconexión.  Consumer  (consumidor):  se  trata  de  un  proceso  o  función  en  Python  pensada  para  recibir   un   mensaje   y   ejecutarse   de   forma   asíncrona,   en   segundo   plano,   en   el  sistema.    Channel  (canal):  es  en  esencia  una  cola  de  tareas.  Escucha  mensajes  enviados  por  los  producers  y  delega  su  acción  en  el  proceso  llamado  consumer.  La  capa  de  canales,  o  channel  layer  es  el  mecanismo  de  transporte  que  Channels  usa   para   pasar   los   mensajes   de   los   producers   a   los   consumers,   es   decir,   la   que  maneja   los  channels.  Esta  capa  está  pensada  para  ser  usada  con  Redis  como  base  de  datos.    Group  (grupo)  es  una  clase  introducida  por  Channels  para  manejar  las  respuestas  múltiples.   Los   canales   solo   entregan   mensajes   a   un   único   destinatario   que   está  escuchando,   por   lo   que   intentar   hacer   broadcast   implicaría   enviar   el   mismo  mensaje  a  numerosos  destinatarios.  Este  problema  se  suple  con  el  uso  de  grupos:  un  usuario  se  suscribe  a  un  grupo  y  cuando  se  envíe  un  mensaje  desde  ese  grupo,  todos  los  usuarios  suscritos  lo  recibirán.  Lo  que  está  haciendo  Channels  por  debajo  es   simplemente   evitar   al   desarrollador   iterar   sobre   todos   esos   usuarios   para  enviar  un  mismo  mensaje.      

46    

4.4.2. Implementación  En  este  proyecto  se  ha  trabajado  con  cuatro  producers  o  eventos  a  los  que  escucha  la  channel  layer,  definidos  en  un  archivo  de  enrutamiento  routing.py:    channel_routing  =  {            

'http.request':  StaticFilesConsumer(),            

'websocket.connect':  consumers.ws_connect,            

'websocket.receive':  consumers.ws_receive,            

'websocket.disconnect':  consumers.ws_disconnect,  }    El   primero   es   el   que   delega   en   un   consumer   las   peticiones   HTTP   básicas.  Funcionará   de   forma   síncrona   y   en   un   proceso   aparte,   para   mantener   el  comportamiento  básico  de  Django.    Los   otros   tres   se   refieren   a   mensajes   provenientes   de   websockets:   delegan   la  conexión,   desconexión   y   envío   de  mensajes   en   distintos   procesos   que   serán   los  consumers.    La   forma   en   que   se   configura   Django   para   habilitar   esta   channel   layer   es   la  siguiente,   dentro   del   archivo   settings.py   que   contiene   todas   las   configuraciones  necesarias  para  un  proyecto:    CHANNEL_LAYERS  =  {            

"default":  {                    

"BACKEND":  "asgi_redis.RedisChannelLayer",                    

"CONFIG":  {                            

"hosts":  ["redis://localhost:6379"],                  },                    

"ROUTING":  "lingvo.routing.channel_routing",          },    

}      Como  se  aprecia,  esta  capa  va  asociada  al  enrutamiento  anteriormente  descrito  y  a  una  base  de  datos  Redis.  Redis  es  un  motor  de  bases  de  datos,  que  guarda  en  memoria  estructuras  de  datos  en   forma   de   clave-­‐valor   y   que   es   usada   en   esta   capa   a  modo   de   cola   de   tareas,  según  los  mensajes  vayan  llegando.                    

47    

Veamos  la  implementación  de  uno  de  los  consumers,  concretamente  el  que  recibe  un  mensaje  de  una  sala  de  chat:    @channel_session_user def ws_receive(message): label = message.channel_session['room'] chat = Chat.objects.get(label=label) data = json.loads(message['text']) if data: Message.objects.create(chat=chat, =data['message'],

user=message.user) Group('chat-' + label, channel_layer=message.channel_)

.send({"text": str(message.user.id) + ": " + data['message']})

   Este   consumer,   que   es   una   función   de   Python,   tiene   una   anotación  (@channel_session_user)   que   evita   que   se   ejecute   en   caso   de   que   el   usuario   que  envía  el  mensaje  no  esté  autenticado.  Recibe  un  mensaje  con  los  datos  de  la  sala  (Chat)  en  la  que  participa  el  usuario  y  el  mensaje  que  ha  enviado.  Mediante  Message.objects.create()   se   añade   el   mensaje   a   la   base   de   datos,   para  tener  un  historial.  Por  último,  se  accede  al  grupo  asociado  a  esa  conversación  y  se  emite,  mediante  el  método  send(),  el  mensaje  enviado  a  todos  los  usuarios  suscritos  al  grupo,  es  decir,  los  participantes  de  la  conversación.        

4.4.3. Arquitectura final  Una   vez   explicados   los   conceptos   básicos   de  Django   Channels   y   cómo   se   trabaja  con  ellos,  es  necesario  exponer  cómo  afecta  esta  forma  de  trabajar  a  la  arquitectura  y  por  qué  se  ha  cambiado.    La  forma  de  funcionar  que  ha  tenido  Django  siempre  ha  sido  orientada  a  la  filosofía  HTTP:   recibe   una   petición,   la   resuelve   en   tiempo   de   ejecución   y   devuelve   una  respuesta  HTTP,  como  se  ve  en  la  imagen:  

48    

 Recuperado  de:  https://blog.heroku.com  

 De   esta   manera   la   lógica   del   negocio   queda   delegada   en   funciones   view   y   es   el  proceso  de  Django  corriendo  en  el  servidor  (proceso  runserver)  el  que  maneja  los  protocolos  de  petición  y  respuesta.    La  inclusión  de  Django  Channels  cambia  esta  vista:    

49    

 Recuperado  de:  https://blog.heroku.com  

 El  esquema  ahora  se  ha  ampliado  para  poder  separar  responsabilidades.      Echando  un  vistazo  al   rectángulo   inferior,  para  hacer  más  comprensible  el   resto:  ahora   todos   los  procesos  se  ejecutan  como  workers,  de  modo  que  cada  uno  tiene  una  responsabilidad  única.  En  el  esquema  vemos  que  hay  un  proceso  orientado  a  manejar   las  peticiones  HTTP   (que   cumple   el   comportamiento  básico  de  Django),  otro   para   manejar   los   mensajes   de   los   websockets,   y   otros   relacionados   con  procesos  en  segundo  plano.    Una  vez  aclarada  esta  separación  de  responsabilidades,  veamos  el  funcionamiento  de  arriba  abajo:    Desde  un  navegador  Web  o  browser  se  envía  una  petición  al  servidor.  Ésta  puede  ser   a   través   de  HTTP   o   con   una   conexión   iniciada   por   un  websocket.   Por   ello   la  

50    

necesidad  de  un  interface  server  (servidor  de  interfaz).  Este  servidor  es  realmente  un   proceso   encargado   de   transformar   cualquier   tipo   de   conexión   entrante   en  mensajes  que  van  a  los  canales  y  despacharla  según  su  origen.  La   capa   de   canales   o   channel   layer   es   la   encargada   de   comunicar   el   servidor   de  interfaz   con   los   workers,   haciendo   uso   de   Redis   para   almacenar   los   mensajes  encolados.  Por   último,   los  workers   escuchan   los  mensajes   que   les   delega   la   capa  de   canales  para  ejecutar  los  consumers  correspondientes  cuando  reciben  el  mensaje.    Evidentemente  toda  esta  comunicación  es  bidireccional  por  lo  que  la  conexión  de  vuelta  al  cliente  recae  finalmente  en  el  servidor  de  interfaz.      Esto  afecta  a  la  arquitectura  del  proyecto  y  debe  ser  adaptada  a  esta  filosofía.    En  primer  lugar,  se  necesita  dentro  del  servidor  un  proceso  que  haga  de  servidor  de   interfaces.   Para   ello   se   utiliza   Daphne,   un   servidor   automático   de   protocolo  desarrollado  precisamente  para  Channels  .  Para  iniciar  el  proceso:    

daphne lingvo.asgi:channel_layer --port 8888

También  es  necesario  tener  en  el  servidor  una  base  de  datos  de  Redis  corriendo,  independientemente  de  PostgreSQL  que  también  debe  estar  en  funcionamiento:       redis-­‐server    En   tercer   lugar,   un   proceso   que   haga   de   worker.   Es   posible   instanciar   varios  workers  para  separar  procesos  en  caso  de  necesitar  mayor  rendimiento  pero  en  el  caso  de  este  proyecto  no  ha  sido  necesario:       python manage.py runworker--settings=lingvo.settings    Por  último  se  ha  instanciado  un  proceso  de  la  siguiente  manera:    

python manage.py runserver --noworker --settings=lingvo.settings

El   proceso   runserver   es   el   que   se   utiliza   en   Django   por   defecto   para   correr   el  servidor.   Como   se   ha   explicado,   ahora   esa   responsabilidad   recae   en   los  workers,  pero  debido  a  la  importancia  de  runserver,  es  recomendable  delegarlo  a  un  proceso  aparte   dedicado   exclusivamente   a   resolver   las   peticiones   que   llegan   por   HTTP.  Esto  se  especifica  con  el  argumento  –noworker.      Con   todo   lo   anterior,   la   arquitectura   final   del   proyecto   queda   reflejada   de   esta  manera:    

51    

                     

52    

4.5. Cliente Web  Además  de  la  parte  de  servidor,  en  este  proyecto  se  ha  desarrollado  una  aplicación  Web  que  hace  uso  del  mismo  y  orientada  a  ser  usada  en  navegadores.    Se   ha  desarrollado   como  una  Single  Page  Application,   es   decir,   una   aplicación  de  una  sola  página  que  contiene  cierta  lógica  de  presentación  y  que  solo  contacta  con  el  servidor  para  pedir  o  enviar  datos,  pero  no  para  cargar  archivos  estáticos  como  HTML,  CSS  o  JavaScript  después  de  la  carga  inicial.    

4.5.1. Herramientas  

4.5.1.1. Herramientas básicas  Como  toda  página  o  aplicación  Web,  se  han  usado  las  tecnologías  básicas  para  este  tipo  de  desarrollo:  HTML,  CSS  y  JavaScript.    

4.5.1.2. AngularJS  Para  conseguir  una  Single  Page  Application  se  ha  usado  el  framework  de  JavaScript  AngularJS   (versión   1.5.6).   Es   un   framework   de   código   abierto   mantenido   por  Google   que   provee   un   entorno   de   trabajo   orientado   al   uso   del   Modelo-­‐Vista-­‐Controlador   (se   puede   considerar   de   tipo   Modelo/Vista/Vista-­‐Modelo   ya   que  incorpora   el   enlace   de   datos   automático   o   data   binding).   AngularJS   consigue  disociar  la  manipulación  del  DOM  de  HTML  de  la  lógica  de  la  aplicación,  lo  cual  es  un  concepto  relativamente  novedoso  en  el  desarrollo  Web.    Para   entender   cómo   funciona   AngularJS   hay   que   explicar   las   piezas   que   lo  componen:    

-­‐ Vista:  es  lo  que  ve  el  usuario,  el  DOM  de  HTML.  -­‐ Controlador:  contiene  la  lógica  de  negocio  que  hay  detrás  de  las  vistas.  Son  

archivos  JavaScript.  -­‐ Modelo:  los  datos  con  los  que  se  trabaja,  que  se  muestran  en  la  vista  y  con  

los  que  el  usuario  interactúa.  -­‐ Directiva:  un  HTML  extendido  con  atributos  personalizables.  Una  directiva  

tiene   su   propio   controlador   y   aporta   la   ventaja   de   ser   reutilizable   desde  cualquier  vista.  

-­‐ Scope:  se  trata  de  un  contexto  en  el  que  los  controladores,  las  directivas  y  las  vistas  pueden  acceder  al  modelo.  Éste  contexto  es  el  que  provee  el  data  binding.  

-­‐ Servicio:  lógica  de  negocio  reutilizable  independiente  de  las  vistas.            

53    

AngularJS   permite   la   inyección   de   otras   aplicaciones   en   la   aplicación   que   se  desarrolla,  a  modo  de  librerías.  

4.5.1.3. Gestión de dependencias  Para  gestionar  estas  librerías  o  aplicaciones  se  ha  hecho  uso  de  Bower,  un  gestor  de  dependencias  para  aplicaciones  de  cliente  Web  que  depende  de  npm  (un  gestor  de  paquetes  JavaScript  desarrollado  para  Node.js)    Bower   permite   instalar   paquetes   alojados   en   Internet   a   través   de   la   línea   de  comandos:     bower  install  angular  –-­‐save    Esto  da  como  resultado  la  descarga  del  paquete  llamado  angular,  guardarlo  en  una  carpeta  llamada  bower_components  y  referenciar  esa  dependencia  en  un  archivo  de  configuración  bower.json,  que  tendría  esta  forma:    {          

"name":  "lingvo",          

"version":  "0.0.0",          

"authors":  ["Jorge  Rabanos"],          

"license":  "Copyright",          

"dependencies":  {"angular":  "~1.5.6",  "bootstrap":  "~3.3.6"  }      

}    Realmente   no   se   acaba   de   gestionar   la   dependencia   en   el   proyecto   sino   en   el  entorno  de  trabajo,  y  es  responsabilidad  del  desarrollador  importar  o  hacer  uso  de  los  archivos  ahora  alojados  en  la  carpeta  bower_components.    

4.5.1.4. Grunt  Otra  herramienta  que  ha  conformado  el  entorno  de  trabajo  es  Grunt.  Se   trata  de  un  automatizador  de  tareas  para  JavaScript  que  permite  al  desarrollador  ahorrarse  repetir  ciertas  tareas  que  son  imprescindibles  para  el  desarrollo  pero  que  resultan  monótonas.  A   través   de   un   archivo   de   configuración   llamado   Gruntfile.js   permite   establecer  ciertas  actividades  para  que  sean  ejecutadas  constantemente  o  en  función  de  una  acción.  En  este  proyecto  Grunt  se  ha  usado  para  varios  propósitos:  El  primero  de  ellos  es  el  de  gestionar  todas  las  hojas  de  JavaScript.  El  hecho  de  que  AngularJS   permita   la   separación   de   Modelo-­‐Vista-­‐Controlador   provoca   que   se  acaben  creando  numerosos  archivos.            

54    

En  un  ejemplo  reducido:    app:  {                                    

  src:  [                                            

//  Libraries                                          

'bower_components/angular/angular.min.js',                                                                                    

'bower_components/angular-­‐route/angular-­‐route.min.js',                                                                                    

//  Application  scripts                                            

'static/js/app.js',                                            

'static/js/services/*.js',                                          

'static/js/factories/*.js',                                          

'static/js/controllers/*.js',                                                            

'static/js/directives/*.js',                                          

'static/js/filters/*.js'                                    

],                                    

dest:  'static/built/app.js'                            

}    Esto  se   interpreta  como  que   todos   los  archivos  que  hay  en   la   lista  de  src  que,  en  este   ejemplo   son   dos   librerías   instaladas   con   bower   y   numerosos   archivos   del  proyecto  (el  asterisco  sirve  para  indicar  que  cualquier  nombre  de  archivo  es  válido  y   se   debe   incluir);   deben   componer   un   archivo   final   alojado   en   la   dirección  indicada  en  dest.  Este  archivo  se  va  a  crear  a  partir  de  la  concatenación  de  todos  los  anteriores.  Cada  vez  que  se  introduzca  un  cambio  en  uno  de  esos  archivos,  Grunt  lo  va  a  identificar  y  va  a  volver  a  generar  el  archivo  destino,  por  lo  que  la  ventaja  que  ofrece  es  muy  notable.    Otro  uso  de  Grunt  es  el  de  minificar  un  archivo.  Minificar  se  refiere  a  la  eliminación  de   bytes   innecesarios   (espacios,   saltos   de   línea,   sangrías…)   en   incluso   cambiar  nombres  de  variables  por  otros  más  cortos.  Esto  se  hace  con  el  fin  de  disminuir  el  tamaño  de  los  archivos  y  por  tanto  reducir  el  tiempo  de  carga:    uglify:  {                            

  built:  {                                    

    files:  {                                            

      'static/built/app.min.js':  ['static/built/app.js']  

      }    

                         },  

                 }      Con   esta   configuración   se   consigue   que   cada   vez   que   el   archivo   app.js   sea  modificado,  se  cree  un  archivo  app.min.js  con  el  mismo  contenido  pero  minificado.  

55    

4.5.1.5. Angular Material  También  se  ha  hecho  uso  de  Angular-­‐Material  1.0.9.  Angular   Material   es   un   framework   de   interfaz   de   usuario   desarrollado   para  funcionar  como  librería  de  AngularJS.  Está  desarrollado  por  Google  y  sigue   la  especificación  de  principios  de  diseño  de  Material  Design,  también  de  Google.    Proporciona  herramientas  para  construir  un  sistema  visual  interactivo  y  uniforme.  Está   orientado   al   diseño   adaptativo,   cuyo   fin   es   adaptar   la   apariencia   de   las  páginas  Web  al  dispositivo    que  se  esté  utilizando  para  visualizarla    

4.5.2. Estructura  El  cliente  está  dividido  en  dos  partes:  registro/autenticación  y  aplicación.    La   página   de   registro   o   autenticación   es   pública   y,   si   se   intenta   acceder  directamente  a   la  aplicación  si  haber   iniciado  sesión,   se  produce  una  redirección  automática  a  la  página  de  autenticación  (login).  Esta  redirección  y  la  comprobación  de   autenticación   se   realiza   desde   el   servidor  mediante   el   sistema   de   gestión   de  URLs  de  Django.  El  hecho  de  separar  el  cliente  en  dos  páginas  se  debe  a  dos  razones:  la  primera  es  por   peso.   El   acceso   sólo   a   la   página   de   registro   es   mucho   más   liviano   ya   que  contiene  pocos  scripts,  y  ahorra  una  carga  más  pesada  para  un  usuario  que  quizás  no  quiera  acceder  a  la  aplicación.  Por  otro  lado  está  el  tema  de  la  seguridad:  a  pesar  de  que  la  API  está  protegida  con  autenticación   y   uso   de   sesión,   es   mejor   no   dar   información   innecesaria   a   un  posible  atacante.    La  parte  de  la  aplicación  está  organizada  de  la  siguiente  manera:  En  el  nivel  raíz,  tres  carpetas:  

-­‐ Styles:  contiene  los  archivos  de  estilo  CSS.  -­‐ Templates:  se  encuentran  archivos  HTML,  que  en  AngularJS  son  realmente  

porciones  de  vistas  asociadas  a  un  controlador  con   lógica  de  presentación  propia.  

-­‐ JS:   en   esta   carpeta   se   encuentra   la   aplicación   (llamada   app.js)   y   varias  carpetas  con  sus  componentes:  controllers,  directives  y  services.  

 

4.5.3. Implementación de la interfaz  

4.5.3.1. Comunicación con el servidor  Para  hacer  peticiones   a   través  de   la  API  REST   se  ha  usado  un   servicio  nativo  de  Angular  denominado  $http.    Éste   facilita   la   comunicación   con   servidores   HTTP   a   través   del   objeto  XMLHttpRequest,  es  decir,  para  realizar  llamadas  AJAX.  

56    

AJAX   (Asynchronous   JavaScript  And  XML)  es  una   técnica  de  desarrollo  Web  que  permite  realizar  peticiones  de  manera  asíncrona  y  en  segundo  plano,  por  lo  que  se  pueden  actualizar  los  datos  de  una  página  sin  necesidad  de  recargarla.    Éste  servicio  devuelve  una  respuesta  asíncrona,  una  función  que  se  ejecutará  una  vez  se  haya  resuelto  la  petición.  Veamos  un  ejemplo:    $http.post(url_login,  data)  

.success(function  (data,  status,  headers,  config)  {window.location  =  "/";})  

.error(function  (data,  status,  headers,  config)  {$scope.errors  =  'Incorrect  

data';  });    Se  realiza  una  petición  POST  a  una  URL  y  con  unos  datos  especificados  y,  una  vez  resuelta   (cuando   se   reciba   la   respuesta),   se   ejecutará   la   función   contenida   en  success  en  caso  de  éxito    o  la  contenida  en  error  en  caso  de  fallo.                                                                

57    

4.5.3.2. Registro y autenticación  Como   se  ha   explicado   anteriormente,   la   parte  de   registro   y   autenticación   es  una  página  aparte  de   la  aplicación.  Provee  de   los   formularios  necesarios  para  que  un  usuario  se  de  de  alta    en  el  sistema  y  para  que  se  autentique,  así  como  un  control  de  errores.    

   

4.5.3.3. Main  Ya  en  la  aplicación,  existe  una  vista  principal  que  engloba  a  todas  las  demás.  Esta  vista  Main   provee   de   un  menú   con   enlaces,   común   a   todas   las   interfaces,   y   que  sirve   de   navegador   entre   las   diferentes   vistas   que   contiene:   people,   my   profile,  search  meetings,  my  meetings  y  opened  chats  (gente,  mi  perfil,  buscar  quedadas,  mis  quedadas  y  conversaciones  abiertas).      

4.5.3.4. People  Esta  vista  presenta  una  lista  de  usuarios  afines  al  usuario  que  la  visita  en  función  de   sus   idiomas.   Se   muestran   algunos   de   los   idiomas   que   habla   y   practica   cada  usuario  de  la  lista.  

58    

 

   

4.5.3.4. Profiles  Estas  vistas  son  similares  y   las  componen  tanto  el  detalle  de  un  perfil  de  usuario  como  la  edición  de  perfil.  

     

4.5.3.5. Meetings  Las   vistas   relacionadas   con   las   quedadas   necesitan   hacer   uso   de  mapas.   Se   han  desarrollado  haciendo  uso  de  la  API  pública  de  Google  Maps.  

59    

 Además,  se  ha  hecho  uso  de  la  librería  Ng-­‐map,  que  proporciona  una  directiva  de  AngularJS  para  instanciar  mapas  de  Google  Maps  en  las  vistas.    Veamos  un  ejemplo  de  cómo  se  han  usado  los  mapas  en  la  creación  de  quedadas,  que  necesita  de  una  posición  específica  en  el  mapa  para  ser  creada:    Dentro  del  controlador  correspondiente  a  la  vista  de  creación,  tenemos  el  siguiente  código:    NgMap.getMap().then(function  (map)  {                            

   this.map  =  map;                    

});                    

$scope.placeMarker  =  function  (e)  {                            

if  (marker  !=  null)  {                                    

marker.setMap(null);                            

}                              

marker  =  new  google.maps.Marker({position:  e.latLng,  map:  vm.map});                            

$scope.lat  =  marker.getPosition().lat();                            

$scope.lng  =  marker.getPosition().lng();                            

vm.map.panTo(e.latLng);                    

}  

});      Éste  código  ha  instanciado  un  nuevo  mapa  por  medio  de  la  API  de  la   librería  Ng-­‐map  y  lo  ha  asignado  al  contexto  actual  (this.map  =  map).    También  ha  incluido  un  método  que  se  ejecuta  en  función  de  un  evento:  cada  vez  que  se  haga  clic  sobre  el  mapa,   posicionará   un   marcador   en   las   coordenadas   correspondientes   al   punto  clicado,  además  de  guardar  el  valor  de  esas  coordenadas  ($scope.lat  y  $scope.lng)  para  su  futuro  uso.    Por  otro  lado,  se  hace  uso  de  la  directiva  correspondiente  al  mapa  en  HTML:    <ng-­‐map  zoom="10"  center=" 48.1321286,  11.5946726"  on-­‐click="placeMarker()">  

</ng-­‐map>    Este  código  instanciará  un  mapa  con  centro  en  las  coordenadas  indicadas,  que  son  editables,   y   con   un   evento   on-­‐click   que   llama   a   la   función   que   se   ha   definido  anteriormente.            

60    

El  resultado:    

     Por   último,   en   la   parte   de   quedadas   hay   que   destacar   la   búsqueda   de   eventos  cercanos.   Para   realizar   esta   búsqueda   es   necesaria   la   localización   actual   del  usuario.  Para  ello  se  hace  uso  de  HTML5  Geolocation,  una  API  de  JavaScript  usada  para  precisamente  obtener  la  posición  geográfica  del  cliente.  Debido  a  que  puede  comprometer   la   privacidad   del   usuario,   esta   API   se   activa   bajo   aprobación   del  usuario.  Su  uso:  

navigator.geolocation.getCurrentPosition(location)    De   esta   manera   se   representa   la   lista   de   quedadas   cercanas   a   un   usuario   en   el  mapa  dentro  de  un  radio  editable  que,  por  defecto,  es  de  dos  kilómetros:    

61    

   

 

3.5.3.6. Chat  La   implementación   del   chat   es   una   interfaz   básica   en   la   que   se   puede   leer   el  historial  de  mensajes  de  arriba  abajo  diferenciando  al  usuario  que  lo  envía.      

62    

   

   

3.5.4. Websockets / Chat  Como   ya   se   ha   explicado,   websocket   es   una   tecnología   que   hace   posible   una  conexión  continua  entre  el  cliente  y  el  servidor  basada  en  el  protocolo  ws.    La   API   de   websockets   está   disponible   para   el   código   JavaScript   y   funcional   en  navegadores  como  Firefox,  Chrome  y  Safari.  Para   hacer   uso   de   ella   se   debe   instanciar   un   objeto   de   la   clase  WebSocket,   y  especificar  una  dirección  de  conexión.    Esta  dirección  deberá   llevar  el  prefijo  “ws:”  (o  “wss:”  para  conexiones  cifradas)  el  cual   es   un   nuevo   esquema   de   URI   definido   en   la   especificación   del   protocolo  WebSocket.    En   este   proyecto,   en   el   servidor,   se   habilitó   una   URI   específica   para   recibir   una  petición  de  conexión  mediante  WebSocket.    Creando  una  conexión  a  una  dirección  de  la  forma:    

ws:host/chat/{  idUsuario  }    se  habilitará  un  canal  de  comunicación  entre  el  usuario  que  crea   la  conexión  y  el  servidor,   referente   a   una   conversación   entre   tal   usuario   y   el   identificado   por   el  argumento  idUsuario.  

63    

Un  proceso  referente  a  la  conexión  se  encargará  de  que  los  mensajes  enviados  sean  guardados   y   que   cualquiera   de   los   dos   usuarios   que   tenga   abierto   un   canal  WebSocket  los  reciba  sin  tener  que  pedirlos.    Para  abrir  una  conexión  por  lo  tanto:    

var  socket  =  new  WebSocket('ws://'    

+  window.location.host  +  "/chat/"  +  $routeParams.id  +  "/");        Seguidamente   hay   que   asociar   al   socket   un   evento   que   escuche   los   mensajes  recibidos:    socket.onmessage  =  function  (message)  {                            

             var  args  =  message.data.split(":");                            

             var  style  =  args[0]  ==  $routeParams.id;                            

var  item  =  {"text":  args[1],  "style":  style};                          

$scope.messages.push(item);                            

var  element  =  document.getElementById("chatListId");                          

$(element).scrollTop(parseInt($(element)[0].scrollHeight)  +  200);                          

$scope.$apply();                    

};    Obviando   el   código   en   gris,   por   no   entrar   en   detalles   de   la   implementación,   se  observa   que   el   mensaje   que   se   recibe   como   argumento   se   guarda   en   un   array  ($scope.messages).   Este   array   está   presentado   en   la   vista   como   una   lista   de  mensajes.  Al  ser  un  proceso  en  segundo  plano,  que  es  posible  que  se  esté  ejecutando  en  una  ventana   o   pestaña   inactiva,   es   posible   que   esa   adición   de   datos   no   se   vea  representada   en   la   interfaz   hasta   que   la   ventana   sea   activa.   Esto   es   un  comportamiento  propio  de  AngularJS    y  la  última  sentencia  ($scope.$apply())  sirve  precisamente  para  obligar  a  que  se  aplique  inmediatamente;  con  un  coste  adicional  de  memoria,  evidentemente.  De   esta   manera   se   evita   también   que   se   pierdan   datos,   ya   que   las   conexiones  WebSocket   no   garantizan   el   envío   de   todos   los   mensajes   y   encolarlos   podría  provocar  la  pérdida  de  alguno.    Por  último,  el  envío  de  datos  desde  JavaScript  se  realiza  así:     socket.send(JSON.stringify({message:  $scope.message}));    Y,   con   esta   base,   la   aplicación   soporta   conversaciones   en   tiempo   real   de   forma  totalmente  funcional  a  través  de  una  API  nativa.    

64    

5. Conclusiones    Este  proyecto  se  ha  desarrollado  en  torno  a  una  idea  personal  con  el  objetivo  tanto  de  realizar  un  seguimiento  sobre  el  desarrollo  de  un  software  como  de  aprender  a  usar  nuevas  tecnologías.    La  variedad  de  herramientas  utilizadas  ha  sido  bastante  amplia.  Algunas   de   las   tecnologías   utilizadas   ya   las   conocía   en  mayor   o   menor  medida,  pero   otras   han   sido   totalmente   nuevas   para  mí   y   han   resultado  muy   didácticas:  Django-­‐Channels,  Websockets,  Angular-­‐Material  y  algunas  librerías  de  AngularJS.    La   idea   inicial   era  desarrollar   tanto   la  parte  de  cliente   como   la  de   servidor,   y  un  módulo  acoplable  para  integrar  un  chat.  La  parte  de  servidor  se  debía  desarrollar  siguiendo  la  filosofía  de  API  REST.    Estos  objetivos  iniciales  se  han  cumplido  con  éxito.    Respecto   al   uso   de   Django-­‐Channels   y   Websockets,   creo   que   es   un   paso   muy  importante   para   el   framework   el   hecho   de   incluir   esta   forma   de   trabajar.   El  desarrollo  Web  avanza  muy  deprisa  y  el  concepto  de  que  cada  página  o  vista  sea  una  petición  distinta  se  está  quedando  atrás,  por   lo  pesado  que  resulta  y   lo   lento  que   se   percibe.   No   sólo   Django,   la   mayoría   de   los   frameworks   actuales   de  desarrollo  Web  trabajan  de  esta  manera  y  deberían  ir  orientando  su  desarrollo  al  ofrecimiento   de   servicios   a   Single   Page   Applications  mediante   tecnologías   como  Websockets  o  el  nuevo  protocolo  HTTP/2.      En   definitiva,   el   proyecto   se   ha   realizado   de   forma   satisfactoria   cumpliendo   las  expectativas  iniciales,  con  una  aplicación  totalmente  funcional.                                        

65    

6. Futuras ampliaciones  Como   ampliación   para   esta   aplicación   se   propone   extender   el   módulo   de  conversaciones  en  tiempo  real  para  que  sea  capaz  de  soportar  más  de  dos  usuarios  en  el  mismo  chat.      También   se   propone   la   inclusión   de   conversaciones   a   través   de   vídeo  mediante  WebRTC   (Web   Real   Time   Communications),   un   proyecto   abierto,   iniciativa   de  Mozilla,   Google   y   Opera,   que   hace   posible   la   comunicación   de   audio   y   vídeo   en  tiempo  real  en  navegadores  Web  a  través  de  una  API  de  JavaScript.    Por  último  se  propone  aplicar  el  estilo  Progressive  Web  App  a  esta  aplicación.  Esto  implica   adaptarla   de   manera   que   pueda   funcionar   como   una   aplicación   de  escritorio   en   smartphones   Android   y   iOS,   realizando   tareas   en   segundo   plano   y  declarando  un  archivo  de  manifiesto.                                                                    

66    

7. Bibliografía  

-­‐ BAUMGARTNER, Peter y MALET, Yann. High Performance Django.

Createspace, 2015.  

-­‐ FREEMAN, Adam. Pro AngularJS. Apress, 2014.  

-­‐ GODWIN,  Andrew.  Django  Channels  [librería].  Python.  Disponible  en  

https://channels.readthedocs.io    (última  consulta  06/2016).  

-­‐ GREENFELD, Daniel y ROY, Audrey. Two Scoops of Django: Best Practices

for Django. Two Scoops Press, 2015.  

-­‐ HOLOVATY Adrian y KAPLAN-MOSS, Jacob. The Django Book. Apress,

Diciembre 2007.  

-­‐ KAPLAN-­‐MOSS,  Jacob.  Finally,  Real-­‐Time  Django  Is  Here:  Get  Started  

with  Django  Channels.  Disponible  en  https://blog.heroku.com  (última  

consulta  06/2016).  

-­‐ LUTZ Mark. Learning Python. O’Reilly Media, Julio 2013.  

-­‐ MARTIN, Robert C. Clean code: A Handbook of Agile Software

Craftmanship. Prentice Hall 2008.  

-­‐ MOZILLA DEVELOPER NETWORK. WebSockets. Disponible en:

https://developer.mozilla.org/ (última consulta 06/2016).  

-­‐ PERCIVAL, Harry. Test-Driven Development with Python. O’Reilly, 2014.  

-­‐ TIVIX  INC.  Django-­‐Rest-­‐Auth  [librería].  Python.  Disponible  en  

https://django-­‐rest-­‐auth.readthedocs.io  (última  consulta  06/2016).  

-­‐ WIKIPEDIA. Interfaz de Programación de Aplicaciones. Disponible en:

http://es.wikipedia.org/wiki/Interfaz_de_programacion_de_aplicaciones (última

consulta 06/2016).  

                       

67    

8. Apéndice    

8.1. Especificación API REST  

8.1.1. Login  REQUEST  URL   /rest-­‐auth/login/  REQUEST  TYPE   POST  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   None  EXAMPLE   {  

“username”:  USERNAME,  “email”:  EMAIL,  “password”:  PASSWORD  }  

 Donde   USERNAME   es   el   nombre   usuario   en   el   sistema,   EMAIL   su   correo  electrónico  y  PASSWORD  su  contraseña  de  acceso.    Respuesta  esperada:    HTTP  201  CREATED  {“key”:  KEY}    Siendo  KEY  la  clave  de  sesión.    

8.1.2. Logout  REQUEST  URL   /rest-­‐auth/logout/  REQUEST  TYPE   POST  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {“key”:  KEY}    KEY  es  la  clave  de  sesión  recibida  en  el  login.  Respuesta  esperada:    HTTP  200  OK  {  "success":  "Successfully  logged  out."}        

68    

8.1.3. Registration  REQUEST  URL   /rest-­‐auth/registration/  REQUEST  TYPE   POST  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   None  EXAMPLE   {  

“username”:  USERNAME,  “email”:  EMAIL,  “password1”:  PASSWORD,  “password2”:  PASSWORD  }  

 El  password  se  debe  enviar  dos  veces  para  evitar  errores  del  usuario.  Respuesta  esperada:    HTTP  201  CREATED  {“key”:  KEY}    KEY  es  la  clave  de  sesión.                                                        

69    

8.1.4. Profile  Recuperar  datos  de  un  perfil:    REQUEST  URL   /api/1.0/profiles/ID  REQUEST  TYPE   GET  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {}    ID  es  el  identificador  único  del  perfil  que  se  desea  recuperar  Respuesta  esperada:    HTTP  200  OK  {      "description":  "Hello!",      "genre":  "MSC",      "born_date":  "12/11/1991",      "user":  {          "id":  6,          "email":  "[email protected]",          "username":  "Morla"      },      "speaks":  [          ...      ],      "practices":  [          ...      ],      "picture":  "pictures/homer.jpg",      "id":  7  }      Actualizar  perfil:    REQUEST  URL   /api/1.0/profiles/ID  REQUEST  TYPE   PATCH  /  PUT  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {  

   "description":  "Hello!",      "genre":  "MSC",      "born_date":  "12/11/1991"      }    

 ID  es  el  identificador  único  del  perfil  que  se  desea  recuperar.  Respuesta  esperada:  

70    

HTTP  200  OK  {      "description":  "Hello!",      "genre":  "MSC",      "born_date":  "12/11/1991",      "user":  {          "id":  6,          "email":  "[email protected]",          "username":  "Morla"      },      "speaks":  [          ...      ],      "practices":  [          ...      ],      "picture":  "pictures/homer.jpg",      "id":  7  }    Obtener  perfil  propio:    REQUEST  URL   /api/1.0/users/me/  REQUEST  TYPE   GET  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {}    Respuesta  esperada:    HTTP  200  OK  {      "description":  "Hello!",      "genre":  "MSC",      "born_date":  "12/11/1991",      "user":  {          "id":  6,          "email":  "[email protected]",          "username":  "Morla"      },      "speaks":  [          ...      ],      "practices":  [          ...      ],      "picture":  "pictures/homer.jpg",      "id":  7  }  

71    

 

8.1.5. Language  Listado  de  idiomas:    REQUEST  URL   /api/1.0/languages/  REQUEST  TYPE   GET  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {  }    Respuesta  esperada:    HTTP  200  OK  [      {          "id":  48,          "flag":  "flags/frFlag.png",          "code":  "fr",          "name":  "French"      },      {          "id":  38,          "flag":  "flags/enFlag.png",          "code":  "en",          "name":  "English"      },    …  ]    Añadir  idioma  hablado:    REQUEST  URL   /api/1.0/languages/speak/  REQUEST  TYPE   POST  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {"user":USER_ID,"language":LANGUAGE_ID}    USER_ID  es  el  identificador  del  usuario,  y  LANGUAGE_ID  el  del  idioma  que  se  desea  añadir.  Respuesta  esperada:    HTTP  201  CREATED  {      "id":  5024,      "user":  112,      "language":  1  }  

72    

 Eliminar  idioma  hablado:    REQUEST  URL   /api/1.0/languages/speak/ID  REQUEST  TYPE   DELETE  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {"user":USER_ID,"language":LANGUAGE_ID}    ID  es  el  identificador  de  la  instancia  de  idioma  hablado.  USER_ID  es  el  identificador  del  usuario,  y  LANGUAGE_ID  el  del  idioma  que  se  desea  añadir.  Respuesta  esperada:    HTTP  204  NO  CONTENT    Añadir  idioma  practicado:    REQUEST  URL   /api/1.0/languages/practice/  REQUEST  TYPE   POST  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {"user":USER_ID,"language":LANGUAGE_ID}    USER_ID  es  el  identificador  del  usuario,  y  LANGUAGE_ID  el  del  idioma  que  se  desea  añadir.  Respuesta  esperada:    HTTP  201  CREATED  {      "id":  5024,      "user":  112,      "language":  1  }    Eliminar  idioma  practicado:    REQUEST  URL   /api/1.0/languages/practice/ID  REQUEST  TYPE   DELETE  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {"user":USER_ID,"language":LANGUAGE_ID}    ID  es  el  identificador  de  la  instancia  de  idioma  hablado.  USER_ID  es  el  identificador  del  usuario,  y  LANGUAGE_ID  el  del  idioma  que  se  desea  añadir.  Respuesta  esperada:    HTTP  204  NO  CONTENT    

73    

 

8.1.6. Meeting  Obtener  quedadas  cercanas:    REQUEST  URL   /api/1.0/meetings/?distance=DIS&lat=LAT&lon=LON  REQUEST  TYPE   GET  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION  

Session  

EXAMPLE   {}    DIS  es  la  distancia  en  metros  del  radio  de  búsqueda  de  quedadas.  LAT  y  LON  son  las  coordenadas  del  punto  de  búsqueda.  Respuesta  esperada:    HTTP  200  OK  {      "type":  "FeatureCollection",      "features":  [          {              "id":  5,              "type":  "Feature",              "geometry":  {                  "type":  "Point",                  "coordinates":  [                      40.383333,                      -­‐3.716667                  ]              },              "properties":  {                  "title":  "Evento",                  "time":  "2016-­‐09-­‐13T18:00:00Z",                  "creator":  6,                  "attendances":  [],                  "distance":  0              }          },              …    ]  }                

74    

Obtener  detalle  de  quedada:    REQUEST  URL   /api/1.0/meetings/ID  REQUEST  TYPE   GET  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {}    ID  es  el  identificador  del  evento.  Respuesta  esperada:    HTTP  200  OK  {      "id":  26,      "type":  "Feature",      "geometry":  {          "type":  "Point",          "coordinates":  [              40.383333,              -­‐3.716667          ]      },      "properties":  {          "title":  "Event  title",          "time":  "2016-­‐09-­‐12T19:30:00Z",          "creator":  112,          "attendances":  [],          "distance":  ""      }  }        Crear  quedada:    REQUEST  URL   /api/1.0/meetings/  REQUEST  TYPE   POST  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {  

       "title":  “Event  title”,          "position":  "POINT(40.383333  -­‐3.716667)",          "time":  “2016-­‐09-­‐12T19:30:99”  }  

 Respuesta  esperada:      

75    

HTTP  201  CREATED  {      "id":  26,      "type":  "Feature",      "geometry":  {          "type":  "Point",          "coordinates":  [              40.383333,              -­‐3.716667          ]      },      "properties":  {          "title":  "Event  title",          "time":  "2016-­‐09-­‐12T19:30:00Z",          "creator":  112,          "attendances":  [],          "distance":  ""      }  }      Eliminar  quedada:    REQUEST  URL   /api/1.0/meetings/ID  REQUEST  TYPE   DELETE  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {}    ID  es  el  identificador  del  evento.  Respuesta  esperada:    HTTP  204  NO  CONTENT      

8.1.7. Attendance  Crear  asistencia:    REQUEST  URL   /api/1.0/attendances/  REQUEST  TYPE   POST  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {  

"user":  USER_ID,    "meeting":  MEETING_ID  }  

 

76    

USER_ID  y  MEETING_ID  son  los  identificadores  del  usuario  y  el  evento  a  asistir.  Respuesta  esperada:    HTTP  201  CREATED  {      "user":  112,      "meeting":  22,      "id":  13  }      Obtener  asistencias  de  usuario:    REQUEST  URL   /api/1.0/attendances/  REQUEST  TYPE   GET  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {}    Respuesta  esperada:    HTTP  200  OK  [      {          "user":  {              "id":  112,              "email":  "[email protected]",              "username":  "user23"          },          "meeting":  {              "id":  22,              "title":  "German  talk",              "position":  {                  "type":  "Point",                  "coordinates":  [                      40.389115990879745,                      -­‐3.6436843872070312                  ]              },              "time":  "2016-­‐08-­‐27T18:27:33.441000Z",              "creator":  5          },          "id":  13      }  ]          

77    

Eliminar  asistencia:    REQUEST  URL   /api/1.0/attendances/ID  REQUEST  TYPE   POST  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {}    Donde  ID  es  el  identificador  único  de  la  asistencia  Respuesta  esperada:    HTTP  204  NO  CONTENT    

8.1.8. Related    Obtener  usuarios  afines:    REQUEST  URL   /api/1.0/related/  REQUEST  TYPE   GET  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {}    Respuesta  esperada:    HTTP  200  OK  [      {          "description":  "Description",          "genre":  "FEM",          "born_date":  "2001-­‐01-­‐18",          "user":  {              "id":  5,              "email":  "[email protected]",              "username":  "Monkey"          },          "speaks":  [              …          ],          "practices":  [              …          ],          "picture":  "pictures/faul.jpg",          "id":  6      },  …  ]  

78    

 

8.1.9. Chat  Conversaciones  del  usuario:    REQUEST  URL   /api/1.0/chats/  REQUEST  TYPE   GET  REQUEST  FORMAT   JSON  REQUEST  AUTHENTICATION   Session  EXAMPLE   {}    Respuesta  esperada:    HTTP  200  OK  [          {                  "id":  12,                  "user_from":  {                          "id":  1,                          "username":  "Robin"                  },                  "user_to":  {                          "id":  108,                          "username":  "Batman"                  },                  "label":  "1-­‐108"          },    …  ]