Code cleanup and workflow improvements.

- Removed '_id' postfix in relation fields.
- Added relation with templatebox in document boxes.
- Improved workflow with background alternative to scan process.
- Background scanning process sends a request to the user when it's finished.
This commit is contained in:
Albert Cervera i Areny 2008-10-31 00:33:15 +01:00
parent 67edd150b1
commit 100fc8b3bc
8 changed files with 144 additions and 48 deletions

View File

@ -607,7 +607,7 @@ class MainWindow(QMainWindow):
if self._template.id:
Rpc.session.call( '/object', 'execute', 'nan.template', 'write', [self._template.id], {'name': self._template.name } )
ids = Rpc.session.call( '/object', 'execute', 'nan.template.box', 'search', [('template_id','=',self._template.id)] )
ids = Rpc.session.call( '/object', 'execute', 'nan.template.box', 'search', [('template','=',self._template.id)] )
Rpc.session.call( '/object', 'execute', 'nan.template.box', 'unlink', ids )
else:
self._template.id = Rpc.session.call( '/object', 'execute', 'nan.template', 'create', {'name': self._template.name } )
@ -621,7 +621,7 @@ class MainWindow(QMainWindow):
'feature_y' : x.featureRect.y(),
'feature_width' : x.featureRect.width(),
'feature_height' : x.featureRect.height(),
'template_id': self._template.id,
'template': self._template.id,
'name': x.name,
'text': x.text,
'recognizer': x.recognizer,

View File

@ -34,7 +34,7 @@ class TemplateStorageManager:
def save(template):
if template.id:
Rpc.session.call( '/object', 'execute', 'nan.template', 'write', [template.id], {'name': template.name } )
ids = Rpc.session.call( '/object', 'execute', 'nan.template.box', 'search', [('template_id','=',template.id)] )
ids = Rpc.session.call( '/object', 'execute', 'nan.template.box', 'search', [('template','=',template.id)] )
Rpc.session.call( '/object', 'execute', 'nan.template.box', 'unlink', ids )
else:
template.id = Rpc.session.call( '/object', 'execute', 'nan.template', 'create', {'name': template.name } )
@ -48,7 +48,7 @@ class TemplateStorageManager:
'feature_y' : x.featureRect.y(),
'feature_width' : x.featureRect.width(),
'feature_height' : x.featureRect.height(),
'template_id': template.id,
'template': template.id,
'name': x.name,
'text': x.text,
'recognizer': x.recognizer,

View File

@ -1,5 +1,5 @@
#!/bin/bash
export PYTHONPATH=../../ktiny/bin
export PYTHONPATH=/home/albert/d/koo
./loader.py $1 $2

View File

@ -35,7 +35,8 @@
"category" : "Generic Modules/Attachments",
"init_xml" : [],
"demo_xml" : [],
"update_xml" : ["auto_attach_view.xml", "auto_attach_wizard.xml", "auto_attach_workflow.xml"],
"update_xml" : ["auto_attach_view.xml", "auto_attach_wizard.xml",
"auto_attach_workflow.xml", "auto_attach_data.xml"],
"active": False,
"installable": True
}

View File

@ -46,10 +46,10 @@ 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'),
'boxes' : fields.one2many('nan.template.box', 'template', 'Boxes'),
'attach_function' : fields.char('Attachment Function', 256),
'action_function' : fields.char('Action Function', 256),
'documents' : fields.one2many('nan.document', 'template_id', 'Documents')
'documents' : fields.one2many('nan.document', 'template', 'Documents')
}
# Returns a Template from the fields of a template. You'll usually use
@ -57,10 +57,11 @@ class nan_template(osv.osv):
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'])] )
ids = self.pool.get('nan.template.box').search( cr, uid, [('template','=',data['id'])] )
boxes = self.pool.get('nan.template.box').read(cr, uid, ids)
for y in boxes:
box = TemplateBox()
box.id = y['id']
box.rect = QRectF( y['x'], y['y'], y['width'], y['height'] )
box.name = y['name']
box.text = y['text']
@ -97,7 +98,7 @@ nan_template()
class nan_template_box(osv.osv):
_name = 'nan.template.box'
_columns = {
'template_id' : fields.many2one('nan.template', 'Template', required=True, ondelete='cascade'),
'template' : fields.many2one('nan.template', 'Template', required=True, ondelete='cascade'),
'x' : fields.float('X'),
'y' : fields.float('Y'),
'width' : fields.float('Width'),
@ -125,12 +126,13 @@ class nan_document(osv.osv):
_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' ),
'properties': fields.one2many('nan.document.property', 'document', 'Properties'),
'template': 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': fields.selection( [('pending','Pending'),('scanning','Scanning'),
('scanned','Scanned'), ('verified','Verified'),('processing','Processing'),
('processed','Processed')],
'State', required=True, readonly=True )
}
_defaults = {
@ -142,20 +144,31 @@ class nan_document(osv.osv):
# 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
if 'template' in values:
for x in self.read( cr, uid, ids, ['state', 'template'], context ):
# We only scan the document if template 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'] } )
if x['state'] == 'scanned' and x['template'] != values['template']:
toScan.append( {'id': x['id'], 'template': values['template'] } )
ret = super(nan_document, self).write(cr, uid, ids, values, context)
for x in toScan:
self.scanDocumentWithTemplate( cr, uid, x['id'], x['template_id'] )
self.scanDocumentWithTemplate( cr, uid, x['id'], x['template'] )
return ret
def scan_document(self, cr, uid, imageIds):
def scan_document_background(self, cr, uid, imageIds):
self.pool.get('ir.cron').create(cr, uid, {
'name': 'Scan document',
'user_id': uid,
'model': 'nan.document',
'function': 'scan_document',
'args': repr([ imageIds, True ])
})
cr.commit()
# If notify=True sends a request/notification to uid
def scan_document(self, cr, uid, imageIds, notify=False):
# Load templates into 'templates' list
templates = self.pool.get('nan.template').getAllTemplates( cr, uid )
@ -165,15 +178,14 @@ class nan_document(osv.osv):
# 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
if document.state not in ('pending','scanning'):
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 )
recognizer.recognize( QImage( image ) )
result = recognizer.findMatchingTemplateByOffset( templates )
template = result['template']
doc = result['document']
if not template:
@ -185,14 +197,29 @@ class nan_document(osv.osv):
template_id = template.id
else:
template_id = False
self.write(cr, uid, [document.id], {'template_id': template_id, 'state': 'scanned'})
self.write(cr, uid, [document.id], {'template': 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 } )
obj.create(cr, uid, {
'name': box.templateBox.name,
'value': box.text,
'document': document.id,
'template_box': box.templateBox.id
})
if notify:
self.pool.get('res.request').create( cr, uid, {
'act_from': uid,
'act_to': uid,
'name': 'Finished scanning document',
'body': 'The auto_attach system has finished scanning the document you requested. A reference to the document can be found in field Document Ref 1.',
'ref_doc1': 'nan.document,%d' % document.id,
})
self.executeAttachs( cr, uid, imageIds )
self.executeActions( cr, uid, imageIds, True )
cr.commit()
def scanDocumentWithTemplate(self, cr, uid, documentId, templateId):
@ -200,7 +227,7 @@ class nan_document(osv.osv):
# Whether templateId is valid or not
# Remove previous properties
obj = self.pool.get('nan.document.property')
ids = obj.search( cr, uid, [('document_id','=',documentId)] )
ids = obj.search( cr, uid, [('document','=',documentId)] )
obj.unlink( cr, uid, ids )
if templateId:
@ -224,7 +251,12 @@ class nan_document(osv.osv):
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'] } )
obj.create(cr, uid, {
'name': box.templateBox.name,
'value': box.text,
'document': document['id'],
'template_box': box.templateBox.id
})
self.executeAttachs( cr, uid, [documentId] )
self.executeActions( cr, uid, [documentId], True )
cr.commit()
@ -259,13 +291,14 @@ class nan_document(osv.osv):
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
print "Executing action on document with state ", document.state
if not explain and document.state not in ('verified','processing'):
continue
print "Yes"
task = None
if document.template_id:
function = document.template_id.action_function
if document.template:
function = document.template.action_function
if function:
properties = dict( [(x.name, unicode(x.value)) for x in document.properties] )
(name, parameters) = self._parseFunction(function, properties)
@ -279,23 +312,22 @@ class nan_document(osv.osv):
ref = document.document.split(',')
model = ref[0]
id = ref[1]
attach = {
self.pool.get( 'ir.attachment' ).create( cr, uid, {
'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 )
'description': 'Document attached automatically'
})
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 document.template:
function = document.template.attach_function
if function:
properties = dict( [(x.name, unicode( x.value, 'latin-1' )) for x in document.properties] )
@ -357,6 +389,8 @@ class nan_document_property(osv.osv):
_columns = {
'name' : fields.char('Text', 256),
'value' : fields.char('Value', 256),
'document_id' : fields.many2one('nan.document', 'Document', required=True, ondelete='cascade')
'document' : fields.many2one('nan.document', 'Document', required=True, ondelete='cascade'),
'template_box' : fields.many2one('nan.template.box', 'Template Box', required=True, ondelete='set null')
}
nan_document_property()

View File

@ -0,0 +1,10 @@
<?xml version="1.0" ?>
<terp>
<data>
<record model="res.request.link" id="wkf">
<field name="name">Document in Queue</field>
<field name="object">nan.document</field>
</record>
</data>
</terp>

View File

@ -10,7 +10,7 @@
<tree string="Documents in queue">
<field name="name"/>
<field name="state"/>
<field name="template_id"/>
<field name="template"/>
</tree>
</field>
</record>
@ -24,16 +24,20 @@
<page string="General">
<field name="name"/>
<field name="state"/>
<field name="template_id"/>
<field name="template"/>
<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"/>
<button name="scan_document" states="pending" string="Scan document"/>
<button name="pending_to_scanning" states="pending" string="Scan document in the background"/>
<button name="verify_document" states="scanned" string="Verify document"/>
<button name="scanned_to_pending" states="scanned" string="Back to pending"/>
<button name="process_document" states="verified" string="Process document"/>
<button name="verified_to_processing" states="verified" string="Process document in the background"/>
<button name="verified_to_scanned" states="verified" string="Unset verified"/>
</group>
</page>
<page string="Properties">
@ -41,10 +45,12 @@
<form string="Properties">
<field name="name"/>
<field name="value"/>
<field name="template_box"/>
</form>
<tree string="Properties">
<field name="name"/>
<field name="value"/>
<field name="template_box"/>
</tree>
</field>
</page>

View File

@ -12,6 +12,13 @@
<field name="wkf_id" ref="wkf"/>
<field name="flow_start">True</field>
<field name="name">pending</field>
<field name="action">write({'state':'pending'})</field>
</record>
<record model="workflow.activity" id="scanning">
<field name="wkf_id" ref="wkf"/>
<field name="name">scanning</field>
<field name="action">scan_document_background()&#10;write({'state':'scanning'})</field>
<field name="kind">function</field>
</record>
<record model="workflow.activity" id="scanned">
<field name="wkf_id" ref="wkf"/>
@ -25,11 +32,17 @@
<field name="action">write({'state':'verified'})</field>
<field name="kind">function</field>
</record>
<record model="workflow.activity" id="processing">
<field name="wkf_id" ref="wkf"/>
<field name="name">processing</field>
<field name="action">process_document_background()&#10;write({'state':'processing'})</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="action">process_document()&#10;write({'state':'processed'})</field>
<field name="kind">function</field>
</record>
@ -51,5 +64,37 @@
<field name="act_to" ref="processed"/>
<field name="signal">process_document</field>
</record>
<record model="workflow.transition" id="t4">
<field name="act_from" ref="pending"/>
<field name="act_to" ref="scanning"/>
<field name="signal">pending_to_scanning</field>
</record>
<record model="workflow.transition" id="t5">
<field name="act_from" ref="scanning"/>
<field name="act_to" ref="scanned"/>
<field name="signal">scanning_to_scanned</field>
</record>
<record model="workflow.transition" id="t6">
<field name="act_from" ref="verified"/>
<field name="act_to" ref="processing"/>
<field name="signal">verified_to_processing</field>
</record>
<record model="workflow.transition" id="t7">
<field name="act_from" ref="processing"/>
<field name="act_to" ref="processed"/>
<field name="signal">processing_to_processed</field>
</record>
<record model="workflow.transition" id="t8">
<field name="act_from" ref="scanned"/>
<field name="act_to" ref="pending"/>
<field name="signal">scanned_to_pending</field>
</record>
<record model="workflow.transition" id="t9">
<field name="act_from" ref="verified"/>
<field name="act_to" ref="scanned"/>
<field name="signal">verified_to_scanned</field>
</record>
</data>
</terp>