Add source and adf-mode fields to the sane scanner source. Make all fields optional.

Improve error handling.
This commit is contained in:
Roberto Rosario
2017-02-06 04:44:04 -04:00
parent 25f1f7d067
commit 091bac591d
8 changed files with 262 additions and 105 deletions

View File

@@ -0,0 +1,8 @@
from __future__ import unicode_literals
class SourceException(Exception):
"""
Base sources warning
"""
pass

View File

@@ -85,7 +85,10 @@ class SaneScannerUploadForm(UploadBaseForm):
class SaneScannerSetupForm(forms.ModelForm): class SaneScannerSetupForm(forms.ModelForm):
class Meta: class Meta:
fields = ('label', 'device_name', 'mode', 'resolution', 'enabled') fields = (
'label', 'device_name', 'mode', 'resolution', 'source',
'adf_mode', 'enabled'
)
model = SaneScanner model = SaneScanner

View File

@@ -2,6 +2,22 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
SCANNER_SOURCE_FLATBED = 'flatbed'
SCANNER_SOURCE_ADF = 'Automatic Document Feeder'
SCANNER_SOURCE_CHOICES = (
(SCANNER_SOURCE_FLATBED, _('Flatbed')),
(SCANNER_SOURCE_ADF, _('Document feeder')),
)
SCANNER_ADF_MODE_SIMPLEX = 'simplex'
SCANNER_ADF_MODE_DUPLEX = 'duplex'
SCANNER_ADF_MODE_CHOICES = (
(SCANNER_ADF_MODE_SIMPLEX, _('Simplex')),
(SCANNER_ADF_MODE_DUPLEX, _('Duplex')),
)
SCANNER_MODE_LINEART = 'lineart' SCANNER_MODE_LINEART = 'lineart'
SCANNER_MODE_MONOCHROME = 'monochrome' SCANNER_MODE_MONOCHROME = 'monochrome'
SCANNER_MODE_COLOR = 'color' SCANNER_MODE_COLOR = 'color'

View File

@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-02-06 07:10
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sources', '0012_auto_20170205_0743'),
]
operations = [
migrations.AddField(
model_name='sanescanner',
name='adf_mode',
field=models.CharField(blank=True, choices=[('simplex', 'Simples'), ('duplex', 'Duplex')], default='simplex', help_text='Selects the document feeder mode (simplex/duplex). If this option is not supported by your scanner, leave it blank.', max_length=16, verbose_name='ADF mode'),
),
migrations.AddField(
model_name='sanescanner',
name='source',
field=models.CharField(blank=True, choices=[('flatbed', 'Flatbed'), ('document-feeder', 'Document feeder')], default='flatbed', help_text='Selects the scan source (such as a document-feeder). If this option is not supported by your scanner, leave it blank.', max_length=16, verbose_name='Paper source'),
),
migrations.AlterField(
model_name='sanescanner',
name='mode',
field=models.CharField(blank=True, choices=[('lineart', 'Lineart'), ('monochrome', 'Monochrome'), ('color', 'Color')], default='color', help_text='Selects the scan mode (e.g., lineart, monochrome, or color). If this option is not supported by your scanner, leave it blank.', max_length=16, verbose_name='Mode'),
),
migrations.AlterField(
model_name='sanescanner',
name='resolution',
field=models.PositiveIntegerField(blank=True, help_text='Sets the resolution of the scanned image in DPI (dots per inch). Typical value is 200. If this option is not supported by your scanner, leave it blank.', verbose_name='Resolution'),
),
]

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-02-06 07:22
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sources', '0013_auto_20170206_0710'),
]
operations = [
migrations.AlterField(
model_name='sanescanner',
name='adf_mode',
field=models.CharField(blank=True, choices=[('simplex', 'Simplex'), ('duplex', 'Duplex')], default='simplex', help_text='Selects the document feeder mode (simplex/duplex). If this option is not supported by your scanner, leave it blank.', max_length=16, verbose_name='ADF mode'),
),
migrations.AlterField(
model_name='sanescanner',
name='source',
field=models.CharField(blank=True, choices=[('flatbed', 'Flatbed'), ('"Automatic Document Feeder"', 'Document feeder')], default='flatbed', help_text='Selects the scan source (such as a document-feeder). If this option is not supported by your scanner, leave it blank.', max_length=32, verbose_name='Paper source'),
),
]

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-02-06 08:35
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sources', '0014_auto_20170206_0722'),
]
operations = [
migrations.AlterField(
model_name='sanescanner',
name='source',
field=models.CharField(blank=True, choices=[('flatbed', 'Flatbed'), ('Automatic Document Feeder', 'Document feeder')], default='flatbed', help_text='Selects the scan source (such as a document-feeder). If this option is not supported by your scanner, leave it blank.', max_length=32, verbose_name='Paper source'),
),
]

View File

@@ -40,14 +40,17 @@ from metadata.models import MetadataType
from tags.models import Tag from tags.models import Tag
from .classes import Attachment, PseudoFile, SourceUploadedFile, StagingFile from .classes import Attachment, PseudoFile, SourceUploadedFile, StagingFile
from .exceptions import SourceException
from .literals import ( from .literals import (
DEFAULT_INTERVAL, DEFAULT_POP3_TIMEOUT, DEFAULT_IMAP_MAILBOX, DEFAULT_INTERVAL, DEFAULT_POP3_TIMEOUT, DEFAULT_IMAP_MAILBOX,
DEFAULT_METADATA_ATTACHMENT_NAME, SCANNER_MODE_COLOR, SCANNER_MODE_CHOICES, DEFAULT_METADATA_ATTACHMENT_NAME, SCANNER_ADF_MODE_CHOICES,
SCANNER_ADF_MODE_SIMPLEX, SCANNER_MODE_COLOR, SCANNER_MODE_CHOICES,
SCANNER_SOURCE_CHOICES, SCANNER_SOURCE_FLATBED,
SOURCE_CHOICES, SOURCE_CHOICE_STAGING, SOURCE_CHOICE_WATCH, SOURCE_CHOICES, SOURCE_CHOICE_STAGING, SOURCE_CHOICE_WATCH,
SOURCE_CHOICE_WEB_FORM, SOURCE_INTERACTIVE_UNCOMPRESS_CHOICES, SOURCE_CHOICE_WEB_FORM, SOURCE_INTERACTIVE_UNCOMPRESS_CHOICES,
SOURCE_UNCOMPRESS_CHOICES, SOURCE_UNCOMPRESS_CHOICE_N, SOURCE_UNCOMPRESS_CHOICES, SOURCE_UNCOMPRESS_CHOICE_N,
SOURCE_UNCOMPRESS_CHOICE_Y, SOURCE_CHOICE_EMAIL_IMAP, SOURCE_UNCOMPRESS_CHOICE_Y, SOURCE_CHOICE_EMAIL_IMAP,
SOURCE_CHOICE_EMAIL_POP3, SOURCE_CHOICE_SANE_SCANNER SOURCE_CHOICE_EMAIL_POP3, SOURCE_CHOICE_SANE_SCANNER,
) )
from .settings import setting_scanimage_path from .settings import setting_scanimage_path
@@ -175,14 +178,34 @@ class SaneScanner(InteractiveSource):
verbose_name=_('Device name') verbose_name=_('Device name')
) )
mode = models.CharField( mode = models.CharField(
choices=SCANNER_MODE_CHOICES, default=SCANNER_MODE_COLOR, blank=True, choices=SCANNER_MODE_CHOICES, default=SCANNER_MODE_COLOR,
max_length=16, verbose_name=_('Mode') help_text=_(
'Selects the scan mode (e.g., lineart, monochrome, or color). '
'If this option is not supported by your scanner, leave it blank.'
), max_length=16, verbose_name=_('Mode')
) )
resolution = models.PositiveIntegerField( resolution = models.PositiveIntegerField(
default=300, help_text=_( blank=True, help_text=_(
'Sets the resolution of the scanned image in DPI (dots per inch). ' 'Sets the resolution of the scanned image in DPI (dots per inch). '
'Typical value is 200. If this option is not supported by your '
'scanner, leave it blank.'
), verbose_name=_('Resolution') ), verbose_name=_('Resolution')
) )
source = models.CharField(
blank=True, choices=SCANNER_SOURCE_CHOICES,
default=SCANNER_SOURCE_FLATBED, help_text=_(
'Selects the scan source (such as a document-feeder). If this '
'option is not supported by your scanner, leave it blank.'
), max_length=32,
verbose_name=_('Paper source')
)
adf_mode = models.CharField(
blank=True, choices=SCANNER_ADF_MODE_CHOICES,
default=SCANNER_ADF_MODE_SIMPLEX, help_text=_(
'Selects the document feeder mode (simplex/duplex). If this '
'option is not supported by your scanner, leave it blank.'
), max_length=16, verbose_name=_('ADF mode')
)
class Meta: class Meta:
verbose_name = _('SANE Scanner') verbose_name = _('SANE Scanner')
@@ -193,26 +216,50 @@ class SaneScanner(InteractiveSource):
def get_upload_file_object(self, form_data): def get_upload_file_object(self, form_data):
temporary_file_object = TemporaryFile() temporary_file_object = TemporaryFile()
try:
command_line = [ command_line = [
setting_scanimage_path.value, '-d', self.device_name, setting_scanimage_path.value, '-d', self.device_name,
'--resolution', '{}'.format(self.resolution), '--mode', '--format', 'tiff',
self.mode, '--format', 'tiff'
] ]
logger.debug('Scan command line: %s', command_line)
result = subprocess.check_call( if self.resolution:
command_line, stdout=temporary_file_object command_line.extend(
) ['--resolution', '{}'.format(self.resolution)]
except subprocess.CalledProcessError as exception:
logger.error(
'Exception while scanning from source:%s ; %s', self,
exception
)
self.logs.create(
message=_('Error while scanning; %s') % exception
) )
if self.mode:
command_line.extend(
['--mode', self.mode]
)
if self.source:
command_line.extend(
['--source', self.source]
)
if self.adf_mode:
command_line.extend(
['--adf-mode', self.adf_mode]
)
filestderr = TemporaryFile()
try:
logger.debug('Scan command line: %s', command_line)
subprocess.check_call(
command_line, stdout=temporary_file_object, stderr=filestderr
)
except subprocess.CalledProcessError:
filestderr.seek(0)
error_message = filestderr.read()
logger.error(
'Exception while scanning from source:%s ; %s', self,
error_message
)
message = _('Error while scanning; %s') % error_message
self.logs.create(message=message)
raise SourceException(message)
else:
return SourceUploadedFile( return SourceUploadedFile(
source=self, file=PseudoFile( source=self, file=PseudoFile(
file=temporary_file_object, name='scan {}'.format(now()) file=temporary_file_object, name='scan {}'.format(now())

View File

@@ -24,18 +24,13 @@ from documents.tasks import task_upload_new_version
from metadata.api import decode_metadata_from_url from metadata.api import decode_metadata_from_url
from navigation import Link from navigation import Link
from .exceptions import SourceException
from .forms import ( from .forms import (
NewDocumentForm, NewVersionForm, SaneScannerUploadForm, WebFormUploadForm, NewDocumentForm, NewVersionForm, WebFormUploadForm, WebFormUploadFormHTML5
WebFormUploadFormHTML5
)
from .literals import (
SOURCE_CHOICE_STAGING, SOURCE_CHOICE_SANE_SCANNER, SOURCE_CHOICE_WEB_FORM,
SOURCE_UNCOMPRESS_CHOICE_ASK,
SOURCE_UNCOMPRESS_CHOICE_Y
) )
from .literals import SOURCE_UNCOMPRESS_CHOICE_ASK, SOURCE_UNCOMPRESS_CHOICE_Y
from .models import ( from .models import (
InteractiveSource, Source, SaneScanner, StagingFolderSource, InteractiveSource, Source, SaneScanner, StagingFolderSource
WebFormSource
) )
from .permissions import ( from .permissions import (
permission_sources_setup_create, permission_sources_setup_delete, permission_sources_setup_create, permission_sources_setup_delete,
@@ -211,10 +206,13 @@ class UploadInteractiveView(UploadBaseView):
else: else:
expand = False expand = False
try:
uploaded_file = self.source.get_upload_file_object( uploaded_file = self.source.get_upload_file_object(
forms['source_form'].cleaned_data forms['source_form'].cleaned_data
) )
except SourceException as exception:
messages.error(self.request, exception)
else:
shared_uploaded_file = SharedUploadedFile.objects.create( shared_uploaded_file = SharedUploadedFile.objects.create(
file=uploaded_file.file file=uploaded_file.file
) )
@@ -254,6 +252,7 @@ class UploadInteractiveView(UploadBaseView):
'shortly.' 'shortly.'
) )
) )
return HttpResponseRedirect(self.request.get_full_path()) return HttpResponseRedirect(self.request.get_full_path())
def create_source_form_form(self, **kwargs): def create_source_form_form(self, **kwargs):
@@ -339,10 +338,13 @@ class UploadInteractiveVersionView(UploadBaseView):
).dispatch(request, *args, **kwargs) ).dispatch(request, *args, **kwargs)
def forms_valid(self, forms): def forms_valid(self, forms):
try:
uploaded_file = self.source.get_upload_file_object( uploaded_file = self.source.get_upload_file_object(
forms['source_form'].cleaned_data forms['source_form'].cleaned_data
) )
except SourceException as exception:
messages.error(self.request, exception)
else:
shared_uploaded_file = SharedUploadedFile.objects.create( shared_uploaded_file = SharedUploadedFile.objects.create(
file=uploaded_file.file file=uploaded_file.file
) )
@@ -371,6 +373,7 @@ class UploadInteractiveVersionView(UploadBaseView):
'available shortly.' 'available shortly.'
) )
) )
return HttpResponseRedirect( return HttpResponseRedirect(
reverse( reverse(
'documents:document_version_list', args=(self.document.pk,) 'documents:document_version_list', args=(self.document.pk,)