249 lines
7.5 KiB
Python
249 lines
7.5 KiB
Python
#!/usr/bin/env python
|
|
#coding:utf-8
|
|
# Purpose: ODF meta.xml document management
|
|
# Created: 28.12.2010
|
|
# Copyright (C) 2010, Manfred Moitzi
|
|
# License: MIT license
|
|
from __future__ import unicode_literals, print_function, division
|
|
__author__ = "mozman <mozman@gmx.at>"
|
|
|
|
from datetime import datetime
|
|
|
|
from .compatibility import tostr
|
|
from .xmlns import CN, subelement, etree, register_class, XMLMixin
|
|
from .const import META_NSMAP, GENERATOR, META_NS
|
|
|
|
TAGS = {
|
|
'generator': 'meta:generator',
|
|
'title': 'dc:title',
|
|
'description': 'dc:description',
|
|
'subject': 'dc:subject',
|
|
'initial-creator': 'meta:initial-creator',
|
|
'creator': 'dc:creator',
|
|
'creation-date': 'meta:creation-date',
|
|
'date': 'dc:date',
|
|
'editing-cycles': 'meta:editing-cycles',
|
|
'language': 'dc:language',
|
|
}
|
|
|
|
@register_class
|
|
class OfficeDocumentMeta(XMLMixin):
|
|
TAG = CN('office:document-meta')
|
|
generator = GENERATOR
|
|
|
|
def __init__(self, xmlnode=None):
|
|
if xmlnode is None:
|
|
self.xmlnode = etree.Element(self.TAG, nsmap=META_NSMAP)
|
|
elif xmlnode.tag == self.TAG:
|
|
self.xmlnode = xmlnode
|
|
else:
|
|
raise ValueError("Unexpected root node: %s" % xmlnode.tag)
|
|
|
|
self._setup()
|
|
self.keywords = Keywords(self.meta)
|
|
self.usertags = Usertags(self.meta)
|
|
|
|
stats = self.meta.find(CN('meta:document-statistic'))
|
|
if stats is None:
|
|
stats = etree.SubElement(self.meta, CN('meta:document-statistic'))
|
|
self.count = Statistic(stats)
|
|
|
|
def _setup(self):
|
|
self.meta = self.xmlnode.find(CN('office:meta'))
|
|
if self.meta is None: # this is a new document
|
|
self.meta = subelement(self.xmlnode, CN('office:meta'))
|
|
self.xmlnode.set(CN('grddl:transformation'), "http://docs.oasis-open.org/office/1.2/xslt/odf2rdf.xsl")
|
|
self['creation-date'] = datetime.now().isoformat()
|
|
self.touch()
|
|
|
|
def clear(self):
|
|
""" Delete all metatags. """
|
|
self.meta.clear()
|
|
self.count.stats = etree.SubElement(self.meta, CN('meta:document-statistic'))
|
|
|
|
def touch(self):
|
|
self['date'] = datetime.now().isoformat()
|
|
self['generator'] = OfficeDocumentMeta.generator
|
|
|
|
def __setitem__(self, key, value):
|
|
cnkey = CN(TAGS[key]) # key in clark notation
|
|
element = subelement(self.meta, cnkey)
|
|
element.text = value
|
|
|
|
def __getitem__(self, key):
|
|
element = self.meta.find(CN(TAGS[key]))
|
|
if element is not None:
|
|
return element.text
|
|
else:
|
|
raise KeyError(key)
|
|
|
|
def inc_editing_cycles(self):
|
|
try:
|
|
count = self['editing-cycles']
|
|
try:
|
|
count = int(count) + 1
|
|
except ValueError:
|
|
count = 1
|
|
except KeyError:
|
|
count = 1
|
|
self['editing-cycles'] = tostr(count)
|
|
|
|
class Keywords(object):
|
|
def __init__(self, meta):
|
|
self.meta = meta
|
|
|
|
def __iter__(self):
|
|
""" Iterate over all keywords. """
|
|
for keyword in self.meta.findall(CN('meta:keyword')):
|
|
yield keyword.text
|
|
|
|
def __contains__(self, keyword):
|
|
""" True if 'keyword' exists, else False. """
|
|
return self._find(keyword) is not None
|
|
|
|
def add(self, keyword):
|
|
""" Add 'keyword' to meta data. """
|
|
tag = self._find(keyword)
|
|
if tag is None:
|
|
tag = etree.SubElement(self.meta, CN('meta:keyword'))
|
|
tag.text = keyword
|
|
|
|
def remove(self, keyword):
|
|
""" Remove 'keyword' from meta data. """
|
|
tag = self._find(keyword)
|
|
if tag is not None:
|
|
self.meta.remove(tag)
|
|
|
|
def clear(self):
|
|
""" Delete all keywords. """
|
|
for tag in self.meta.findall(CN('meta:keyword')):
|
|
self.meta.remove(tag)
|
|
|
|
def _find(self, keyword):
|
|
""" Find XML element for `keyword`. """
|
|
for tag in self.meta.findall(CN('meta:keyword')):
|
|
if keyword == tag.text:
|
|
return tag
|
|
return None
|
|
|
|
class Usertags(object):
|
|
def __init__(self, meta):
|
|
self.meta = meta
|
|
|
|
def __iter__(self):
|
|
""" Iterate over all user-defined metatags.
|
|
|
|
:returns: (name, value) tuples
|
|
"""
|
|
for metatag in self.meta.findall(CN('meta:user-defined')):
|
|
yield (metatag.get(CN('meta:name')), metatag.text)
|
|
|
|
def __contains__(self, name):
|
|
return self._find(name) is not None
|
|
|
|
def set(self, name, value, value_type=None):
|
|
""" Set/Replace user-defined metatag.
|
|
"""
|
|
tag = self._find(name)
|
|
if tag is None:
|
|
tag = etree.SubElement(self.meta, CN('meta:user-defined'))
|
|
tag.set(CN('meta:name'), name)
|
|
tag.text = tostr(value)
|
|
if value_type is not None:
|
|
tag.set(CN('meta:value-type'), value_type)
|
|
|
|
def __setitem__(self, name, value):
|
|
self.set(name, value)
|
|
|
|
def __getitem__(self, name):
|
|
""" Get value of user-defined metatag 'name'.
|
|
|
|
Raises KeyError, if 'name' not exist.
|
|
"""
|
|
tag = self._find(name)
|
|
if tag is not None:
|
|
return tag.text
|
|
raise KeyError(name)
|
|
|
|
def __delitem__(self, name):
|
|
""" Remove user defined metatag 'name'.
|
|
|
|
Raises KeyError, if 'name' not exist.
|
|
"""
|
|
tag = self._find(name)
|
|
if tag is not None:
|
|
self.meta.remove(tag)
|
|
else:
|
|
raise KeyError(name)
|
|
|
|
def typeof(self, name):
|
|
""" Get type of user defined tag `name`. """
|
|
tag = self._find(name)
|
|
if tag is not None:
|
|
return tag.get(CN('meta:value-type'), 'string')
|
|
raise KeyError(name)
|
|
|
|
def update(self, d):
|
|
""" Set user defined tags from dict `d`. """
|
|
for key, value in d.items():
|
|
self.__setitem__(key, value)
|
|
|
|
def clear(self):
|
|
""" Delete all user defined tags. """
|
|
for tag in self.meta.findall(CN('meta:user-defined')):
|
|
self.meta.remove(tag)
|
|
|
|
def _find(self, name):
|
|
for tag in self.meta.findall(CN('meta:user-defined')):
|
|
if name == tag.get(CN('meta:name')):
|
|
return tag
|
|
return None
|
|
|
|
class Statistic(object):
|
|
TYPES = frozenset(['page', 'table', 'draw', 'image', 'object',
|
|
'ole-object', 'paragraph', 'word', 'character',
|
|
'row', 'frame', 'sentence', 'syllable',
|
|
'non-whitespace-character', 'cell'])
|
|
NS = '{' + META_NS + '}%s-count'
|
|
|
|
def __init__(self, stats):
|
|
self.stats = stats
|
|
|
|
def __getitem__(self, key):
|
|
if key in Statistic.TYPES:
|
|
val = self.stats.get(Statistic.NS % key)
|
|
retval = 0
|
|
try:
|
|
retval = int(val)
|
|
except ValueError:
|
|
pass # it's not an int, should not happen (but shit happens)
|
|
except TypeError:
|
|
pass # None, no stats for `key`
|
|
return retval
|
|
else:
|
|
raise KeyError(key)
|
|
|
|
def __setitem__(self, key, value):
|
|
if key in Statistic.TYPES:
|
|
self.stats.set(Statistic.NS % key, tostr(value))
|
|
else:
|
|
raise KeyError(key)
|
|
|
|
def __iter__(self):
|
|
""" Iterate over all statistics.
|
|
|
|
:returns: (name, value) tulples
|
|
"""
|
|
prefix = len(META_NS) + 2
|
|
for key, value in self.stats.items():
|
|
yield (key[prefix:-6], int(value))
|
|
|
|
def update(self, d):
|
|
""" Set statistics from dict `d`. """
|
|
for key, value in d.items():
|
|
self.__setitem__(key, value)
|
|
|
|
def clear(self):
|
|
""" Clear all statistics. """
|
|
self.stats.clear()
|