This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
pfwbged.tabellio/src/pfwbged/tabellio/documents.py

309 lines
9.4 KiB
Python

import datetime
import os
import psycopg2.extras
import urllib2
import urlparse
from Acquisition import aq_chain
from plone import api
from five import grok
from zope.interface import implements
from AccessControl import ClassSecurityInfo, getSecurityManager
from AccessControl.Permissions import access_contents_information
from zope import schema
from plone.dexterity.content import Container, Item
from plone.supermodel import model
from plone.namedfile.file import NamedBlobFile
from collective.dms.basecontent.dmsdocument import IDmsDocument, DmsDocument
from collective.dms.basecontent.dmsfile import DmsFile
from . import typenames
from . import _
def db_connection():
return api.portal.get().db._wrapper.connection
class IDocumentsFolder(model.Schema):
pass
class DocumentsFolder(Container):
implements(IDocumentsFolder)
security = ClassSecurityInfo()
_v_dbcache = None # volatile attribute
def __iter__(self):
if not self._v_dbcache:
self._v_dbcache = {}
cursor = db_connection().cursor(cursor_factory=psycopg2.extras.NamedTupleCursor)
cursor.execute('''SELECT id, ts, intit, textdefts
FROM t_document''')
while True:
row = cursor.fetchone()
if row is None:
break
rowid, ts, intit, textdfts = row
self._v_dbcache[rowid] = {'ts': ts, 'intit': intit, 'textdfts': textdfts}
yield rowid
cursor.close()
def __getitem__(self, id):
try:
int(id)
except ValueError:
pass
else:
t = Document.from_db(id)
return aq_chain(t.__of__(self))[0]
return super(DocumentsFolder, self).__getitem__(id)
def keys(self):
return list(self.__iter__())
def getFolderContents(self, *args, **kwargs):
folder = self
parent_path = '/'.join(folder.getPhysicalPath()) + '/'
parent_url = folder.absolute_url() + '/'
class FakeBrain(object):
def __init__(self, object_id):
self.id = object_id
self._object = None
self.getId = object_id
self.getObjSize = 0
self.is_folderish = True
self.portal_type = Document._v_portal_type
self.Creator = 'Tabellio'
def Type(self):
return self.portal_type
def get_object_attibute(self, attribute):
if not self._object:
self._object = folder[self.id]
return getattr(self._object, attribute)
@property
def modified(self):
if folder._v_dbcache and self.id in folder._v_dbcache:
return folder._v_dbcache[self.id]['ts']
return datetime.datetime.now()
def ModificationDate(self):
return self.modified
@property
def CreationDate(self):
if folder._v_dbcache and self.id in folder._v_dbcache:
t = folder._v_dbcache[self.id]['textdfts']
if t:
return t
return datetime.datetime.now()
@property
def title(self):
if folder._v_dbcache and self.id in folder._v_dbcache:
return folder._v_dbcache[self.id]['intit']
return self.get_object_attibute('title')
def pretty_title_or_id(self):
if self.title:
return self.title
return self.id
def Title(self):
return self.title
review_state = None
Description = None
def getPath(self):
return parent_path + self.id
def getURL(self, relative=True):
if relative:
return self.id
return parent_url + self.id
# plone.batching.batch doesn't support generators
t = []
for k in self.__iter__():
t.append(FakeBrain(k))
return t
# Acquisition wrappers don't support the __iter__ slot, so re-implement
# iteritems to call __iter__ directly.
def iteritems(self):
for k in self.__iter__():
yield (k, self[k])
security.declareProtected(access_contents_information,
'objectIds')
def objectIds(self, spec=None):
# Returns a list of subobject ids of the current object.
# If 'spec' is specified, returns objects whose meta_type
# matches 'spec'.
assert spec is None, 'spec argument unsupported'
return self.__iter__()
security.declareProtected(access_contents_information,
'objectValues')
def objectValues(self, spec=None):
# Returns a list of actual subobjects of the current object.
# If 'spec' is specified, returns only objects whose meta_type
# match 'spec'.
assert spec is None, 'spec argument unsupported'
for k in self.__iter__():
yield self[k]
security.declareProtected(access_contents_information,
'objectItems')
def objectItems(self, spec=None):
# Returns a list of (id, subobject) tuples of the current object.
# If 'spec' is specified, returns only objects whose meta_type match
# 'spec'
assert spec is None, 'spec argument unsupported'
return self.iteritems()
grok.context(DocumentsFolder)
class IDocument(IDmsDocument):
doctype = schema.TextLine(title=_('Document Type'), required=False)
no = schema.TextLine(title=_('Number'), required=False)
session = schema.TextLine(title=_('Session'), required=False)
nodoc = schema.TextLine(title=_('Secondary Number'), required=False)
noannexe = schema.TextLine(title=_('Appendix Number'), required=False)
class RemoteFile(NamedBlobFile):
_v_filename = None
def __init__(self, url):
self.url = url
self._file_data = None
@property
def filename(self):
return os.path.basename(urlparse.urlparse(self.url).path)
@property
def contentType(self):
return 'application/pdf'
@property
def data(self):
if self._file_data:
return self._file_data
self._file_data = urllib2.urlopen(self.url).read()
return self._file_data
@property
def _blob(self):
# compatibility with collective.documentviewer
from ZODB.blob import Blob
blob = Blob()
f = blob.open('w')
f.write(self.data)
f.close()
return blob
def getSize(self):
return len(self.data)
class TabellioDmsFile(DmsFile):
@property
def file(self):
if self.base_id.startswith('pdf-') or self.base_id.startswith('legi-') or \
self.base_id.startswith('doc-'):
# get it from Tabellio
return None
return RemoteFile('http://archive.pfwb.be/' + self.base_id)
class Document(DmsDocument):
implements(IDocument)
_v_portal_type = 'pfwbged.tabellio.document'
@classmethod
def from_db(cls, id):
cursor = db_connection().cursor(cursor_factory=psycopg2.extras.NamedTupleCursor)
cursor.execute('''SELECT id, st, ts, type, no, sess, nodoc, anx, date, intit,
text1id, text2id, imageid, textprovts, textdefts
FROM t_document
WHERE id = %(id)s''', {'id': id})
row = cursor.fetchone()
cursor.close()
if row is None:
raise KeyError()
obj = cls()
obj.portal_type = cls._v_portal_type
obj.id = str(row.id)
obj.title = row.intit
obj.doctype = typenames.MAPPING.get(row.type, row.type)
obj.no = row.no
obj.session = row.sess
obj.nodoc = row.nodoc
obj.noannexe = row.anx
obj._v_versions = []
if row.text2id:
obj._v_versions.append(row.text2id)
if row.text1id and not row.text1id in obj._v_versions:
obj._v_versions.append(row.text1id)
if row.imageid:
obj._v_versions.append(row.imageid)
return obj
def __getitem__(self, id):
if id.startswith('version-'):
t = TabellioDmsFile()
t.id = str(id)
t.base_id = id.split('-', 1)[1]
t.title = t.base_id
uid = str(''.join(reversed(id)) + '-tabellio')
t.UID = lambda: uid
t.portal_type = 'dmsmainfile'
t = aq_chain(t.__of__(self))[0]
return t
return super(Document, self).__getitem__(id)
def getFolderContents(self, *args, **kwargs):
folder = self
parent_path = '/'.join(folder.getPhysicalPath()) + '/'
parent_url = folder.absolute_url() + '/'
class FakeBrain(object):
portal_type = 'dmsmainfile'
def __init__(self, object_id):
self.id = 'version-' + object_id
self.title = object_id
def getObject(self):
return folder[self.id]
def Title(self):
return self.title
def getPath(self):
return parent_path + self.id
def getURL(self, relative=True):
if relative:
return self.id
return parent_url + self.id
return [FakeBrain(x) for x in reversed(self._v_versions)]