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 Meta:
fields = ('label', 'device_name', 'mode', 'resolution', 'enabled')
fields = (
'label', 'device_name', 'mode', 'resolution', 'source',
'adf_mode', 'enabled'
)
model = SaneScanner

View File

@@ -2,6 +2,22 @@ from __future__ import unicode_literals
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_MONOCHROME = 'monochrome'
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 .classes import Attachment, PseudoFile, SourceUploadedFile, StagingFile
from .exceptions import SourceException
from .literals import (
DEFAULT_INTERVAL, DEFAULT_POP3_TIMEOUT, DEFAULT_IMAP_MAILBOX,
DEFAULT_METADATA_ATTACHMENT_NAME, SCANNER_MODE_COLOR, SCANNER_MODE_CHOICES,
SOURCE_CHOICES,SOURCE_CHOICE_STAGING, SOURCE_CHOICE_WATCH,
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_CHOICE_WEB_FORM, SOURCE_INTERACTIVE_UNCOMPRESS_CHOICES,
SOURCE_UNCOMPRESS_CHOICES, SOURCE_UNCOMPRESS_CHOICE_N,
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
@@ -175,14 +178,34 @@ class SaneScanner(InteractiveSource):
verbose_name=_('Device name')
)
mode = models.CharField(
choices=SCANNER_MODE_CHOICES, default=SCANNER_MODE_COLOR,
max_length=16, verbose_name=_('Mode')
blank=True, choices=SCANNER_MODE_CHOICES, default=SCANNER_MODE_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')
)
resolution = models.PositiveIntegerField(
default=300, help_text=_(
'Sets the resolution of the scanned image in DPI (dots per inch).'
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')
)
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:
verbose_name = _('SANE Scanner')
@@ -193,31 +216,55 @@ class SaneScanner(InteractiveSource):
def get_upload_file_object(self, form_data):
temporary_file_object = TemporaryFile()
command_line = [
setting_scanimage_path.value, '-d', self.device_name,
'--format', 'tiff',
]
if self.resolution:
command_line.extend(
['--resolution', '{}'.format(self.resolution)]
)
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:
command_line = [
setting_scanimage_path.value, '-d', self.device_name,
'--resolution', '{}'.format(self.resolution), '--mode',
self.mode, '--format', 'tiff'
]
logger.debug('Scan command line: %s', command_line)
result = subprocess.check_call(
command_line, stdout=temporary_file_object
subprocess.check_call(
command_line, stdout=temporary_file_object, stderr=filestderr
)
except subprocess.CalledProcessError as exception:
except subprocess.CalledProcessError:
filestderr.seek(0)
error_message = filestderr.read()
logger.error(
'Exception while scanning from source:%s ; %s', self,
exception
)
self.logs.create(
message=_('Error while scanning; %s') % exception
error_message
)
return SourceUploadedFile(
source=self, file=PseudoFile(
file=temporary_file_object, name='scan {}'.format(now())
message = _('Error while scanning; %s') % error_message
self.logs.create(message=message)
raise SourceException(message)
else:
return SourceUploadedFile(
source=self, file=PseudoFile(
file=temporary_file_object, name='scan {}'.format(now())
)
)
)
class StagingFolderSource(InteractiveSource):

View File

@@ -24,18 +24,13 @@ from documents.tasks import task_upload_new_version
from metadata.api import decode_metadata_from_url
from navigation import Link
from .exceptions import SourceException
from .forms import (
NewDocumentForm, NewVersionForm, SaneScannerUploadForm, WebFormUploadForm,
WebFormUploadFormHTML5
)
from .literals import (
SOURCE_CHOICE_STAGING, SOURCE_CHOICE_SANE_SCANNER, SOURCE_CHOICE_WEB_FORM,
SOURCE_UNCOMPRESS_CHOICE_ASK,
SOURCE_UNCOMPRESS_CHOICE_Y
NewDocumentForm, NewVersionForm, WebFormUploadForm, WebFormUploadFormHTML5
)
from .literals import SOURCE_UNCOMPRESS_CHOICE_ASK, SOURCE_UNCOMPRESS_CHOICE_Y
from .models import (
InteractiveSource, Source, SaneScanner, StagingFolderSource,
WebFormSource
InteractiveSource, Source, SaneScanner, StagingFolderSource
)
from .permissions import (
permission_sources_setup_create, permission_sources_setup_delete,
@@ -211,49 +206,53 @@ class UploadInteractiveView(UploadBaseView):
else:
expand = False
uploaded_file = self.source.get_upload_file_object(
forms['source_form'].cleaned_data
)
shared_uploaded_file = SharedUploadedFile.objects.create(
file=uploaded_file.file
)
label = None
if 'document_type_available_filenames' in forms['document_form'].cleaned_data:
if forms['document_form'].cleaned_data['document_type_available_filenames']:
label = forms['document_form'].cleaned_data['document_type_available_filenames'].filename
if not self.request.user.is_anonymous():
user_id = self.request.user.pk
else:
user_id = None
try:
self.source.clean_up_upload_file(uploaded_file)
except Exception as exception:
messages.error(self.request, exception)
task_source_handle_upload.apply_async(kwargs=dict(
description=forms['document_form'].cleaned_data.get('description'),
document_type_id=self.document_type.pk,
expand=expand,
label=label,
language=forms['document_form'].cleaned_data.get('language'),
metadata_dict_list=decode_metadata_from_url(self.request.GET),
shared_uploaded_file_id=shared_uploaded_file.pk,
source_id=self.source.pk,
tag_ids=self.request.GET.getlist('tags'),
user_id=user_id,
))
messages.success(
self.request,
_(
'New document queued for uploaded and will be available '
'shortly.'
uploaded_file = self.source.get_upload_file_object(
forms['source_form'].cleaned_data
)
)
except SourceException as exception:
messages.error(self.request, exception)
else:
shared_uploaded_file = SharedUploadedFile.objects.create(
file=uploaded_file.file
)
label = None
if 'document_type_available_filenames' in forms['document_form'].cleaned_data:
if forms['document_form'].cleaned_data['document_type_available_filenames']:
label = forms['document_form'].cleaned_data['document_type_available_filenames'].filename
if not self.request.user.is_anonymous():
user_id = self.request.user.pk
else:
user_id = None
try:
self.source.clean_up_upload_file(uploaded_file)
except Exception as exception:
messages.error(self.request, exception)
task_source_handle_upload.apply_async(kwargs=dict(
description=forms['document_form'].cleaned_data.get('description'),
document_type_id=self.document_type.pk,
expand=expand,
label=label,
language=forms['document_form'].cleaned_data.get('language'),
metadata_dict_list=decode_metadata_from_url(self.request.GET),
shared_uploaded_file_id=shared_uploaded_file.pk,
source_id=self.source.pk,
tag_ids=self.request.GET.getlist('tags'),
user_id=user_id,
))
messages.success(
self.request,
_(
'New document queued for uploaded and will be available '
'shortly.'
)
)
return HttpResponseRedirect(self.request.get_full_path())
def create_source_form_form(self, **kwargs):
@@ -295,7 +294,7 @@ class UploadInteractiveView(UploadBaseView):
context['title'] = _(
'Upload a local document from source: %s'
) % self.source.label
if not isinstance(self.source, StagingFolderSource) and not isinstance(self.source, SaneScanner) :
if not isinstance(self.source, StagingFolderSource) and not isinstance(self.source, SaneScanner):
context['subtemplates_list'][0]['context'].update(
{
'form_action': self.request.get_full_path(),
@@ -339,38 +338,42 @@ class UploadInteractiveVersionView(UploadBaseView):
).dispatch(request, *args, **kwargs)
def forms_valid(self, forms):
uploaded_file = self.source.get_upload_file_object(
forms['source_form'].cleaned_data
)
shared_uploaded_file = SharedUploadedFile.objects.create(
file=uploaded_file.file
)
try:
self.source.clean_up_upload_file(uploaded_file)
except Exception as exception:
messages.error(self.request, exception)
if not self.request.user.is_anonymous():
user_id = self.request.user.pk
else:
user_id = None
task_upload_new_version.apply_async(kwargs=dict(
shared_uploaded_file_id=shared_uploaded_file.pk,
document_id=self.document.pk,
user_id=user_id,
comment=forms['document_form'].cleaned_data.get('comment')
))
messages.success(
self.request,
_(
'New document version queued for uploaded and will be '
'available shortly.'
uploaded_file = self.source.get_upload_file_object(
forms['source_form'].cleaned_data
)
)
except SourceException as exception:
messages.error(self.request, exception)
else:
shared_uploaded_file = SharedUploadedFile.objects.create(
file=uploaded_file.file
)
try:
self.source.clean_up_upload_file(uploaded_file)
except Exception as exception:
messages.error(self.request, exception)
if not self.request.user.is_anonymous():
user_id = self.request.user.pk
else:
user_id = None
task_upload_new_version.apply_async(kwargs=dict(
shared_uploaded_file_id=shared_uploaded_file.pk,
document_id=self.document.pk,
user_id=user_id,
comment=forms['document_form'].cleaned_data.get('comment')
))
messages.success(
self.request,
_(
'New document version queued for uploaded and will be '
'available shortly.'
)
)
return HttpResponseRedirect(
reverse(
'documents:document_version_list', args=(self.document.pk,)