Added feature rect to template. Appropiate handling added to

Planta template designer, barcode, ocr and recognizer as well
as to the added auto_attach module. Storing and retrieval hasn't
been tested though.

Once tested this will provide the basis for better ways of finding
template offset.
This commit is contained in:
Albert Cervera i Areny 2008-09-02 00:01:04 +02:00
parent 8ac6afa164
commit a5506050fa
15 changed files with 905 additions and 35 deletions

View File

@ -67,6 +67,7 @@ class Barcode:
for x in self.boxes:
print "Text: %s, Type: %s, Position: %f, %f" % (x.text, x.type, x.position.x(), x.position.y())
## @brief Returns all barcode values concatenated for a given region of the image.
def textInRegion(self, region):
for x in self.boxes:
if region.contains(x.position):
@ -74,6 +75,15 @@ class Barcode:
# Always return unicode strings
return u''
## @brief Returns the bounding rectangle of the text returned by textInRegion for
# the given region.
def featureRectInRegion(self, region):
rect = QRectF()
for x in self.boxes:
if region.contains(x.position):
rect = rect.united( QRectF( x.position, x.position ) )
return rect
## @brief Scans the given image (QImage) looking for barcodes.
def scan(self, image):
# Clean boxes so scan() can be called more than once

View File

@ -91,7 +91,17 @@ class Ocr:
# It's the same as calling formatedText().
def textInRegion(self, region):
return self.formatedText( region )
## @brief Returns the bounding rectangle of the text returned by textInRegion for
# the given region.
def featureRectInRegion(self, region):
lines = self.textLinesWithSpaces( region )
rect = QRectF()
for line in lines:
for c in line:
rect = rect.united( c.box )
return rect
## @brief Uses ImageMagick's 'convert' application to convert the given image
# (QImage) into gray scale
def convertToGrayScale(self, image, output):
@ -251,7 +261,7 @@ class Ocr:
def formatedText(self, region=None):
lines = self.textLinesWithSpaces( region )
text = u''
for line in lines
for line in lines:
for c in line:
text += c.character
text += u'\n'

View File

@ -43,6 +43,7 @@ class Recognizer(QObject):
self.barcode = Barcode()
self.ocr = Ocr()
## @brief Returns the text of a given region of the image.
def textInRegion(self, region, type=None):
if type == 'barcode':
return self.barcode.textInRegion( region )
@ -51,6 +52,16 @@ class Recognizer(QObject):
else:
return None
## @brief Returns the bounding rectangle of the text returned by textInRegion for
# the given region.
def featureRectInRegion(self, region, type=None):
if type == 'barcode':
return self.barcode.featureRectInRegion( region )
elif type == 'text':
return self.ocr.featureRectInRegion( region )
else:
return None
def boxes(self, type):
if type == 'barcode':
return self.barcode.boxes
@ -171,20 +182,20 @@ class Recognizer(QObject):
print "Template %s has score %s with offset (%s,%s)" % (template.name, score, xOffset, yOffset)
return best
#def findTemplateOffset( self, template ):
#if not template.boxes:
#return QPoint( 0, 0 )
#
#lines = self.ocr.textLines()
#
#for templateBox in template.boxes:
#if templateBox.type != 'matcher':
#continue
#
#for line in lines:
#templateBox.text
#
# Trigram.trigram( lines[0],
## @brief Returns a QPoint with the offset that needs to be applied to the given
# template to best fit the current image.
def findTemplateOffset( self, template ):
if not template.boxes:
return QPoint( 0, 0 )
lines = self.ocr.textLinesWithSpaces()
for templateBox in template.boxes:
if templateBox.type != 'matcher':
continue
for line in lines:
templateBox.text

View File

@ -41,6 +41,9 @@ class TemplateBox:
def __init__(self):
self.rect = QRectF()
# Holds the rect where the actual text/barcode/whatever
# is found in the template
self.featureRect = QRectF()
self.recognizer = 'text'
self.type = 'matcher'
self.filter = 'none'

View File

@ -113,9 +113,11 @@ class ToolWidget(QWidget):
class TemplateBoxItem(QGraphicsRectItem):
def __init__(self, rect):
def __init__(self, rect, featureRect = None):
QGraphicsRectItem.__init__(self, rect)
self.templateBox = None
if featureRect:
self.feature = QGraphicsRectItem(featureRect, self)
class DocumentScene(QGraphicsScene):
@ -129,6 +131,7 @@ class DocumentScene(QGraphicsScene):
QGraphicsScene.__init__(self,parent)
self._imageBoxesVisible = True
self._templateBoxesVisible = True
self._featureBoxesVisible = True
self._binarizedVisible = False
self._mode = self.CreationMode
self._selection = None
@ -276,6 +279,12 @@ class DocumentScene(QGraphicsScene):
if item and unicode(item.data( 0 ).toString()) == 'TemplateBox':
item.setVisible( value )
def setFeatureBoxesVisible(self, value):
self._featureBoxesVisible = value
for item in self.items():
if item and unicode(item.data( 0 ).toString()) == 'TemplateBox':
item.feature.setVisible( value )
def setBinarizedVisible(self, value):
self._binarizedVisible = value
self._oneBitImage.setVisible( value )
@ -311,7 +320,7 @@ class DocumentScene(QGraphicsScene):
return item
def addTemplateBox(self, box):
item = TemplateBoxItem( self.mapRectFromRecognizer( box.rect ) )
item = TemplateBoxItem( self.mapRectFromRecognizer( box.rect ), self.mapRectFromRecognizer( box.featureRect ) )
item.setPen( self._selectionPen )
item.setBrush( self._selectionBrush )
item.setZValue( 5 )
@ -409,6 +418,7 @@ class MainWindow(QMainWindow):
self.connect( self.actionOpenTemplate, SIGNAL('triggered()'), self.openTemplate )
self.connect( self.actionToggleImageBoxes, SIGNAL('triggered()'), self.toggleImageBoxes )
self.connect( self.actionToggleTemplateBoxes, SIGNAL('triggered()'), self.toggleTemplateBoxes )
self.connect( self.actionToggleFeatureBoxes, SIGNAL('triggered()'), self.toggleFeatureBoxes )
self.connect( self.actionToggleBinarized, SIGNAL('triggered()'), self.toggleBinarized )
self.connect( self.actionLogin, SIGNAL('triggered()'), self.login )
self.connect( self.actionSaveTemplate, SIGNAL('triggered()'), self.saveTemplate )
@ -442,6 +452,7 @@ class MainWindow(QMainWindow):
box = TemplateBox()
box.rect = rect
box.text = self.scene.recognizer.textInRegion( rect, 'text' )
box.featureRect = self.scene.recognizer.featureRectInRegion( rect, 'text' )
add = AddTemplateBoxUndoCommand( self._template, box )
self.undoGroup.activeStack().push( add )
@ -475,6 +486,9 @@ class MainWindow(QMainWindow):
def toggleTemplateBoxes(self):
self.scene.setTemplateBoxesVisible( self.actionToggleTemplateBoxes.isChecked() )
def toggleFeatureBoxes(self):
self.scene.setFeatureBoxesVisible( self.actionToggleFeatureBoxes.isChecked() )
def toggleBinarized(self):
self.scene.setBinarizedVisible( self.actionToggleBinarized.isChecked() )
@ -530,9 +544,22 @@ class MainWindow(QMainWindow):
else:
self._template.id = rpc.session.call( '/object', 'execute', 'nan.template', 'create', {'name': self._template.name } )
for x in self._template.boxes:
values = { 'x': x.rect.x(), 'y': x.rect.y(),
'width': x.rect.width(), 'height': x.rect.height(), 'template_id': self._template.id,
'name': x.name, 'text': x.text, 'recognizer': x.recognizer, 'type': x.type, 'filter': x.filter }
values = {
'x': x.rect.x(),
'y': x.rect.y(),
'width': x.rect.width(),
'height': x.rect.height(),
'feature_x' : x.featureRect.x(),
'feature_y' : x.featureRect.y(),
'feature_width' : x.featureRect.width(),
'feature_height' : x.featureRect.height(),
'template_id': self._template.id,
'name': x.name,
'text': x.text,
'recognizer': x.recognizer,
'type': x.type,
'filter': x.filter
}
rpc.session.call( '/object', 'execute', 'nan.template.box', 'create', values )
self.updateTitle()
return True
@ -561,6 +588,8 @@ class MainWindow(QMainWindow):
for x in model.value('boxes'):
box = TemplateBox()
box.rect = QRectF( x.value('x'), x.value('y'), x.value('width'), x.value('height') )
box.featureRect = QRectF( x.value('feature_x'), x.value('feature_y'),
x.value('feature_width'), x.value('feature_height') )
box.name = x.value('name')
box.text = x.value('text')
box.recognizer = x.value('recognizer')
@ -572,7 +601,7 @@ class MainWindow(QMainWindow):
self.updateTitle()
def updateTitle(self):
self.setWindowTitle( "NaNnar - [%s]" % self._template.name )
self.setWindowTitle( "Planta - [%s]" % self._template.name )
def updateActions(self):
# Allow deleting if there's a TemplateBox selected

View File

@ -10,15 +10,15 @@
</rect>
</property>
<property name="windowTitle" >
<string>MainWindow</string>
<string>Planta</string>
</property>
<widget class="QWidget" name="centralwidget" >
<property name="geometry" >
<rect>
<x>0</x>
<y>63</y>
<y>48</y>
<width>524</width>
<height>401</height>
<height>417</height>
</rect>
</property>
<layout class="QHBoxLayout" >
@ -39,9 +39,9 @@
<property name="geometry" >
<rect>
<x>0</x>
<y>21</y>
<width>71</width>
<height>360</height>
<y>20</y>
<width>70</width>
<height>377</height>
</rect>
</property>
</widget>
@ -60,7 +60,7 @@
<x>0</x>
<y>0</y>
<width>524</width>
<height>30</height>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile" >
@ -99,9 +99,9 @@
<property name="geometry" >
<rect>
<x>0</x>
<y>464</y>
<y>465</y>
<width>524</width>
<height>23</height>
<height>22</height>
</rect>
</property>
</widget>
@ -109,9 +109,9 @@
<property name="geometry" >
<rect>
<x>0</x>
<y>30</y>
<y>22</y>
<width>524</width>
<height>33</height>
<height>26</height>
</rect>
</property>
<property name="windowTitle" >
@ -125,6 +125,7 @@
</attribute>
<addaction name="actionToggleImageBoxes" />
<addaction name="actionToggleTemplateBoxes" />
<addaction name="actionToggleFeatureBoxes" />
<addaction name="actionToggleBinarized" />
<addaction name="separator" />
<addaction name="actionUnzoom" />
@ -216,6 +217,17 @@
<string>Unzoom</string>
</property>
</action>
<action name="actionToggleFeatureBoxes" >
<property name="checkable" >
<bool>true</bool>
</property>
<property name="checked" >
<bool>true</bool>
</property>
<property name="text" >
<string>Show Feature Boxes</string>
</property>
</action>
</widget>
<resources/>
<connections/>

29
auto_attach/__init__.py Normal file
View File

@ -0,0 +1,29 @@
##############################################################################
#
# Copyright (c) 2007-2008 Albert Cervera i Areny <albert@nan-tic.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import auto_attach
import wizard

41
auto_attach/__terp__.py Normal file
View File

@ -0,0 +1,41 @@
##############################################################################
#
# Copyright (c) 2007-2008 Albert Cervera i Areny <albert@nan-tic.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
{
"name" : "Auto Attach",
"version" : "0.1",
"description" : "This module adds the hability to extract text from the attached files.",
"author" : "NaN",
"website" : "http://www.nan-tic.com",
"depends" : ["base"],
"category" : "Generic Modules/Attachments",
"init_xml" : [],
"demo_xml" : [],
"update_xml" : ["auto_attach_view.xml", "auto_attach_wizard.xml", "auto_attach_workflow.xml"],
"active": False,
"installable": True
}

362
auto_attach/auto_attach.py Normal file
View File

@ -0,0 +1,362 @@
##############################################################################
#
# Copyright (c) 2007-2008 Albert Cervera i Areny <albert@nan-tic.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from osv import osv, fields
import base64
import xml.dom.minidom
import tempfile
import os
import shutil
import codecs
import re
from NaNScaN.template import *
from NaNScaN.document import *
from NaNScaN.recognizer import *
from NaNScaN.ocr import *
from PyQt4.QtCore import *
class nan_template(osv.osv):
_name = 'nan.template'
_columns = {
'name' : fields.char('Name', 64, required=True),
'boxes' : fields.one2many('nan.template.box', 'template_id', 'Boxes'),
'attach_function' : fields.char('Attachment Function', 256),
'action_function' : fields.char('Action Function', 256),
'documents' : fields.one2many('nan.document', 'template_id', 'Documents')
}
# Returns a Template from the fields of a template. You'll usually use
# getTemplateFromId() or getAllTemplates()
def getTemplateFromData(self, cr, uid, data):
template = Template( data['name'] )
template.id = data['id']
ids = self.pool.get('nan.template.box').search( cr, uid, [('template_id','=',data['id'])] )
boxes = self.pool.get('nan.template.box').read(cr, uid, ids)
for y in boxes:
box = TemplateBox()
box.rect = QRectF( y['x'], y['y'], y['width'], y['height'] )
box.name = y['name']
box.text = y['text']
box.recognizer = y['recognizer']
box.type = y['type']
box.filter = y['filter']
# Important step: ensure box.text is unicode!
if isinstance( box.text, str ):
box.text = unicode( box.text, 'latin-1' )
template.addBox( box )
print "GETTING TEMPLATE: %s WITH %d BOXES" % ( data['name'], len(boxes) )
return template
# Returns a Template from the given id
def getTemplateFromId(self, cr, uid, id):
templates = self.pool.get('nan.template').read(cr, uid, [id])
if not templates:
return None
return self.getTemplateFromData( cr, uid, templates[0] )
# Returns all templates in a list of objects of class Template
def getAllTemplates(self, cr, uid):
# Load templates into 'templates' list
templates = []
ids = self.pool.get('nan.template').search(cr, uid, [])
templateValues = self.pool.get('nan.template').read(cr, uid, ids)
for x in templateValues:
templates.append( self.getTemplateFromData( cr, uid, x ) )
return templates
nan_template()
class nan_template_box(osv.osv):
_name = 'nan.template.box'
_columns = {
'template_id' : fields.many2one('nan.template', 'Template', required=True, ondelete='cascade'),
'x' : fields.float('X'),
'y' : fields.float('Y'),
'width' : fields.float('Width'),
'height' : fields.float('Height'),
'feature_x' : fields.float('Feature X'),
'feature_y' : fields.float('Feature Y'),
'feature_width' : fields.float('Feature Width'),
'feature_height' : fields.float('Feature Height'),
'name' : fields.char('Name', 256),
'text' : fields.char('Text', 256),
'recognizer': fields.selection( [('text','Text'),('barcode','Barcode')], 'Recognizer' ),
'type' : fields.selection( [('matcher','Matcher'),('input','Input')], 'Type' ),
'filter' : fields.selection( [('numeric','Numeric'), ('alphabetic','Alphabetic'), ('alphanumeric','Alphanumeric'), ('exists', 'Exists'), ('none', 'None')], 'Filter' )
}
nan_template_box()
def attachableDocuments(self, cr, uid, context={}):
obj = self.pool.get('ir.model')
ids = obj.search(cr, uid, [])
res = obj.read(cr, uid, ids, ['model', 'name'], context)
return [(r['model'], r['name']) for r in res]
class nan_document(osv.osv):
_name = 'nan.document'
_columns = {
'name' : fields.char('Name', 64),
'datas': fields.binary('Data'),
'properties': fields.one2many('nan.document.property', 'document_id', 'Properties'),
'template_id': fields.many2one('nan.template', 'Template' ),
'document': fields.reference('Document', selection=attachableDocuments, size=128),
'task' : fields.text('Task', readonly=True),
'state': fields.selection( [('pending','Pending'),('scanned','Scanned'),
('verified','Verified'),('processed','Processed')],
'State', required=True, readonly=True )
}
_defaults = {
'state': lambda *a: 'pending'
}
def write(self, cr, uid, ids, values, context=None):
# Scan after writting as it will modify the objects and thus a "modified in
# the meanwhile" would be thrown, so by now check which of the records
# we'll want to scan later
toScan = []
if 'template_id' in values:
for x in self.read( cr, uid, ids, ['state', 'template_id'], context ):
# We only scan the document if template_id has changed and the document
# is in 'scanned' state.
if x['state'] == 'scanned' and x['template_id'] != values['template_id']:
toScan.append( {'id': x['id'], 'template_id': values['template_id'] } )
ret = super(nan_document, self).write(cr, uid, ids, values, context)
for x in toScan:
self.scanDocumentWithTemplate( cr, uid, x['id'], x['template_id'] )
return ret
def scan_document(self, cr, uid, imageIds):
# Load templates into 'templates' list
templates = self.pool.get('nan.template').getAllTemplates( cr, uid )
# Initialize Ocr System (Gamera)
initOcrSystem()
recognizer = Recognizer()
# Iterate over all images and try to find the most similar template
for document in self.browse(cr, uid, imageIds):
#TODO: Enable workflow test
#if document.state != 'pending':
# continue
fp, image = tempfile.mkstemp()
fp = os.fdopen( fp, 'wb+' )
fp.write( base64.decodestring(document.datas) )
fp.close()
recognizer.recognize( image )
result = recognizer.findMatchingTemplate( templates )
template = result['template']
doc = result['document']
if not template:
print "No template found for document %s." % document.name
else:
print "The best template found for document %s is %s." % (document.name, template.name)
if template:
template_id = template.id
else:
template_id = False
self.write(cr, uid, [document.id], {'template_id': template_id, 'state': 'scanned'})
if doc:
obj = self.pool.get('nan.document.property')
for box in doc.boxes:
obj.create(cr, uid, { 'name' : box.templateBox.name, 'value' : box.text, 'document_id': document.id } )
self.executeAttachs( cr, uid, imageIds )
self.executeActions( cr, uid, imageIds, True )
cr.commit()
def scanDocumentWithTemplate(self, cr, uid, documentId, templateId):
# Whether templateId is valid or not
# Remove previous properties
obj = self.pool.get('nan.document.property')
ids = obj.search( cr, uid, [('document_id','=',documentId)] )
obj.unlink( cr, uid, ids )
if templateId:
# Initialize Ocr System (Gamera)
initOcrSystem()
template = self.pool.get('nan.template').getTemplateFromId( cr, uid, templateId )
documents = self.read(cr, uid, [documentId])
if not documents:
return
document = documents[0]
fp, image = tempfile.mkstemp()
fp = os.fdopen( fp, 'wb+' )
fp.write( base64.decodestring( document['datas'] ) )
fp.close()
recognizer = Recognizer()
recognizer.recognize()
doc = recognizer.extractWithTemplate( image, template )
for box in doc.boxes:
obj.create(cr, uid, { 'name' : box.templateBox.name, 'value' : box.text, 'document_id': document['id'] } )
self.executeAttachs( cr, uid, [documentId] )
self.executeActions( cr, uid, [documentId], True )
cr.commit()
def process_document(self, cr, uid, ids):
self.executeActions( cr, uid, ids, False )
cr.commit()
def _parseFunction(self, function, properties):
expression = re.match('(.*)\((.*)\)', function)
name = expression.group(1)
parameters = expression.group(2)
if name not in dir(self):
print "Function '%s' not found" % (name)
return False
parameters = parameters.split(',')
newParameters = []
for p in parameters:
value = p.strip()
if value.startswith( '#' ):
if value[1:] not in properties:
print "Property '%s' not found" % value
newParameters.append( "''" )
continue
value = properties[ value[1:] ]
value = "'" + value.replace("'","\\'") + "'"
if type(value) != unicode:
value = unicode( value, errors='ignore' )
newParameters.append( value )
return (name, newParameters)
def executeActions( self, cr, uid, ids, explain ):
for document in self.browse( cr, uid, ids ):
#TODO: Enable workflow test
#if not explain and document.state != 'verified':
#continue
task = None
if document.template_id:
function = document.template_id.action_function
if function:
properties = dict( [(x.name, unicode(x.value)) for x in document.properties] )
(name, parameters) = self._parseFunction(function, properties)
obj = self.pool.get('nan.document')
task = eval('obj.%s(cr, uid, explain, %s)' % ( name, ','.join( parameters ) ) )
if explain:
self.write( cr, uid, [document.id], {'task': task} )
elif document.document:
# Attach document to the appropiate reference
ref = document.document.split(',')
model = ref[0]
id = ref[1]
attach = {
'res_id': id,
'res_model': model,
'name': document.name,
'datas': document.datas,
'datas_fname': document.name,
'description': 'Attached automatically'
}
self.pool.get( 'ir.attachment' ).create( cr, uid, attach )
self.write(cr, uid, [document.id], {'state': 'processed'})
def executeAttachs( self, cr, uid, ids ):
for document in self.browse( cr, uid, ids ):
reference = None
if document.template_id:
function = document.template_id.attach_function
if function:
properties = dict( [(x.name, unicode( x.value, 'latin-1' )) for x in document.properties] )
(name, parameters) = self._parseFunction(function, properties)
obj = self.pool.get('nan.document')
#print 'CALLING: obj.%s(cr, uid, %s)' % ( name, u','.join( parameters ) ),
reference = eval('obj.%s(cr, uid, %s)' % ( name, u','.join( parameters ) ) )
if reference:
self.write( cr, uid, [document.id], {'document': '%s,%s' % (reference[0],reference[1]) } )
else:
self.write( cr, uid, [document.id], {'document': False} )
#expression = re.match('(.*)\((.*)\)', function)
#name = expression.group(1)
#parameters = expression.group(2)
#if name not in dir(self):
# print "Function '%s' not found" % (name)
# continue
#parameters = parameters.split(',')
#properties = dict( [(x.name, x.value) for x in document.properties] )
#newParameters = []
#for p in parameters:
# value = p.strip()
# if value.startswith( '#' ):
# print "We'll search '%s' in the properties" % value[1:]
# if value[1:] not in properties:
# continue
# value = properties[ value[1:] ]
# value = "'" + value.replace("'","\\\\'") + "'"
# newParameters.append( value )
#obj = self.pool.get('nan.document')
#reference = eval('obj.%s(cr, uid, %s)' % ( name, ','.join( newParameters ) ) )
def actionAddPartner( self, cr, uid, explain, name ):
if explain:
return "A new partner with name '%s' will be created (if it doesn't exist already)." % name
else:
if not self.pool.get( 'res.partner' ).search( cr, uid, [('name','=',name)]):
self.pool.get( 'res.partner' ).create( cr, uid, {'name': name} )
return True
def attachModelByField( self, cr, uid, model, field, name ):
table = self.pool.get( model )._table
# TODO: Security issues
cr.execute( 'SELECT id FROM "' + table + '" ORDER BY similarity("' + field + '",\'%s\') DESC LIMIT 1' % name )
record = cr.fetchone()
if not record:
return False
return ( model, record[0] )
nan_document()
class nan_document_property(osv.osv):
_name = 'nan.document.property'
_columns = {
'name' : fields.char('Text', 256),
'value' : fields.char('Value', 256),
'document_id' : fields.many2one('nan.document', 'Document', required=True, ondelete='cascade')
}
nan_document_property()

View File

@ -0,0 +1,168 @@
<?xml version="1.0"?>
<terp>
<data>
<!-- Documents views -->
<record model="ir.ui.view" id="view_document_tree">
<field name="name">nan.document.tree</field>
<field name="model">nan.document</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Documents in queue">
<field name="name"/>
<field name="state"/>
<field name="template_id"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="view_document_form">
<field name="name">nan.document.form</field>
<field name="model">nan.document</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Documents in queue">
<notebook>
<page string="General">
<field name="name"/>
<field name="state"/>
<field name="template_id"/>
<field name="document"/>
<separator string="Task"/>
<field name="task" nolabel="1" colspan="4"/>
<separator string="Image"/>
<field name="datas" widget="image" img_width="300" img_height="300" nolabel="1" colspan="4"/>
<group colspan="4">
<button name="scan_document" states="pending" string="Scan Document"/>
<button name="verify_document" states="scanned" string="Verify Document"/>
<button name="process_document" states="verified" string="Process Document"/>
</group>
</page>
<page string="Properties">
<field name="properties" nolabel="1">
<form string="Properties">
<field name="name"/>
<field name="value"/>
</form>
<tree string="Properties">
<field name="name"/>
<field name="value"/>
</tree>
</field>
</page>
</notebook>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="all_document_tree">
<field name="name">Documents</field>
<field name="res_model">nan.document</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain"></field>
</record>
<menuitem name="Administration/Auto Attach/Document Queue" action="all_document_tree" id="menu_document_tree"/>
<record model="ir.actions.act_window" id="pending_document_tree">
<field name="name">Pending Documents</field>
<field name="res_model">nan.document</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('state','=','pending')]</field>
</record>
<menuitem name="Administration/Auto Attach/Document Queue/Pending" action="pending_document_tree" id="menu_pending_document_tree"/>
<record model="ir.actions.act_window" id="scanned_document_tree">
<field name="name">Scanned Documents</field>
<field name="res_model">nan.document</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('state','=','scanned')]</field>
</record>
<menuitem name="Administration/Auto Attach/Document Queue/Scanned" action="scanned_document_tree" id="menu_scanned_document_tree"/>
<record model="ir.actions.act_window" id="verified_document_tree">
<field name="name">Verified Documents</field>
<field name="res_model">nan.document</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('state','=','verified')]</field>
</record>
<menuitem name="Administration/Auto Attach/Document Queue/Verified" action="verified_document_tree" id="menu_verified_document_tree"/>
<record model="ir.actions.act_window" id="processed_document_tree">
<field name="name">Processed Documents</field>
<field name="res_model">nan.document</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('state','=','processed')]</field>
</record>
<menuitem name="Administration/Auto Attach/Document Queue/Processed" action="processed_document_tree" id="menu_processed_document_tree"/>
<!-- Template views -->
<record model="ir.ui.view" id="view_template_tree">
<field name="name">nan.template.tree</field>
<field name="model">nan.template</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Document Templates">
<field name="name"/>
<field name="boxes"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="view_template_form">
<field name="name">nan.template.form</field>
<field name="model">nan.template</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Document Templates">
<field name="name" select="1"/>
<field name="attach_function"/>
<field name="action_function"/>
<newline/>
<field name="boxes" mode="tree,form" colspan="4" nolabel="1">
<form string="Boxes">
<field name="name"/>
<field name="text"/>
<field name="recognizer"/>
<field name="type"/>
<field name="filter"/>
<separator string="Dimensions" colspan="4"/>
<field name="x"/>
<field name="width"/>
<field name="y"/>
<field name="height"/>
<separator string="Feature Dimensions" colspan="4"/>
<field name="feature_x"/>
<field name="feature_width"/>
<field name="feature_y"/>
<field name="feature_height"/>
</form>
<tree string="Boxes">
<field name="name"/>
<field name="text"/>
<field name="recognizer"/>
<field name="type"/>
<field name="filter"/>
<field name="x"/>
<field name="y"/>
<field name="width"/>
<field name="height"/>
</tree>
</field>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="view_template_action">
<field name="name">Templates</field>
<field name="res_model">nan.template</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem name="Administration/Auto Attach/Templates" action="view_template_action" id="menu_view_template"/>
</data>
</terp>

View File

@ -0,0 +1,24 @@
<terp>
<data>
<!-- Scan -->
<record model="ir.actions.wizard" id="nan_document_scan">
<field name="name">Scan documents queue</field>
<field name="wiz_name">nan_document_scan</field>
</record>
<menuitem name="Administration/Auto Attach/Scan Documents Queue"
action="nan_document_scan" id="nan_document_scan_menu" type="wizard"/>
<wizard string="Scan Documents" model="nan.document" name="nan_document_scan" id="wizard_scan_documents"/>
<!-- Process -->
<record model="ir.actions.wizard" id="nan_document_process">
<field name="name">Process documents queue</field>
<field name="wiz_name">nan_document_process</field>
</record>
<menuitem name="Administration/Auto Attach/Process Documents Queue"
action="nan_document_process" id="nan_document_process_menu" type="wizard"/>
<wizard string="Process Documents" model="nan.document" name="nan_document_process" id="wizard_process_documents"/>
</data>
</terp>

View File

@ -0,0 +1,55 @@
<?xml version="1.0" ?>
<terp>
<data>
<record model="workflow" id="wkf">
<field name="name">nan.document.basic</field>
<field name="osv">nan.document</field>
<field name="on_create">True</field>
</record>
<!-- states -->
<record model="workflow.activity" id="pending">
<field name="wkf_id" ref="wkf"/>
<field name="flow_start">True</field>
<field name="name">pending</field>
</record>
<record model="workflow.activity" id="scanned">
<field name="wkf_id" ref="wkf"/>
<field name="name">scanned</field>
<field name="action">scan_document()&#10;write({'state':'scanned'})</field>
<field name="kind">function</field>
</record>
<record model="workflow.activity" id="verified">
<field name="wkf_id" ref="wkf"/>
<field name="name">verified</field>
<field name="action">write({'state':'verified'})</field>
<field name="kind">function</field>
</record>
<record model="workflow.activity" id="processed">
<field name="wkf_id" ref="wkf"/>
<field name="flow_stop">True</field>
<field name="name">processed</field>
<field name="action">write({'state':'processed'})</field>
<field name="kind">function</field>
</record>
<!-- transitions -->
<record model="workflow.transition" id="t1">
<field name="act_from" ref="pending"/>
<field name="act_to" ref="scanned"/>
<field name="signal">scan_document</field>
</record>
<record model="workflow.transition" id="t2">
<field name="act_from" ref="scanned"/>
<field name="act_to" ref="verified"/>
<field name="signal">verify_document</field>
</record>
<record model="workflow.transition" id="t3">
<field name="act_from" ref="verified"/>
<field name="act_to" ref="processed"/>
<field name="signal">process_document</field>
</record>
</data>
</terp>

View File

@ -0,0 +1,2 @@
import scan_documents_queue
import process_documents_queue

View File

@ -0,0 +1,57 @@
from PyQt4.QtCore import *
import wizard
import pooler
view_form_end = """<?xml version="1.0"?>
<form string="Document queue processed">
<label align="0.0" string="The document queue has been processed. Now you can verify the documents!" colspan="4"/>
</form>"""
view_form_start = """<?xml version="1.0"?>
<form string="Document queue update">
<image name="gtk-info" size="64" colspan="2"/>
<group colspan="2" col="4">
<label align="0.0" string="All verified documents in the queue will be processed." colspan="4"/>
<label align="0.0" string="Note that this operation may take a lot of time, depending on the amount of documents." colspan="4"/>
<label align="0.0" string="The following documents will be processed:" colspan="4"/>
<field name="documents" nolabel="1" colspan="4"/>
</group>
</form>"""
view_fields_start = {
"documents": {'type':'text', 'string':'Documents', 'readonly':True}
}
class ProcessDocumentQueueWizard(wizard.interface):
def _before_process(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
obj = pool.get('nan.document')
if 'ids' in data:
ids = data['ids']
else:
ids = obj.search(cr, uid, [('state','=','verified')])
values = obj.read(cr, uid, ids, ['name'])
ret = { 'documents': '\n'.join([x['name'] for x in values]) }
return ret
def _process(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
obj = pool.get('nan.document')
if 'ids' in data:
ids = data['ids']
else:
ids = obj.search(cr, uid, [('state','=','verified')])
obj.process_document(cr, uid, ids)
return {}
states = {
'init': {
'actions': [_before_process],
'result': {'type':'form', 'arch':view_form_start, 'fields': view_fields_start, 'state':[('end','Cancel','gtk-cancel'),('start','Start Process','gtk-ok')]}
},
'start': {
'actions': [_process],
'result': {'type':'form', 'arch':view_form_end, 'fields': {}, 'state':[('end','Close','gtk-close')]}
}
}
ProcessDocumentQueueWizard('nan_document_process')

View File

@ -0,0 +1,57 @@
from PyQt4.QtCore import *
import wizard
import pooler
view_form_end = """<?xml version="1.0"?>
<form string="Document queue scanned">
<label align="0.0" string="The document queue has been scanned. Now you can verify the documents!" colspan="4"/>
</form>"""
view_form_start = """<?xml version="1.0"?>
<form string="Document queue update">
<image name="gtk-info" size="64" colspan="2"/>
<group colspan="2" col="4">
<label align="0.0" string="All pending documents in the queue will be scanned." colspan="4"/>
<label align="0.0" string="Note that this operation may take a lot of time, depending on the amount of documents." colspan="4"/>
<label align="0.0" string="The following documents will be scanned:" colspan="4"/>
<field name="documents" nolabel="1" colspan="4"/>
</group>
</form>"""
view_fields_start = {
"documents": {'type':'text', 'string':'Documents', 'readonly':True}
}
class ScanDocumentQueueWizard(wizard.interface):
def _before_scan(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
obj = pool.get('nan.document')
if 'ids' in data:
ids = data['ids']
else:
ids = obj.search(cr, uid, [('state','=','pending')])
values = obj.read(cr, uid, ids, ['name'])
ret = { 'documents': '\n'.join([x['name'] for x in values]) }
return ret
def _scan(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
obj = pool.get('nan.document')
if 'ids' in data:
ids = data['ids']
else:
ids = obj.search(cr, uid, [('state','=','pending')])
obj.scan_document(cr, uid, ids)
return {}
states = {
'init': {
'actions': [_before_scan],
'result': {'type':'form', 'arch':view_form_start, 'fields': view_fields_start, 'state':[('end','Cancel','gtk-cancel'),('start','Start Scan','gtk-ok')]}
},
'start': {
'actions': [_scan],
'result': {'type':'form', 'arch':view_form_end, 'fields': {}, 'state':[('end','Close','gtk-close')]}
}
}
ScanDocumentQueueWizard('nan_document_scan')