Está en la página 1de 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
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  
 
 

2    
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.  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

3    
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.  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

4    
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.  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

5    
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.  
 
 

6    
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.  
 
 

7    
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   El  usuario  debe  ser  capaz  de  gestionar  
practicados   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.  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

8    
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  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

9    
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  
 
 
 
 
 
 
 
 
 
 

10    
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  
 
 
 
 
 
 
 
 
 
 
 
 

11    
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  
 
 
 
 
 
 
 
 
 

12    
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  
 
 
 
 
 
 

13    
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  
 
 
 
 
 

14    
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  
 
 
 
 
 
 
 
 
 
 
 
 

15    
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  
 
 
 
 
 
 
 
 

16    
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  

17    
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  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

18    
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:  
 
 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

19    
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.  
 
 
 
 
 
 
 

20    
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.  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

21    
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.  
 
 
 
 
 
 
 
 
 
 
 
 

22    
3.6.2. Login
 
 

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

23    
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.  
 
 
 
 
 
 
 
 
 
 
 
 
 

24    
3.6.4. Perfiles afines
 
 

 
 
 
Se  trata  de  un  listado  de  perfiles  afines  al  usuario.  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

25    
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.  
 
 
 
 
 
 
 
 
 
 
 
 

26    
3.6.6. Detalle evento
 
 

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

27    
3.6.7. Listado de eventos
 
 

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

28    
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.  
 
 
 
 
 
 
 
 
 
 
 
 
 
 

29    
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.  
 
 
 
 
 
 
 
 
 
 
 
 
 

30    
3.6.10. Conversación
 
 

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

31    
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.  

32    
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  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

33    
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.  
 
 
 

34    
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.  
 
   
 
 
 
 
 

35    
 

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.  
 

36    
 
 
 
 
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.  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

37    
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.    

38    
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,   Estos  recursos  no  están  directamente  asociados  a  un  dato  o  
registration   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,   Este  recurso  va  asociado  a  la  relación  que  existe  entre  User  
languages-­‐ y  Language.  Es  decir,  los  idiomas  que  habla  y  practica  cada  
practice   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.  
 
 

39    
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()              
 

40    
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.  
 

41    
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.  
 

42    
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  
 
 
 
 

43    
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).  
 
 

44    
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.  
 
 

45    
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.  
 
 
 
 
 
 
 
 
 

46    
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:  

47    
 
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:  
 

48    
 
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  

49    
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:  
 

50    
 
 
 
 
 
 
 
 
 
 
 

51    
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. Herram ientas 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.  
 
 
 
 
 

52    
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.  
 
 
 
 
 

53    
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.  

54    
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. Com unicació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.  

55    
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.  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

56    
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. M ain
 
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.  

57    
 

 
 
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. M eetings
 
Las   vistas   relacionadas   con   las   quedadas   necesitan   hacer   uso   de   mapas.   Se   han  
desarrollado  haciendo  uso  de  la  API  pública  de  Google  Maps.  

58    
 
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.  
 
 
 
 
 

59    
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:  
 

60    
 
 
 
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.    
 

61    
 
 
 
 

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.  

62    
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.  
 

63    
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.  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

64    
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.  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

65    
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).  
 
 
 
 
 
 
 
 
 
 
 
 

66    
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."}  
 
 
 

67    
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.  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

68    
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":  "user@kf.net",  
       "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:  

69    
HTTP  200  OK  
{  
   "description":  "Hello!",  
   "genre":  "MSC",  
   "born_date":  "12/11/1991",  
   "user":  {  
       "id":  6,  
       "email":  "user@kf.net",  
       "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":  "user@kf.net",  
       "username":  "Morla"  
   },  
   "speaks":  [  
       ...  
   ],  
   "practices":  [  
       ...  
   ],  
   "picture":  "pictures/homer.jpg",  
   "id":  7  
}  

70    
 

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  
}  

71    
 
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  
 

72    
 

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   Session  
AUTHENTICATION  
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  
           }  
       },  
           …  
 ]  
}  
 
 
 
 
 
 
 

73    
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:  
 
 

74    
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  
}  
 

75    
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":  "user23@kf.net",  
           "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  
   }  
]  
 
 
 
 

76    
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":  "monkey@dluffy.es",  
           "username":  "Monkey"  
       },  
       "speaks":  [  
           …  
       ],  
       "practices":  [  
           …  
       ],  
       "picture":  "pictures/faul.jpg",  
       "id":  6  
   },  
…  
]  

77    
 

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"  
       },  
 
…  
]  
 
 

78    

También podría gustarte