/[ambar]/ambar/conversacion.py
ViewVC logotype

Diff of /ambar/conversacion.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 2.3 by amoyav, Sun Sep 1 17:05:01 2002 UTC revision 2.4 by pabloruiz, Tue Nov 5 22:58:22 2002 UTC
# Line 1  Line 1 
1  #!/usr/bin/env python  #!/usr/bin/env python
2  #  #
3  #Minë. Mundo Interactivo-Narrativo en Español [Tierra-Media]  #Minë. Mundo Interactivo-Narrativo en Español [Tierra-Media]
4  #Copyright (C) 2002  Pablo Ruiz Múzquiz  #Copyright (C) 2002  Pablo Ruiz Múzquiz
5  #  #
6  #  #
7  #This program is free software; you can redistribute it and/or modify  #This program is free software; you can redistribute it and/or modify
8  #it under the terms of the GNU General Public License as published by  #it under the terms of the GNU General Public License as published by
9  #the Free Software Foundation; either version 2 of the License, or  #the Free Software Foundation; either version 2 of the License, or
10  #(at your option) any later version.  #(at your option) any later version.
11  #  #
12  #This program is distributed in the hope that it will be useful,  #This program is distributed in the hope that it will be useful,
13  #but WITHOUT ANY WARRANTY; without even the implied warranty of  #but WITHOUT ANY WARRANTY; without even the implied warranty of
14  #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  #GNU General Public License for more details.  #GNU General Public License for more details.
16  #  #
17  #You should have received a copy of the GNU General Public License  #You should have received a copy of the GNU General Public License
18  #along with this program; if not, write to the Free Software  #along with this program; if not, write to the Free Software
19  #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA  #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  #  #
21    
22  #Fichero: conversacion.py  #Fichero: conversacion.py
23    
24  """Clase Conversacion."""  """Clase Conversacion."""
25    
26    
27  from xml.dom import minidom  from xml.dom import minidom
28  import random  import random
29  import sys  import sys
30  import string  import string
31  import os  import os
32    
33  from utils import *  from utils import *
34  from bloquedialogo import *  from bloquedialogo import *
35  from contexto import *  from contexto import *
36    
37  class Conversacion(BloqueDialogo):  class Conversacion(BloqueDialogo):
38      """Una coleccion de contextos accesibles durante un mismo dialogo.      """Una coleccion de contextos accesibles durante un mismo dialogo.
39            
40      Contiene los siguientes atributos:      Contiene los siguientes atributos:
41       - id (string): identifica univocamente la conversacion. No puede contener espacios.       - id (string): identifica univocamente la conversacion. No puede contener espacios.
42       - version (string): versión del sistema de dialogos.       - version (string): versión del sistema de dialogos.
43       - autor (string): nombre del autor de la descripción.       - autor (string): nombre del autor de la descripción.
44       - pnj (string): nombre del pnj al que pertenece la conversacion.       - pnj (string): nombre del pnj al que pertenece la conversacion.
45       - comentario (string): comentario explicativo del fichero de conversacion.       - comentario (string): comentario explicativo del fichero de conversacion.
46    
47       - _tolerancia_offtopic(int): porcentaje de rechazo de temas fuera de contexto       - _tolerancia_offtopic(int): porcentaje de rechazo de temas fuera de contexto
48       - _probabilidad_offtopic_continuar(float): probabilidad de que un offtopic permita o impida cambiar de tema       - _probabilidad_offtopic_continuar(float): probabilidad de que un offtopic permita o impida cambiar de tema
49    
50       - _sinonimos({string: string}): diccionario de conversión de sinonimos a los nombres de tema equivalentes       - _sinonimos({string: string}): diccionario de conversión de sinonimos a los nombres de tema equivalentes
51       - _contextos_iniciales([contexto]): contextos definidos con el atributo inicial='si'       - _contextos_iniciales([contexto]): contextos definidos con el atributo inicial='si'
52       - _indice_contextos({string: contexto}): acceso a los contextos por identificador       - _indice_contextos({string: contexto}): acceso a los contextos por identificador
53       - _temas_especiales: diccionario con las lista de temas especiales por defecto para la conversación.       - _temas_especiales: diccionario con las lista de temas especiales por defecto para la conversación.
54            
55            
56    
57      Invariantes:      Invariantes:
58        id != None        id != None
59        version == BloqueDialogo.Version        version == BloqueDialogo.Version
60        autor != None        autor != None
61        pnj != None        pnj != None
62        comentario != None        comentario != None
63                
64        _tolerancia_offtopic >= 0        _tolerancia_offtopic >= 0
65    
66        _sinonimos != None        _sinonimos != None
67        _contextos_iniciales != None        _contextos_iniciales != None
68        _indice_contextos != None        _indice_contextos != None
69    
70        _sinonimos.has_key(s) <-> _sinonimos[s] == tema.nombre() \        _sinonimos.has_key(s) <-> _sinonimos[s] == tema.nombre() \
71                                      para algun tema in contexto.temas() \                                      para algun tema in contexto.temas() \
72                                      para algun contexto in self.contextos()                                      para algun contexto in self.contextos()
73        -indice_contextos.has_key(n) <-> contexto.id == n para algun contexto in self._contextos        -indice_contextos.has_key(n) <-> contexto.id == n para algun contexto in self._contextos
74        -item.relacionados().has_key(t) <-> el contexto item.relacionado(nombretema)[t] contiene un tema con nombre t        -item.relacionados().has_key(t) <-> el contexto item.relacionado(nombretema)[t] contiene un tema con nombre t
75      """      """
76    
77    
78      def __init__(self, id):      def __init__(self, id):
79          """Crea una conversacion nueva a partir de un fichero de descripción de conversacion.          """Crea una conversacion nueva a partir de un fichero de descripción de conversacion.
80                    
81          Requiere:          Requiere:
82            id != None            id != None
83            id corresponde con un fichero existente            id corresponde con un fichero existente
84          """          """
85                    
86          self.id = id          self.id = id
87          self.__inicializar_conversacion()          self.__inicializar_conversacion()
88    
89    
90      def __inicializar_conversacion(self):      def __inicializar_conversacion(self):
91          """Rellena todos los datos de la conversacion a partir de su fichero          """Rellena todos los datos de la conversacion a partir de su fichero
92          de descripcion.          de descripcion.
93          """          """
94          archivo = os.path.join(BloqueDialogo.DIR_DESC_DIALOGOS, self.id.split('-')[0], self.id + BloqueDialogo.EXT_DESC_DIALOGOS)          archivo = os.path.join(BloqueDialogo.DIR_DESC_DIALOGOS, self.id.split('-')[0], self.id + BloqueDialogo.EXT_DESC_DIALOGOS)
95          stream = open(archivo)          stream = open(archivo)
96          doc_xml = minidom.parse(stream).documentElement          doc_xml = minidom.parse(stream).documentElement
97          stream.close()          stream.close()
98    
99          self.__fecha_fichero = os.path.getmtime(archivo)          self.__fecha_fichero = os.path.getmtime(archivo)
100          self._sinonimos = {}          self._sinonimos = {}
101    
102          self._limpiar_comentarios(doc_xml)          self._limpiar_comentarios(doc_xml)
103          self.__parsear_conversacion(doc_xml)          self.__parsear_conversacion(doc_xml)
104          self.__parsear_id(doc_xml)                self.__parsear_id(doc_xml)      
105          self._tolerancia_offtopic = 1          self._tolerancia_offtopic = 1
106          self._probabilidad_offtopic_continuar = 100          self._probabilidad_offtopic_continuar = 100
107                    
108          especiales_xml = doc_xml.getElementsByTagName("temasEspeciales")          especiales_xml = doc_xml.getElementsByTagName("temasEspeciales")
109          if especiales_xml:          if especiales_xml:
110              self._temas_especiales = self._parsear_temas_especiales(especiales_xml[0])              self._temas_especiales = self._parsear_temas_especiales(especiales_xml[0])
111              offtopic = especiales_xml[0].getElementsByTagName("temaOfftopic")              offtopic = especiales_xml[0].getElementsByTagName("temaOfftopic")
112              if offtopic and offtopic[0].getAttribute("probabilidad"):              if offtopic and offtopic[0].getAttribute("probabilidad"):
113                  self._probabilidad_offtopic_continuar = string.atoi(offtopic[0].getAttribute("probabilidad_continuar"))                  self._probabilidad_offtopic_continuar = string.atoi(offtopic[0].getAttribute("probabilidad_continuar"))
114              if offtopic and offtopic[0].getAttribute("tolerancia"):              if offtopic and offtopic[0].getAttribute("tolerancia"):
115                  self._tolerancia_offtopic = string.atoi(offtopic[0].getAttribute("tolerancia"))                  self._tolerancia_offtopic = string.atoi(offtopic[0].getAttribute("tolerancia"))
116          else:          else:
117              self._temas_especiales = {"temaAgotado": [], "temaNoEncontrado": [], "temaOfftopic" : []}              self._temas_especiales = {"temaAgotado": [], "temaNoEncontrado": [], "temaOfftopic" : []}
118                    
119          self._contextos = self._parsear_contextos(doc_xml)          self._contextos = self._parsear_contextos(doc_xml)
120          self._indice_contextos = {}          self._indice_contextos = {}
121          self.__crear_indices()          self.__crear_indices()
122          self.__revisar_integridad(doc_xml)          self.__revisar_integridad(doc_xml)
123    
124          #Establece contextos iniciales          #Establece contextos iniciales
125          self._contextos_iniciales = []          self._contextos_iniciales = []
126          for nodo_contexto in doc_xml.getElementsByTagName("contextos")[0].getElementsByTagName("contexto"):          for nodo_contexto in doc_xml.getElementsByTagName("contextos")[0].getElementsByTagName("contexto"):
127              if nodo_contexto.getAttribute("inicial") == "si":              if nodo_contexto.getAttribute("inicial") == "si":
128                  self._contextos_iniciales.append(self.contexto(nodo_contexto.getAttribute("id")))                  self._contextos_iniciales.append(self.contexto(nodo_contexto.getAttribute("id")))
129                  filter(None, self._contextos_iniciales)                  filter(None, self._contextos_iniciales)
130          escribir("Cargada con éxito la conversación " + archivo)          escribir("Cargada con éxito la conversación " + archivo)
131    
132                    
133      def __parsear_conversacion(self, doc_xml):      def __parsear_conversacion(self, doc_xml):
134          """Extrae la información del elemento conversacion:          """Extrae la información del elemento conversacion:
135              pnj: el personaje al que pertenece la conversacion              pnj: el personaje al que pertenece la conversacion
136              autor: el autor de la conversacion              autor: el autor de la conversacion
137              version: la versión del tipo de conversacion"""              version: la versión del tipo de conversacion"""
138          version_desc_conversacion = doc_xml.attributes["version"].value.encode('ISO-8859-1')          version_desc_conversacion = doc_xml.attributes["version"].value.encode('ISO-8859-1')
139    
140          if version_desc_conversacion != BloqueDialogo.VERSION_DESC_DIALOGO:          if version_desc_conversacion != BloqueDialogo.VERSION_DESC_DIALOGO:
141              raise ValueError("Version incorrecta del fichero descriptor de conversacion " + self.id + ": " +              raise ValueError("Version incorrecta del fichero descriptor de conversacion " + self.id + ": " +
142                               version_desc_conversacion + " cuando se esperaba " + BloqueDialogo.VERSION_DESC_DIALOGO)                               version_desc_conversacion + " cuando se esperaba " + BloqueDialogo.VERSION_DESC_DIALOGO)
143    
144          self.version = BloqueDialogo.VERSION_DIALOGO          self.version = BloqueDialogo.VERSION_DIALOGO
145                    
146          self.autor = doc_xml.attributes["autor"].value.encode('ISO-8859-1')          self.autor = doc_xml.attributes["autor"].value.encode('ISO-8859-1')
147          self.pnj = doc_xml.attributes["pnj"].value.encode('ISO-8859-1')          self.pnj = doc_xml.attributes["pnj"].value.encode('ISO-8859-1')
148          try:          try:
149              self.comentario = doc_xml.attributes["comentario"].value.encode('ISO-8859-1')              self.comentario = doc_xml.attributes["comentario"].value.encode('ISO-8859-1')
150          except:          except:
151              self.comentario = "nada"              self.comentario = "nada"
152                    
153    
154      def __parsear_id(self, doc_xml):      def __parsear_id(self, doc_xml):
155          """Extrae el id de la conversacion"""          """Extrae el id de la conversacion"""
156          id_fichero = doc_xml.getElementsByTagName("id")[0].firstChild.nodeValue.encode('ISO-8859-1')          id_fichero = doc_xml.getElementsByTagName("id")[0].firstChild.nodeValue.encode('ISO-8859-1')
157          if id_fichero != self.id:          if id_fichero != self.id:
158              raise ValueError("El id no coincide con el nombre del fichero")              raise ValueError("El id no coincide con el nombre del fichero")
159                    
160            
161      def __crear_indices(self):      def __crear_indices(self):
162          """Crea las estructuras índice de contextos e índice de sinónimos."""          """Crea las estructuras índice de contextos e índice de sinónimos."""
163          # Acelera la busqueda de contextos por nombre          # Acelera la busqueda de contextos por nombre
164          for contexto in self._contextos:          for contexto in self._contextos:
165              self._indice_contextos[contexto.id] = contexto              self._indice_contextos[contexto.id] = contexto
166                            
167          # Acelera la busqueda y conversion de sinonimos          # Acelera la busqueda y conversion de sinonimos
168          for contexto in self._contextos:          for contexto in self._contextos:
169              for tema in contexto.temas():              for tema in contexto.temas():
170                  self._sinonimos[tema.nombre()] = tema.nombre()                  self._sinonimos[tema.nombre()] = tema.nombre()
171                  for sinonimo in tema._sinonimos:                  for sinonimo in tema._sinonimos:
172                      self._sinonimos[sinonimo] = tema.nombre()                      self._sinonimos[sinonimo] = tema.nombre()
173                                    
174      def __revisar_integridad(self, doc_xml):      def __revisar_integridad(self, doc_xml):
175          """Asegura la integridad de los índices y temas relacionados."""          """Asegura la integridad de los índices y temas relacionados."""
176          # Carga sinonimos adicionales definidos a nivel de conversacion, chequeando su integridad          # Carga sinonimos adicionales definidos a nivel de conversacion, chequeando su integridad
177          if doc_xml.getElementsByTagName("sinonimos"):          if doc_xml.getElementsByTagName("sinonimos"):
178              for nodo_tema in doc_xml.getElementsByTagName("sinonimos")[0].getElementsByTagName("tema"):              for nodo_tema in doc_xml.getElementsByTagName("sinonimos")[0].getElementsByTagName("tema"):
179                  nombretema = nodo_tema.getAttribute("nombre").encode('ISO-8859-1')                  nombretema = nodo_tema.getAttribute("nombre").encode('ISO-8859-1')
180                  #print str(nodo_tema) + " - " + str(nodo_tema.attributes.keys()) #getAttribute("nombre")                  #print str(nodo_tema) + " - " + str(nodo_tema.attributes.keys()) #getAttribute("nombre")
181                  # Comprueba si el tema existe                  # Comprueba si el tema existe
182                  if not self._sinonimos.has_key(nombretema):                  if not self._sinonimos.has_key(nombretema):
183                      escribir("Error de integridad en conversacion: definido sinonimo para tema '" + nombretema + "' no existente.")                      escribir("Error de integridad en conversacion: definido sinonimo para tema '" + nombretema + "' no existente.")
184                      continue                      continue
185                  # Completa el índice de sinónimos con los sinónimos definidos en el tema.                  # Completa el índice de sinónimos con los sinónimos definidos en el tema.
186                  for sinonimo in self._parsear_sinonimos(nodo_tema):                  for sinonimo in self._parsear_sinonimos(nodo_tema):
187                      self._sinonimos[sinonimo] = nombretema                      self._sinonimos[sinonimo] = nombretema
188    
189          # Comprueba la integridad de los temas relacionados de cada item          # Comprueba la integridad de los temas relacionados de cada item
190          for contexto in self._contextos:          for contexto in self._contextos:
191              for tema  in  contexto.temas():              for tema  in  contexto.temas():
192                  for item   in   tema.items():                  for item   in   tema.items():
193                      eliminarEnlaces = []                      eliminarEnlaces = []
194                      # Por cada enlace de cada item                      # Por cada enlace de cada item
195                      for enlace in item.relacionados():                      for enlace in item.relacionados():
196                          nombrecontexto = item.relacionados()[enlace]                          nombrecontexto = item.relacionados()[enlace]
197                          if self._indice_contextos.has_key(nombrecontexto):                          if self._indice_contextos.has_key(nombrecontexto):
198                              # Si el contexto destino no contiene un tema con el nombre enlazado...                              # Si el contexto destino no contiene un tema con el nombre enlazado...
199                              if not self._indice_contextos[nombrecontexto].tema(enlace): # not enlace in map(lambda tema: tema.nombre(), self._indice_contextos[nombrecontexto].temas()):                              if not self._indice_contextos[nombrecontexto].tema(enlace): # not enlace in map(lambda tema: tema.nombre(), self._indice_contextos[nombrecontexto].temas()):
200                                  eliminarEnlaces.append(enlace)                                  eliminarEnlaces.append(enlace)
201                                  escribir("Error de integridad en conversacion: definido enlace para tema '" \                                  escribir("Error de integridad en conversacion: definido enlace para tema '" \
202                                        + enlace + "' que no pertenece al contexto '" + nombrecontexto + "'.")                                        + enlace + "' que no pertenece al contexto '" + nombrecontexto + "'.")
203                              else:                              else:
204                                  pass                                  pass
205                          else:                          else:
206                              eliminarEnlaces.append(enlace)                              eliminarEnlaces.append(enlace)
207                              escribir("Error de integridad en conversacion: definido enlace para tema '" \                              escribir("Error de integridad en conversacion: definido enlace para tema '" \
208                                     + enlace + "' en el contexto '" + nombrecontexto + "' no existente.")                                     + enlace + "' en el contexto '" + nombrecontexto + "' no existente.")
209                      # Borra los enlaces incorrectos                      # Borra los enlaces incorrectos
210                      for enlace in eliminarEnlaces:                      for enlace in eliminarEnlaces:
211                          del (item._relacionados[enlace])                          del (item._relacionados[enlace])
212            
213      def contexto(self, nombrecontexto):      def contexto(self, nombrecontexto):
214          """Devuelve el contexto con ese nombre."""          """Devuelve el contexto con ese nombre."""
215          return self._indice_contextos[nombrecontexto]          return self._indice_contextos[nombrecontexto]
216    
217      def contextos(self):      def contextos(self):
218          return self._contextos          return self._contextos
219    
220      def temas_especiales(self):      def temas_especiales(self):
221          return self._temas_especiales          return self._temas_especiales
222    
223      def tolerancia(self):      def tolerancia(self):
224          return self._tolerancia_offtopic          return self._tolerancia_offtopic
225    
226      def probabilidad_cancelacion(self):      def probabilidad_cancelacion(self):
227          return 100 - self._probabilidad_offtopic_continuar          return 100 - self._probabilidad_offtopic_continuar
228                    
229      def __str__(self):      def __str__(self):
230          texto = '<Conversacion - "' + self.id          texto = '<Conversacion - "' + self.id
231          texto = texto + '"\n\n  Contextos:\n'          texto = texto + '"\n\n  Contextos:\n'
232          for contexto in self._contextos:          for contexto in self._contextos:
233              if contexto in self._contextos_iniciales:              if contexto in self._contextos_iniciales:
234                  texto = texto + "  (inicial)\n"                  texto = texto + "  (inicial)\n"
235              texto = texto + str(contexto) + "\n"              texto = texto + str(contexto) + "\n"
236          texto = texto + '  Temas especiales:\n'          texto = texto + '  Temas especiales:\n'
237          for tipo in self._temas_especiales:          for tipo in self._temas_especiales:
238              texto = texto + "\tTipo: " + tipo + "\n"              texto = texto + "\tTipo: " + tipo + "\n"
239              for tema in self._temas_especiales[tipo]:              for tema in self._temas_especiales[tipo]:
240                  tema._nombre = ''                  tema._nombre = ''
241                  tema._sinonimos = []                  tema._sinonimos = []
242                  texto = texto + str(tema) + "\n"                  texto = texto + str(tema) + "\n"
243          texto = texto + "/>\n"          texto = texto + "/>\n"
244          return texto          return texto
245            
246            
247    
248    
249  # Código para pruebas unitarias del módulo.  # Código para pruebas unitarias del módulo.
250    
251  if (__name__ == '__main__'):  if (__name__ == '__main__'):
252      ejemplo = Conversacion("tutora")      ejemplo = Conversacion("tutora")
253      print "\n" + str(ejemplo) + "\n"      print "\n" + str(ejemplo) + "\n"
254      print ejemplo._contextos_iniciales      print ejemplo._contextos_iniciales
255  #    print "No hay prueba unitaria de módulo. "  #    print "No hay prueba unitaria de módulo. "

Legend:
Removed from v.2.3  
changed lines
  Added in v.2.4

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26