mirror of https://github.com/NaN-tic/nanscan.git
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:
parent
8ac6afa164
commit
a5506050fa
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/>
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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()
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
@ -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() 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>
|
|
@ -0,0 +1,2 @@
|
|||
import scan_documents_queue
|
||||
import process_documents_queue
|
|
@ -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')
|
|
@ -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')
|
Loading…
Reference in New Issue