Seite 1 von 1

Ausgabe von Formatinformationen und Inhalt von OOo .odt-Date

Verfasst: Donnerstag 7. August 2008, 17:25
von genders
Diese Vorgangsweise ist für mich Neuland und ich weiss nicht ob alles korrekt ist.

Ist leider noch ein frühes Stadium des Programms, hat aber bereits einiges Lernen und einige Arbeit erfordert, gibt aber bereits einige Antworten auf meine Fragen. Ich will mehr über die 'harte' und 'weiche' Formatierung verstehen bzw. das Dokument auch maschinell verändern sowie kontrolliert in anderen Formaten ausgeben können. Liefert bei entsprechender Einstellung von .print_flag sehr viel Angaben. Bitte um Anregungen und Verbesserungsvorschläge

Code: Alles auswählen

#!/usr/bin/env python 
# -*- coding: iso-8859-15 -*-

'''Tool for an analyse of XML-Dokuments

Gerhard Enders
start 2008.07.18
working version 2008.08.07

To Do:
an other solution instaed of 'c_' 's_' for marking style source, to show equal
  styles in style and content, perhaps new attribute 'source'
check for .zip-files
many other
'''

import os
import sys
import zipfile
import xml.dom.minidom

#------------------------------------------------------------------------------
class HandleOpenDocumentFiles:
  '''.zip files

  it shoud bee more general, odf is a special case  
  '''
  #----------------------------------------------------------------------------
  def __init__ (self, odf_filename):
    '''Get a valid ODF file

    '''
    if len(sys.argv) > 1 :
      fpos = str(os.getcwd())
      self.odf_file = fpos + '\\' + odf_filename
    else :
      print 'Sorry, you forgot to give an ODF file name.'
    '''
    # test if the file is a valid pkzip file
    #
    if zipfile.is_zipfile(zfilename):
    #
      print "%s is a valid pkzip file" % zfilename
    #
    else:
    #
      print "%s is not a valid pkzip file" % zfilename
    #
    '''
  #----------------------------------------------------------------------------
  def get_odf_component(self, component_name):
    '''Get a named component from an ODF file.

    '''
    odf_zipfile = zipfile.ZipFile(self.odf_file, 'r')
    component = odf_zipfile.read(component_name)
    odf_zipfile.close()
    return component
  #----------------------------------------------------------------------------
  def print_odf_zipfile_info(self) :
    '''Print the names of the zip components in the file.

    '''
    total_compressed_size = 0
    total_uncompressed_size = 0
    odf_zipfile = zipfile.ZipFile(self.odf_file, 'r')
    self.print_separator()
    print 'The components in the %s ODF zip file are:\n' % (self.odf_file)
    print '  %-40s %12s %12s'   % ('Name', 'Compressed', 'Uncompressed')
    print '  %-40s %12s %12s\n' % (' ', 'Size', 'Size')
    for info in odf_zipfile.infolist():
      total_compressed_size += info.compress_size
      total_uncompressed_size += info.file_size
      print '  %-40s %12d %12d' % (info.filename,
                                   info.compress_size,
                                   info.file_size)
    print '  %-40s %12s %12s' % (' ', '--------', '--------')
    print '  %-40s %12d %12d' % ('Total',
                                 total_compressed_size,
                                 total_uncompressed_size),
    odf_zipfile.close()
    self.print_separator()
    return total_compressed_size
  #----------------------------------------------------------------------------
  def print_separator(self):
    print '\n' + 79 * '-'
  #----------------------------------------------------------------------------
  def rewrite_zipfile(self, name, old):
    '''
    '''
    new_name = name + '.zip'
    new_zipfile = zipfile.ZipFile(new_name, 'w')
    old_zipfile = zipfile.ZipFile(old, 'r')
    old_zipfile.close
    new_zipfile.close
  #----------------------------------------------------------------------------
  def print_attr (self, list):
    attr = []
    for item in range(len(list)):
      print type(item)
#------------------------------------------------------------------------------
class AnalyseXmlDocument:
  '''Tools for an analyse of a XML-Document

  '''
  def __init__(self):
    ''' '''
    self.ntype = [
      '',
      'ELEMENT_NODE', 
      'ATTRIBUTE_NODE',
      'TEXT_NODE',
      'CDATA_SECTION_NODE', 
      'ENTITY_REFERENCE_NODE'
      'ENTITY_NODE',
      'PROCESSING_INSTRUCTION_NODE',
      'COMMENT_NODE',
      'DOCUMENT_NODE',
      'DOCUMENT_TYPE_NODE', 
      'DOCUMENT_FRAGMENT_NODE',
      'NOTATION_NODE'
      ]
    self.attr_names = [
      'style:name',
      'style:font-name',
      'style:list-style-name',
      'text:name',
      'text:style-name'
      ]
    self.document_content = ''
    self.floor = 0
    self.node_hash = {}
    self.stairs = {}
    self.print_flag = False
    self.text_flag = False
  #----------------------------------------------------------------------------
  def walkingThroughDocument(self, element, part):
    '''Recursive walking through the whole Element
    
    The actual element is always a node
    '''
    self.documentNode(element, part)
    self.floor += 1
    for node in element.childNodes:
      self.walkingThroughDocument(node, part)
    self.floor -= 1
    # take care of formating end
    if self.text_flag is True and self.stairs[self.floor] <> '#text':
      self.document_content += self.formatingInfo('END', self.floor)
    return
  #----------------------------------------------------------------------------
  def formatingInfo(self, case, floor):
    return '%2s' %(floor) + ' ' + case + ': ' + self.stairs[floor] + '\n'
  #----------------------------------------------------------------------------
  def printInfo(self, info):
    if self.print_flag is True:
      print info
    return
  #----------------------------------------------------------------------------
  def walkingThroughDocumentFinish(self):
    self.text_flag = False
  #----------------------------------------------------------------------------
  def documentNode(self, node, part):
    '''Print all information of the node

    '''
    self.printInfo(str(self.floor) + ' ' + unicode(node))
    self.printInfo('Type      : ' + str(node.nodeType) + ' ' + self.ntype[node.nodeType])
    self.printInfo('NodeName  : ' + str(node.nodeName) + ' ' + str(node.localName))
    attr = {}
    name = node.nodeName
    if node.nodeType <> node.TEXT_NODE:
      if self.print_flag is True: print 'Attributes:',
      if node.hasAttributes():
        d = node.attributes.keys()
        if self.print_flag is True: print len(d), d
        for item in d:
          self.printInfo("  %-35s %-15s " % (item, node.getAttribute(item)))
          attr[item] = node.getAttribute(item)
          if item in self.attr_names:
            name += ('_' + node.getAttribute(item))
            self.printInfo(node.getAttribute(item))
          else:
            pass
      else:
        self.printInfo('None')
      try:
        self.node_hash[part + name].append(attr)
      except KeyError:
        self.node_hash[part + name] = [attr]
      self.printInfo(attr)
    # update floor-name
    try:
      self.stairs[self.floor] = name
    except Keyerror:
      self.stairs[self.floor] = name
    # take care of formating start
    if self.text_flag is True and self.stairs[self.floor] <> '#text':
      self.document_content += self.formatingInfo('BEG', self.floor)
    # take care of text content
    if node.nodeType == node.TEXT_NODE:
      self.printInfo('Content   : ' + node.nodeValue.strip())
      if self.text_flag is False and self.floor > 1:
        for i in range(self.floor - 1):
          self.document_content += self.formatingInfo('BEG', i)
      self.text_flag = True
      self.document_content += (node.nodeValue.strip() + '\n')
      self.printInfo('')
    return
  #----------------------------------------------------------------------------
  def print_attr(self, liste):
    '''Print table of a list of directories

    Testversion  
    '''
    attr = []                            # is a list with all attributes
    attr_len = 0                         # largest length of one of the attributes
    value_len = 0                        # largest length of one of the values
    for item in range(len(liste)):
      act_dir = liste[item].keys()       # is a directory with attributes
      for elem in act_dir:
        if elem not in attr:
          attr.append(elem)
          act_alen = len(elem)
          if act_alen > attr_len:
            attr_len = act_alen
        act_vlen = len(liste[item][elem])
        if act_vlen > value_len:
          value_len = act_vlen
    attr.sort()
    mask1 = '%-' + str(attr_len) + 's'
    mask2 = '%-' + str(value_len) + 's'
    print
    for j in range(len(attr)):
      print mask1 %(attr[j]),
      for item in range(len(liste)):
        if liste[item].has_key(attr[j]):
          print mask2 %(liste[item][attr[j]]),
        else:
          print mask2 %('n.e.'),
      print
    print
    return
#------------------------------------------------------------------------------
if __name__ == "__main__":
  """  """
  odt = HandleOpenDocumentFiles(sys.argv[1])
  odt.print_odf_zipfile_info()
  # odt.rewrite_zipfile('ZipNeu', odt.odf_file)
  analyse = AnalyseXmlDocument()
  print
  dom = xml.dom.minidom.parseString(odt.get_odf_component('content.xml'))
  analyse.walkingThroughDocument(dom.documentElement, 'c_')
  analyse.walkingThroughDocumentFinish()
  dom = xml.dom.minidom.parseString(odt.get_odf_component('styles.xml'))
  analyse.walkingThroughDocument(dom.documentElement, 's_')
  analyse.walkingThroughDocumentFinish()
  if analyse.print_flag is True:
    print analyse.node_hash
    print
    print
  keys = analyse.node_hash.keys()
  keys.sort()
  analyse.print_flag = True
  if analyse.print_flag is True:
    print keys
    for item in keys:
      print item + ':' , len(analyse.node_hash[item])
      analyse.print_attr(analyse.node_hash[item])
    print 
    print
  print analyse.document_content
  
Edit: END > BEG

Verfasst: Donnerstag 7. August 2008, 20:28
von BlackJack
Üblich sind vier Leerzeichen pro Einrückungsebene. Ein paar Zeilen sind länger als 80 Zeichen.

Der ``<>``-Operator ist veraltet und sollte nicht mehr verwendet werden. In Python 3.0 gibt's den nicht mehr.

Mehrzeilige Zeichenketten zum auskommentieren von Quelltext zu verwenden ist relativ unüblich. Editoren bieten normalerweise die Möglichkeit mehrere Zeilen mit den normalen Kommentarzeichen ein und aus zu kommentieren.

Die Klassennamen sind schlecht gewählt. Klassen beschreiben Objekte, sollten also mit Hauptwörtern benannt werden. Verben sind für Funktionen und Methoden gedacht.

`HandleOpenDocumentFiles.__init__()` ist verwirrend. Es wird getestet, ob dem Programm mindestens ein Argument angegeben wurde, aber das ist an der Stelle völlig irrelevant, weil das Argument da gar nicht verwendet wird. Dann wird das Ergebnis von ``os.getcwd()`` in eine Zeichenkette umgewandelt, was unnötig ist, weil die Funktion schon eine Zeichenkette liefert. Danach kommt dann ein echter Fehler: '\\' als Pfadtrenner funktioniert nicht plattformübergreifend. Dafür gibt es `os.path.join()`. Wobei diese Aktion sowieso etwas sinnfrei ist, man muss ja gar nicht das aktuelle Arbeitsverzeichnis vor den Pfad hängen. Falls es doch gewünscht ist, würde ich eher `os.path.abspath()` oder `os.path.normpath()` verwenden, statt selber etwas zu schreiben.

`HandleOpenDocumentFiles.print_separator()` ist eine allgemeine Funktion und keine Methode.

`HandleOpenDocumentFiles.rewrite_zipfile()` ist auch eine Funktion. Ausserdem werden die Dateien nicht geschlossen. Es reicht nicht die `close`-Methode zu referenzieren, man muss sie auch *aufrufen*. Aber die Funktion scheint mir nichts sinnvolles zu tun!?

`HandleOpenDocumentFiles.print_attr()` ist auch wieder keine Methode und extremst sinnlos. WTF!?

In `AnalyseXmlDocument` haben die Methoden "unpythonische" Namen. Die Konvention ist kleinbuchstaben_mit_unterstrichen.

``is`` sollte man nur Verwenden, wenn man wirklich auf Objektidentität testen möchte. Nicht um auf Werte wie `True` oder `False` zu testen, insbesondere weil es in Python viele Werte gibt, die einen "Wahrheitsgehalt" haben. Bei `True` und `False` in Bedingungen sollte man sowieso nicht explizit testen, das ist überflüssig.

Zeichenketten nach und nach mit ``+=`` auf zu bauen ist ineffizient, weil bei jeder dieser Operationen eine neue Zeichenkette erzeugt wird, die aus Kopien der beiden alten besteht.

Ein ``return`` ohne expliziten Wert am Ende einer Funktion ist überflüssig. Genauso wie ``else``-Zweige, die nur aus einem ``pass`` bestehen.

`documentNode()` ist für meinen Geschmack etwas zu komplex. Das sollte man vereinfachen, oder aufteilen. Es gibt kein `Keyerror`, die Ausnahme heisst `KeyError`. `d` für den Namen einer Liste?

`print_attr()` kann man auch vereinfachen. Wofür steht das `dir` in `act_dir`? Das ist eine Liste mit Schlüsseln!? Die ganze Indexerei ist scheusslich und Schleifen über ``range(len(obj))`` sind in der Regel ein Zeichen, dass es einfacher geht, indem man direkt über die Elemente in `obj` iteriert, statt den Umweg über einen Index zu nehmen.

Nach dem ``%``-Operator fehlt jedesmal ein Leerzeichen. Warum lässt Du die ausgerechnet dort immer weg?

XML würde ich mit `ElementTree` oder `lxml` verarbeiten, statt der eher unschönen DOM-API. Und ich glaube auch nicht, dass Dein Programm besonders gut mit Unicode klar kommt.

Verfasst: Donnerstag 7. August 2008, 21:26
von genders
Danke für die rasche und ausführliche Antwort, einiges werde ich kurzfristig bereinigen, bei einigen Punkten werde ich noch dazulernen müssen einiges wird länger dauern. Vorerst geht es mir um rasche Ergebnisse, aber ich will dazu lernen.

Ich arbeite mit PythonWin und weiss nicht ob ich den Funktionsumfang schon voll ausreize.

Bezüglich der vier Zeichen Einrückungen aber habe ich mich grundsätzlich für zwei entschieden.

mfg

Verfasst: Donnerstag 7. August 2008, 22:04
von BlackJack
Gerade eine einheitliche Einrückung ist aber wichtig. So baust Du eine Barriere auf, bei der Du keinen Quelltext von anderen übernehmen kannst, und auch niemand welchen von Dir, ohne alles neu Einrücken zu müssen.