Пример #1
0
    def setUp(self):
        self.setUpPyfakefs()
        logging.disable()

        type_ = models.NotebookType('A4', 210, 297)
        path = pathlib.Path('/test/notebook.pdf')
        self.notebook = models.Notebook('notebook', type_, path)

        self.fs.create_file(str(self.notebook.path))

        self.pages_dir = pathlib.Path('~/.local/share/smth/pages').expanduser()
        self.fs.create_dir(str(self.pages_dir / self.notebook.title))

        untitled_notebook = models.Notebook('Untitled', None, None)

        self.db = mock.MagicMock(**{
            'get_notebook_titles.return_value': [self.notebook.title],
            'get_notebook_by_title.return_value': self.notebook,
            'get_notebook_by_path.return_value': untitled_notebook,
            'notebook_exists.return_value': False,
        })

        self.view = mock.MagicMock(**{
            'ask_for_notebook.return_value': self.notebook.title,
        })

        self.args = mock.MagicMock()
Пример #2
0
    def get_notebook_by_title(self, title: str) -> models.Notebook:
        """Return notebook with specific title from database.

        Args:
            title:
                A title of a notebook.
        Raises:
            db.Error:
                An error occured executing the query.
        """
        notebook = models.Notebook('', models.NotebookType('', 0, 0),
                                   pathlib.Path())

        connection = None

        try:
            connection = self._connect()
            cursor = connection.execute(const.SQL_GET_NOTEBOOK_BY_TITLE,
                                        (title, ))
            row = cursor.fetchone()
            if row:
                notebook = self._make_notebook_from_row(row)

        except (sqlite3.Error, Error) as exception:
            self._handle_error('Failed to get notebook from database',
                               exception)

        finally:
            if connection:
                connection.close()

        return notebook
Пример #3
0
    def test_scan_paired_pages(self):
        type_ = models.NotebookType('', 160, 200)
        type_.pages_paired = True
        notebook = models.Notebook('', type_, '')

        pages_queue = collections.deque()
        pages_queue.extend([1, 2, 3])

        scanner_ = scanner.Scanner(self.conf, self.callback)
        scanner_.scan(notebook, pages_queue)

        self.callback.on_start.assert_called_once_with('device', [1, 2, 3])
        self.callback.on_start_scan_page.assert_has_calls([
            mock.call(1),
            mock.call(2),
            mock.call(3),
        ])

        page_width_pt = math.ceil(type_.page_width * 150 / 25.4)
        page_height_pt = math.ceil(type_.page_height * 150 / 25.4)

        self.image = self.image.crop((0, 0, page_width_pt, page_height_pt))

        self.callback.on_finish_scan_page.assert_has_calls([
            mock.call(notebook, 1, self.image),
            mock.call(notebook, 2, self.image),
            mock.call(notebook, 3, self.image),
        ])

        self.assertEqual(notebook.total_pages, 3)
Пример #4
0
    def setUp(self):
        self.setUpPyfakefs()
        logging.disable()

        self.notebook = models.Notebook('notebook', None,
                                        pathlib.Path('/test/path.pdf'))

        self.db = mock.MagicMock(
            **{
                'get_notebook_titles.return_value': [self.notebook.title],
                'get_notebook_by_title.return_value': self.notebook,
            })

        self.view = mock.MagicMock(
            **{
                'ask_for_notebook.return_value': self.notebook.title,
                'confirm.return_value': True,
            })

        self.fs.create_file(self.notebook.path)

        pages_root = pathlib.Path('~/.local/share/smth/pages').expanduser()
        self.pages_dir_path = pages_root / self.notebook.title
        self.fs.create_dir(self.pages_dir_path)

        self.args = mock.MagicMock()
Пример #5
0
    def test_scan_nothing_to_scan(self):
        scanner_ = scanner.Scanner(self.conf, self.callback)
        notebook = models.Notebook('', models.NotebookType('', 0, 0), '')
        pages_queue = collections.deque()
        scanner_.scan(notebook, pages_queue)

        sane.scan.assert_not_called()
        self.callback.on_error.assert_called_once()
Пример #6
0
 def _make_notebook_from_row(self, row: sqlite3.Row) -> models.Notebook:
     type_ = self.get_type_by_id(row['type_id'])
     notebook = models.Notebook(row['title'], type_,
                                pathlib.Path(row['path']))
     notebook.id = row['id']
     notebook.total_pages = row['total_pages']
     notebook.first_page_number = row['first_page_number']
     return notebook
Пример #7
0
    def setUp(self):
        logging.disable()

        type_ = models.NotebookType('A4', 210, 297)
        self.notebook = models.Notebook('Notebook', type_, '/test/path.pdf')
        self.notebook.total_pages = 3

        self.db = mock.MagicMock(
            **{
                'get_notebook_titles.return_value': [self.notebook.title],
                'get_notebook_by_title.return_value': self.notebook,
            })

        self.scan_prefs = {
            'device': 'device',
            'notebook': self.notebook.title,
            'append': '3'
        }

        self.view = mock.MagicMock(
            **{
                'ask_for_notebook.return_value': self.notebook.title,
                'ask_for_pages_to_append.return_value': 3,
                'ask_for_pages_to_replace.return_value': ['1', '2', '3-4'],
            })

        self.args = mock.MagicMock(**{
            'pdf_only': False,
            'set_device': False,
        })

        self.conf = mock.MagicMock(**{
            'scanner_device': None,
            'scanner_delay': 0,
        })

        config_patcher = mock.patch('smth.config.Config')
        config_patcher.start().return_value = self.conf
        self.addCleanup(config_patcher.stop)

        self.pdf = mock.MagicMock()
        fpdf_patcher = mock.patch('fpdf.FPDF')
        fpdf_patcher.start().return_value = self.pdf
        self.addCleanup(fpdf_patcher.stop)

        self.image = mock.MagicMock(size=(100, 200))
        self.scanner = mock.MagicMock(**{'scan.return_value': self.image})

        sane.init = mock.MagicMock()

        devices = [
            ('pixma:04A9176D_3EBCC9', 'vendor', 'mode', 'scanner device'),
        ]

        sane.get_devices = mock.MagicMock(return_value=devices)

        sane.open = mock.MagicMock(return_value=self.scanner)
Пример #8
0
    def test_scan_keyboard_interrupt(self):
        sane.open.side_effect = KeyboardInterrupt

        scanner_ = scanner.Scanner(self.conf, self.callback)

        notebook = models.Notebook('', models.NotebookType('', 0, 0), '')
        pages_queue = collections.deque()
        scanner_.scan(notebook, pages_queue)

        sane.scan.assert_not_called()
        self.callback.on_error.assert_called_once()
        sane.exit.assert_called_once()
Пример #9
0
    def test_total_pages(self):
        notebook = models.Notebook('title', None, 'path')

        self.assertEqual(notebook.total_pages, 0)

        notebook.total_pages = 10
        self.assertEqual(notebook.total_pages, 10)

        notebook.total_pages = 0
        self.assertEqual(notebook.total_pages, 0)

        notebook.total_pages = -10
        self.assertEqual(notebook.total_pages, 0)
Пример #10
0
    def setUp(self):
        self.db = db.DB(self.DB_PATH)

        # Types for tests
        self.types = self.db.get_types()
        type1 = models.NotebookType('Type 1', 100, 200)
        type2 = models.NotebookType('Type 2', 200, 100)
        type2.pages_paired = True
        self.types.append(type1)
        self.types.append(type2)
        for type_ in self.types:
            self.db.save_type(type_)

        # Notebooks for tests
        notebook1 = models.Notebook('Notebook 1', type1, '/test/notebook1.pdf')
        notebook2 = models.Notebook('Notebook 2', type2, '/test/notebook2.pdf')
        notebook2.total_pages = 10
        notebook2.first_page_number = 0
        notebook3 = models.Notebook('Notebook 3', type1, '/test/notebook3.pdf')
        notebook3.first_page_number = 2
        self.notebooks = [notebook1, notebook2, notebook3]
        for notebook in self.notebooks:
            self.db.save_notebook(notebook)
Пример #11
0
    def test_on_finish(self):
        type_ = models.NotebookType('', 160, 200)

        notebook = models.Notebook('', type_, pathlib.Path('/test/path.pdf'))
        notebook.total_pages = 3

        with mock.patch('importlib.util.find_spec') as find_spec:
            find_spec.return_value = None

            self.callback.on_finish(notebook)

        self.db.save_notebook.assert_called_once()
        self.assertEqual(self.pdf.add_page.call_count, 3)
        self.assertTrue(notebook.path.exists())
Пример #12
0
    def test_execute_path_already_taken_by_another_notebook(self):
        path = pathlib.Path('/new/path.pdf')

        existing_notebook = models.Notebook('another', None, path)
        self.db.get_notebook_by_path.return_value = existing_notebook

        answers = {
            'title': 'notebook',
            'path': str(path),
        }

        self.view.ask_for_updated_notebook_properties.return_value = answers

        with self.assertRaises(SystemExit):
            commands.UpdateCommand(self.db, self.view).execute(self.args)

        self.db.save_notebook.assert_not_called()
Пример #13
0
    def test_on_finish_missing_images(self):
        """Should create PDF but show errors."""
        self.pdf.image.side_effect = RuntimeError

        type_ = models.NotebookType('', 160, 200)

        notebook = models.Notebook('', type_, pathlib.Path('/test/path.pdf'))
        notebook.total_pages = 3

        with mock.patch('importlib.util.find_spec') as find_spec:
            find_spec.return_value = None

            self.callback.on_finish(notebook)

        self.db.save_notebook.assert_called_once()
        self.assertEqual(self.pdf.add_page.call_count, 3)
        self.assertEqual(self.view.show_error.call_count, 3)
        self.assertTrue(notebook.path.exists())
Пример #14
0
    def execute(self, args: argparse.Namespace) -> None:
        """Asks user for new notebook info, saves notebook in the database.

        If path to PDF ends with '.pdf', it is treated as a file.  That allows
        user to specify custom name for notebook's file.  Otherwise, treat the
        path as a directory.  The file will be stored in that directory.
        If the directory does not exist, create it with all parent directories.
        """
        try:
            type_titles = self._db.get_type_titles()

        except db.Error as exception:
            self.exit_with_error(exception)

        if not type_titles:
            self._view.show_info("No types found. Please, create a new one:")
            self._view.show_separator()
            types.TypesCommand(self._db, self._view).execute(['--create'])

            type_titles = self._db.get_type_titles()

            if type_titles:
                self._view.show_separator()
                self._view.show_info('Creating a new notebook:')
            else:
                self.exit_with_error("No types found. Create one with "
                                     "'smth types --create'.")

        validator = validators.NotebookValidator(self._db)

        answers = self._view.ask_for_new_notebook_info(type_titles, validator)

        if not answers:
            log.info('Creation stopped due to keyboard interrupt')
            self._view.show_info('Nothing created.')
            return

        title = answers['title']

        try:
            type_ = self._db.get_type_by_title(answers['type'])

        except db.Error as exception:
            self.exit_with_error(exception)

        path = pathlib.Path(os.path.expandvars(
            answers['path'])).expanduser().resolve()

        if str(path).endswith('.pdf'):
            if not path.parent.exists():
                path.parent.mkdir(parents=True, exist_ok=True)
        else:
            if not path.exists():
                path.mkdir(parents=True, exist_ok=True)

            path = path / f'{title}.pdf'

            if path.exists():
                message = ("Notebook not created because "
                           f"'{path}' already exists.")
                self.exit_with_error(message)

        notebook = models.Notebook(title, type_, path)
        notebook.first_page_number = answers['first_page_number']

        pdf = fpdf.FPDF()
        pdf.add_page()

        try:
            pdf.output(path)
            log.info("Created empty PDF at '{path}'")

            self._db.save_notebook(notebook)

        except (OSError, db.Error) as exception:
            self.exit_with_error(exception)

        pages_root = os.path.expanduser('~/.local/share/smth/pages')
        pages_dir = os.path.join(pages_root, notebook.title)
        pathlib.Path(pages_dir).mkdir(parents=True)

        message = (f"Create notebook '{notebook.title}' "
                   f"of type '{notebook.type.title}' at '{notebook.path}'")
        log.info(message)
        self._view.show_info(message)
Пример #15
0
 def test_title(self):
     self.assertEqual(models.Notebook('Test', None, 'path').title, 'Test')
     self.assertEqual(models.Notebook('', None, 'path').title, 'Untitled')
Пример #16
0
 def setUp(self):
     self.notebook = models.Notebook('title', None, 'path')
Пример #17
0
 def test_type(self):
     type_ = mock.MagicMock()
     notebook = models.Notebook('title', type_, 'path')
     self.assertIs(notebook.type, type_)
     with self.assertRaises(AttributeError):
         notebook.type = type_
Пример #18
0
 def test_path(self):
     path = models.Notebook('', None, '/path.pdf').path
     self.assertEqual(path, '/path.pdf')
     self.notebook.path = '/test/path/to/file.pdf'
     self.assertEqual(self.notebook.path, '/test/path/to/file.pdf')
Пример #19
0
 def setUp(self):
     self.type = models.NotebookType('', 100, 200)
     self.notebook = models.Notebook('', self.type, '')
     self.resolution = 150
Пример #20
0
 def test__repr__(self):
     type_ = mock.MagicMock()
     type_.title = 'Test Type'
     notebook = models.Notebook('Test', type_, 'path')
     expected = "<Notebook 'Test' of type 'Test Type'>"
     self.assertEqual(notebook.__repr__(), expected)