import xmltodict
import glob
from extraction.runnables import Extractor, Filter, RunnableError

class ExtractionRunner(object):
   def __init__(self):
      self.filters = []
      self.extractors = []
      self.runnables = []

   def add_runnable(self, runnable):
      #TODO we should construct a graph of filters and extractors
      # then if there are any circular dependencies we can throw an error
      # we can also remove the requirement to add runnables in order
      # these comments also apply to the add_extractor method
      self.runnables.append(runnable)

      if issubclass(runnable, Extractor):
         self.extractors.append(runnable)
      if issubclass(runnable, Filter):
         self.filters.append(runnable)


   def run(self, data, pretty=False, filename=None):
      results = {}
      for runnable in self.runnables:
         dep_results = dict(filter(lambda (k,v): k in runnable.dependencies(), results.items()))

         try:
            result = runnable().run(data, dep_results)
         except RunnableError as err:
            result = err

         results[runnable] = result

      doc = '<?xml version="1.0" encoding="utf-8"?>\n'
      if filename:
         doc += '<extraction file="{0}">'.format(filename)
      else:
         doc += '<extraction>'

      doc += '<filters>'
      for filt in self.filters:
         doc += '<{0}>'.format(filt.__name__)
         doc += self._result_to_string(results[filt])
         doc += '</{0}>'.format(filt.__name__)
      doc += '</filters>'

      doc += '<extractors>'
      for extractor in self.extractors:
         doc += '<{0}>'.format(extractor.__name__)
         doc += self._result_to_string(results[extractor])
         doc += '</{0}>'.format(extractor.__name__)
      doc += '</extractors>'
         
      doc += '</extraction>'
      if pretty:
         doc = xmltodict.unparse(xmltodict.parse(doc), pretty=True)
      return doc

   def run_from_file(self, path, pretty=False):
      return self.run(open(path, 'rb').read(), pretty=pretty, filename=path)

   def run_batch(self, list_of_data, pretty=False):
      for data in list_of_data:
         yield self.run(data, pretty=pretty)

   def run_batch_from_glob(self, dir_glob, pretty=False):
      for path in glob.iglob(dir_glob):
         yield (path, self.run_from_file(path, pretty=pretty))

   def _result_to_string(self, result):
      if isinstance(result, dict):
         return '<result>{0}</result>'.format(xmltodict.unparse(result, full_document=False))
      elif isinstance(result, RunnableError):
         return '<error>{0}</error>'.format(result.msg)
      else:
         return '<result>{0}</result>'.format(result)



@@ -1123,16 +1123,17 @@
   doc = 
+u
 '%3C?xml v
@@ -1194,32 +1194,33 @@
          doc += 
+u
 '%3Cextraction fil
@@ -1266,32 +1266,33 @@
          doc += 
+u
 '%3Cextraction%3E'%0A%0A
@@ -1304,16 +1304,17 @@
  doc += 
+u
 '%3Cfilter
@@ -1357,32 +1357,33 @@
          doc += 
+u
 '%3C%7B0%7D%3E'.format(f
@@ -1458,32 +1458,33 @@
          doc += 
+u
 '%3C/%7B0%7D%3E'.format(
@@ -1511,16 +1511,17 @@
  doc += 
+u
 '%3C/filte
@@ -1539,16 +1539,17 @@
  doc += 
+u
 '%3Cextrac
@@ -1611,16 +1611,17 @@
  doc += 
+u
 '%3C%7B0%7D%3E'.
@@ -1722,16 +1722,17 @@
  doc += 
+u
 '%3C/%7B0%7D%3E'
@@ -1764,32 +1764,33 @@
 _)%0A      doc += 
+u
 '%3C/extractors%3E'%0A
@@ -1812,16 +1812,17 @@
  doc += 
+u
 '%3C/extra
@@ -2433,32 +2433,33 @@
          return 
+u
 '%3Cresult%3E%7B0%7D%3C/re
@@ -2582,16 +2582,17 @@
  return 
+u
 '%3Cerror%3E
@@ -2651,16 +2651,17 @@
  return 
+u
 '%3Cresult

Use unicode strings instead of ascii