Exemple #1
0
 def dispatch_command(self, command, user):
     if type(command) == list and len(command) > 0:
         if command[0] == ' get_books_list':
             from ia_scribe.book.library import Library
             l = Library()
             blist = l.get_all_books()
             msg = str(list(blist))
             self.connection.msg(user, msg.encode('utf-8'))
             self.message_callback(
                 'Sent information about {} books in library'.format(
                     len(blist)))
Exemple #2
0
 def __init__(self, **kwargs):
     self.book = None
     self._book_obj = None
     self._books_db = Library()
     self._show_advanced_options = False
     self._log_panel = None
     self._log_displayed = False
     self._trigger_update = Clock.create_trigger(self._update, -1)
     super(BookMenuPopup, self).__init__(**kwargs)
     self.use_tooltips = True
     Clock.schedule_once(self._postponed_init, -1)
        def build(self):
            from ia_scribe.tasks.task_scheduler import TaskScheduler
            task_scheduler = TaskScheduler()
            task_scheduler.start()

            from ia_scribe.book.library import Library
            library = Library()

            app_screen = BookDownloadAppScreen(task_scheduler=task_scheduler,
                                               library=library)
            return app_screen
Exemple #4
0
 def __init__(self, **kwargs):
     self._identifier = None
     self._metadata = None
     self._new_book = False
     self._config = Scribe3Configuration()
     self.book_obj = None
     self.books_db = kwargs.pop('books_db', Library())
     self.rcs_manager = RCS()
     self.rcs_manager.subscribe(self._rcs_update_handler)
     self.task_scheduler = kwargs.pop('task_scheduler')
     self.books_dir = kwargs.get('books_dir', None) or BOOKS_DIR
     self.book_path = kwargs.get('book_path', None)
     self.config_path = kwargs.get('config_path', None) or CONFIG_DIR
     super(BookMetadataScreenBackend, self).__init__(**kwargs)
Exemple #5
0
 def _postponed_init(self, *args):
     menu = self.ids.menu_bar
     menu.fbind(menu.EVENT_OPTION_SELECT, self.on_menu_bar_option_select)
     view = self.ids.note_leafs_view
     view.fbind(view.EVENT_LEAF_SELECT, self.on_note_leaf_select)
     self._books_db = Library()
Exemple #6
0
class ReScribeScreen(TooltipScreen, Screen):

    cover_image = StringProperty(MISSING_IMAGE)
    book = ObjectProperty(None)
    scandata = ObjectProperty(None)
    scribe_widget = ObjectProperty(None)
    screen_manager = ObjectProperty(None)

    def __init__(self, **kwargs):
        self._note_leafs = []
        self.book_obj = None
        super(ReScribeScreen, self).__init__(**kwargs)
        Clock.schedule_once(self._postponed_init)

    def _postponed_init(self, *args):
        menu = self.ids.menu_bar
        menu.fbind(menu.EVENT_OPTION_SELECT, self.on_menu_bar_option_select)
        view = self.ids.note_leafs_view
        view.fbind(view.EVENT_LEAF_SELECT, self.on_note_leaf_select)
        self._books_db = Library()

    def on_pre_enter(self):
        self.load_scandata()
        self.load_note_leafs()
        self.setup_menu_bar()
        self.setup_book_info_panel()
        self.setup_note_leafs_view()

    def load_scandata(self):
        book_path = self.book['path']
        book_uuid = basename(book_path)
        self.book_obj = self._books_db.get_book(book_uuid)
        self.scandata = ScanData(book_path, downloaded=True)
        Logger.info(
            'ReScribeScreen: Loaded scandata from directory: {}'.format(
                book_path))

    def load_note_leafs(self):
        leafs = self._note_leafs
        del leafs[:]
        scandata = self.scandata
        book_path = self.book['path']
        original_path = join(book_path, 'thumbnails')
        reshoot_path = join(book_path, 'reshooting', 'thumbnails')
        for note_leaf in scandata.iter_flagged_leafs():
            leaf_data = scandata.get_page_data(note_leaf)
            image_name = '{:04d}.jpg'.format(note_leaf)
            reshoot_image_path = join(reshoot_path, image_name)
            page_number = leaf_data.get('pageNumber', None)
            new_leaf_data = {
                'original_image': join(original_path, image_name),
                'reshoot_image': reshoot_image_path,
                'leaf_number': note_leaf,
                'page_number': self._get_page_number(page_number),
                'page_type': leaf_data['pageType'],
                'note': leaf_data.get('note', None) or u'',
                'status': 1 if exists(reshoot_image_path) else 0
            }
            leafs.append(new_leaf_data)

    def _get_page_number(self, page_number_data):
        # TODO: Remove this method when scandata structure becomes the same
        # for reshooting mode and otherwise
        if page_number_data:
            if isinstance(page_number_data, dict):
                page_number = page_number_data.get('num', None)
                return None if page_number is None else int(page_number)
            elif isinstance(page_number_data, str):
                return int(page_number_data)
        return None

    def setup_menu_bar(self):
        menu = self.ids.menu_bar
        menu.identifier = self.book['identifier']
        #menu.upload_button_disabled = not self.is_rescribing_complete()
        menu.reshooting_button_disabled = not bool(self._note_leafs)

    def setup_book_info_panel(self):
        panel = self.ids.book_info_panel
        panel.scroll_y = 1.0
        cover_image = join(self.book['path'], 'thumbnails', '0001.jpg')
        if not exists(cover_image):
            cover_image = MISSING_IMAGE
        panel.cover_image = cover_image
        panel.claimer = self.get_claimer()
        panel.update_from_metadata(get_metadata(self.book['path']))

    def get_claimer(self):
        path = join(expanduser(self.book['path']), 'claimer')
        if exists(path):
            with open(path, 'r') as f:
                return f.read() or NONE_STR
        return NONE_STR

    def setup_note_leafs_view(self):
        view = self.ids.note_leafs_view
        view.leafs[:] = self._note_leafs
        view.refresh_views()

    def popup_dismiss_to_home(self, popup, *args):
        popup.dismiss(animation=False)
        self.go_to_home()

    def go_to_home(self, *args, **kwargs):
        self.screen_manager.transition.direction = 'left'
        self.screen_manager.current = 'upload_screen'

    def on_menu_bar_option_select(self, menu, option):
        if option == menu.OPTION_UPLOAD:
            self.package_and_schedule_for_upload()
        elif option == menu.OPTION_FIRST_LEAF:
            leaf_number = self.find_first_non_reshoot_leaf_number()
            if leaf_number:
                self.open_book_at_leaf(leaf_number)
        elif option == menu.OPTION_PUBLIC_NOTES:
            metadata = get_metadata(self.book['path'])
            notes = metadata.get('notes', None) or ''
            popup = BookNotesPopup(title='Edit public book notes', notes=notes)
            popup.bind(on_submit=self.on_book_notes_submit)
            popup.open()
        elif option == menu.OPTION_INTERNAL_NOTES:
            internal_notes = self.scandata.get_internal_book_notes() or ''
            popup = BookNotesPopup(title='Edit internal book notes',
                                   notes=internal_notes)
            popup.bind(on_submit=self.on_internal_book_notes_submit)
            popup.open()

    def on_book_notes_submit(self, popup, notes):
        metadata = get_metadata(self.book['path'])
        metadata_notes = metadata.get('notes', None) or ''
        notes = notes.strip()
        if metadata_notes != notes:
            if notes:
                metadata['notes'] = notes
                message = 'Saved public book notes: %s' \
                          % ('\n%s' % notes if '\n' in notes else notes)
            else:
                metadata.pop('notes', None)
                message = 'Removed public book notes'
            set_metadata(metadata, self.book['path'])
            Logger.info('ReScribeScreen: %s' % message)
            self.book_obj.reload_metadata()

    def on_internal_book_notes_submit(self, popup, notes):
        scandata = self.scandata
        internal_notes = scandata.get_internal_book_notes() or ''
        notes = notes.strip()
        if internal_notes != notes:
            scandata.set_internal_book_notes(notes)
            scandata.save()
            if notes:
                message = 'Saved internal book notes: %s' \
                          % ('\n%s' % notes if '\n' in notes else notes)
            else:
                message = 'Removed internal book notes'
            Logger.info('ReScribeScreen: %s' % message)
            self.book_obj.reload_scandata()

    def on_note_leaf_select(self, note_leafs_view, note_leaf):
        self.open_book_at_leaf(note_leaf['leaf_number'])

    def find_first_non_reshoot_leaf_number(self):
        for note_leaf_data in self._note_leafs:
            if note_leaf_data['status'] == 0:
                return note_leaf_data['leaf_number']
        try:
            ret = self._note_leafs[0]['leaf_number']
            return ret
        except:
            return None

    def open_book_at_leaf(self, leaf_number):
        Logger.debug('ReScribeScreen: Trying to open book with id: {}'.format(
            self.book['identifier']))
        screen_name = 'reshoot_screen'
        try:
            capture_screen = self.screen_manager.get_screen(screen_name)
        except Exception:
            capture_screen = ReShootScreen(name=screen_name)
            self.screen_manager.add_widget(capture_screen)
            capture_screen.pos = self.screen_manager.pos
        capture_screen.book = self.book
        capture_screen.reopen_at = leaf_number
        capture_screen.scandata = self.scandata
        capture_screen.screen_manager = self.screen_manager
        capture_screen.scribe_widget = self.scribe_widget
        '''
        target_screen = screen_name
        models, ports = self.scribe_widget.cameras.get_cameras()
        camera_ports = self.scribe_widget.cameras.camera_ports
        if camera_ports['left'] not in ports:
            target_screen = 'calibration_screen'
        if camera_ports['right'] not in ports:
            target_screen = 'calibration_screen'
        foldout_port = camera_ports['foldout']
        if foldout_port is not None and foldout_port not in ports:
            target_screen = 'calibration_screen'
        if target_screen == 'calibration_screen':
            screen = self.screen_manager.get_screen('calibration_screen')
            screen.target_screen = 'reshoot_screen'
            self.screen_manager.transition.direction = 'left'
            self.screen_manager.current = target_screen
        else:
        '''
        self.screen_manager.transition.direction = 'left'
        self.screen_manager.current = screen_name

    def is_rescribing_complete(self):
        if not self._note_leafs:
            return False
        for leaf_data in self._note_leafs:
            if not exists(leaf_data['reshoot_image']):
                return False
        return True

    def package_and_schedule_for_upload(self):
        if self.is_rescribing_complete():
            self.action = UploadCorrectionsBookActionMixin(
                book=self.book_obj,
                task_scheduler=self.scribe_widget.task_scheduler,
                done_action_callback=self.go_to_home)
            self.action.display()
        else:
            msg = 'ReScribeScreen: Book is not done rescribing'
            popup = InfoPopup(title='Error', message=msg, auto_dismiss=False)
            popup.bind(on_submit=popup.dismiss)
            popup.open()
            Logger.error(msg)
Exemple #7
0
class BookMenuPopup(TooltipControl, FormBehavior, OverlayView):

    image = StringProperty()
    media_type = StringProperty(allownone=False)
    book_path = StringProperty(NONE_STR)
    title = StringProperty(NONE_STR)
    creator = StringProperty(NONE_STR)
    volume = StringProperty(NONE_STR)
    boxid = StringProperty(NONE_STR)
    uuid = StringProperty(NONE_STR)
    identifier = StringProperty(NONE_STR)
    isbn = StringProperty(NONE_STR)
    shiptracking = StringProperty(NONE_STR)
    status = StringProperty(NONE_STR)
    status_numeric = StringProperty(NONE_STR)
    next_states = StringProperty(NONE_STR)
    path_to_success = StringProperty(NONE_STR)
    operator = StringProperty(NONE_STR)
    edit_date = StringProperty(NONE_STR)
    update_date = StringProperty(NONE_STR)
    create_date = StringProperty(NONE_STR)
    leafs = StringProperty(NONE_STR)
    ppi = StringProperty(NONE_STR)
    error_msg = StringProperty(NONE_STR)
    task_msg = StringProperty(NONE_STR)
    worker_log = StringProperty()
    loading_image = LOADING_IMAGE

    _has_identifier = BooleanProperty(False)

    def __init__(self, **kwargs):
        self.book = None
        self._book_obj = None
        self._books_db = Library()
        self._show_advanced_options = False
        self._log_panel = None
        self._log_displayed = False
        self._trigger_update = Clock.create_trigger(self._update, -1)
        super(BookMenuPopup, self).__init__(**kwargs)
        self.use_tooltips = True
        Clock.schedule_once(self._postponed_init, -1)

    def _postponed_init(self, *args):
        menu = self.ids.entry_box
        menu.fbind('on_selection', self._on_option_selection)

    def init_from_data(self, book):
        self.book = book
        self._book_obj = self._books_db.get_item(book['uuid'])
        self._book_obj.subscribe(self._trigger_update)
        self._update()

    def _update(self, *args, **kwargs):
        self._update_attributes()
        self._update_from_metadata()
        self._update_ppi_from_scandata()
        self._update_book_options()
        if self._book_obj.is_locked():
            self.lock_controls()
            owner = self._book_obj.worker_lock
            self.task_msg = 'This book is being worked on by {}'.format(owner)
        else:
            self.task_msg = ''
            self.unlock_controls()

    def _update_attributes(self):
        book_obj = self._book_obj
        self.uuid = self.book['uuid']
        self.media_type = self.book.get('type').lower()
        self.book_path = book_obj.path
        self.error_msg = self.book.get('error', None) \
                         or book_obj.msg if book_obj.msg else None \
                         or book_obj.error if book_obj.error else None \
                         or ''
        status_tag = book_obj.status
        self.status = status_human_readable.get(status_tag, status_tag)
        self.status_numeric = '%s' % book_obj.get_numeric_status()

        if int(self.status_numeric) in ERROR_STATES:
            self.ids.status_numeric_button.rgba = (0.5, 0, 0, 1)
        else:
            self.ids.status_numeric_button.rgba = (0, 0.28, 0.42, .7)

        self.next_states = self._format_status_list(
            book_obj.get_available_next_states(human_readable=True))
        self.path_to_success = self._format_status_list(
            book_obj.get_path_to_upload(human_readable=True), separator='->')
        self.identifier = book_obj.identifier or NONE_STR
        self._has_identifier = bool(book_obj.identifier)
        to_string = self._book_value_to_string
        self.leafs = to_string(book_obj, 'leafs')
        self.title = to_string(book_obj, 'title')
        self.creator = to_string(book_obj, 'creator')
        self.volume = to_string(book_obj, 'volume')
        self.boxid = to_string(book_obj, 'boxid')
        self.isbn = to_string(book_obj, 'isbn')
        self.shiptracking = to_string(book_obj, 'shiptracking')
        self.operator = to_string(book_obj, 'operator')
        if book_obj.date_last_modified:
            self.edit_date = time.strftime(
                '%m/%d/%Y %H:%M:%S',
                time.localtime(book_obj.date_last_modified))
        else:
            self.edit_date = NONE_STR

        if book_obj.date_last_updated:
            self.update_date = time.strftime(
                '%m/%d/%Y %H:%M:%S',
                time.localtime(book_obj.date_last_updated))
        else:
            self.update_date = NONE_STR

        if book_obj.date_created:
            self.create_date = time.strftime(
                '%m/%d/%Y %H:%M:%S', time.localtime(book_obj.date_created))
        else:
            self.create_date = NONE_STR

        self.image = book_obj.get_cover_image()
        self.worker_log = book_obj.get_log()

    def _format_status_list(self, status_list, separator='|'):
        ret = " {} ".format(separator).join(status_list)
        return text_type(ret)

    def _update_book_options(self):
        menu = self.ids.entry_box
        num_status = UploadStatus[self._book_obj.status].value
        data = BOOK_STATUS_OPTIONS_TABLE.get(num_status, None)
        data = list(data) if data else list()

        advanced_text = 'Show Advanced'
        if self._show_advanced_options:
            advanced_text = 'Hide Advanced'
            data.append({
                'key': 'show_plan',
                'text': 'Action Plan',
                'icon': 'icon_expand_menu.png',
                'color': 'blue'
            })
            data.append({
                'key': 'move_along',
                'text': 'Move along',
                'icon': 'ff.png',
                'color': 'blue'
            })
        data.append({
            'key': 'advanced',
            'text': advanced_text,
            'icon': 'baseline_play_circle_filled_white_48dp.png',
            'color': 'gray'
        })
        data.append({
            'key': 'cancel',
            'text': 'Close',
            'icon': 'close_x.png',
            'color': 'gray'
        })
        if self._show_advanced_options:
            data = self._generate_next_actions_buttons() + data
        if len(data) % 2 == 1:
            data.insert(-2, {'key': 'dummy', 'disabled': True, 'opacity': 0})
        menu.data = data

    def _generate_next_actions_buttons(self):
        out = []
        next_actions = self._book_obj.get_available_next_actions()
        for action in next_actions:
            option = {
                'key': 'do_action',
                'text': action,
                'icon': 'baseline_play_circle_filled_white_48dp.png',
                'color': 'orange'
            }
            out.append(option)
        return out

    def _update_from_metadata(self):
        ''' this updates the values of certain self fields
        listed in `augmented_fields`, with the values fetched from
        reading metadata.xml of that book. We shouldn't be doing this
        here, instead relying on all the right information being passed
        to the constructor, which will come when we can pass more structured
        Book objects (2.0-Release branch)
        '''
        try:
            self.metadata = get_metadata(self.book_path)
        except Exception:
            Logger.exception(
                'Failed to get book metadata from path: {}'.format(
                    self.book_path))
            self.metadata = None
        augmented_fields = ['isbn', 'shiptracking']
        if self.metadata:
            for field in augmented_fields:
                if field in self.metadata:
                    setattr(self, field,
                            self._book_value_to_string(self.metadata, field))

    def _update_ppi_from_scandata(self):
        scandata = self._book_obj.get_scandata()
        bookData = scandata.get('bookData', None)
        if bookData:
            ppi = bookData.get('ppi', None)
            if ppi:
                self.ppi = str(ppi)

    def _book_value_to_string(self, md, key):
        value = md.get(key, None)
        if value is not None:
            if isinstance(value, list):
                return '; '.join(value)
            return '%s' % value
        return NONE_STR

    def _on_option_selection(self, menu, selection):
        selected = selection[0]
        if selected['key'] == 'advanced':
            self._show_advanced_options = not self._show_advanced_options
            self._update_book_options()
        elif selected['key'] == 'do_action':
            self.submit_data((selection[0]['key'], selection[0]['text']))
        else:
            self.submit_data(selection[0]['key'])

    def _on_book_event(self, event, book, topic):
        #self._update()
        pass

    def open_book_path(self):
        book_path = self.book_path
        if book_path and exists(book_path):
            subprocess.check_call(['xdg-open', book_path.encode('utf-8')])

    def on_dismiss(self):
        super(BookMenuPopup, self).on_dismiss()
        self.hide_log_panel()
        if self._book_obj:
            self._book_obj.unsub(self._trigger_update)
            self._book_obj = None
        self.book = None

    def show_log_panel(self, log, log_state):
        if not self._log_panel:
            self._log_panel = BookMenuPopupLogPanel(size_hint_y=0.95)
            self._log_panel.fbind('state', self._on_log_panel_state)
        if not self._log_displayed:
            self.ids.container.add_widget(self._log_panel)
            self._log_displayed = True
        self._log_panel.log = log
        self._log_panel.state = log_state

    def hide_log_panel(self):
        if self._log_panel and self._log_displayed:
            self._log_displayed = False
            self._log_panel.state = 'close'
            self.ids.container.remove_widget(self._log_panel)

    def _on_log_panel_state(self, log_panel, state):
        if state == 'close':
            self.hide_log_panel()
        elif state == 'show_log':
            log_panel.log = self.worker_log
        elif state == 'show_full_log':
            log = self._book_obj.get_full_log()
            log_panel.log = log[-10000:]
        elif state == 'show_history':
            history = self._book_obj.get_status_history()
            log_panel.log = '\n'.join(history)

    def show_log(self):
        self.show_log_panel(self.worker_log, 'show_log')

    def show_book_history(self):
        history = self._book_obj.get_status_history()
        self.show_log_panel('\n'.join(history), 'show_history')

    def show_book_full_log(self, *args):
        log = self._book_obj.get_full_log()
        # not entirely sure why truncating is necessary, but for now...
        self.show_log_panel(log[-10000:], 'show_full_log')

    def open_web_identifier(self):
        if self._has_identifier:
            webbrowser.open('https://archive.org/details/{}'.format(
                self._book_obj.identifier))

    def lock_controls(self):
        self.ids.entry_box.disabled = True

    def unlock_controls(self):
        self.ids.entry_box.disabled = False
from ia_scribe.book.automata import move_along
from ia_scribe.book.library import Library
from ia_scribe.book.book import Book
from ia_scribe.breadcrumbs import api as metrics_api
from ia_scribe.breadcrumbs import other_stats
from ia_scribe.cameras.optics import Cameras
from ia_scribe.config.config import Scribe3Configuration
from ia_scribe.notifications.notifications_manager import NotificationManager
from ia_scribe.update.update import UpdateManager
from ia_scribe.utils import restart_app, restart_process
from ia_scribe.book import metadata
from ia_scribe.ia_services.rcs import RCS

cameras = Cameras()
config = Scribe3Configuration()
library = Library()
notification_manager = NotificationManager()
update_manager = UpdateManager()
rcs_manager = RCS()

ALLOWED_TOKENS = [
    'help',
    'print',
    'clear',
    'exit',
    'library',
    'book',
    'telemetry',
    'metadata',
    'rcs',
    'config',
 def _postponed_init(self, *args):
     self._books_db = Library()
     self.update_manager = UpdateManager()
     self.update_manager.task_scheduler = self.task_scheduler
     self.update_manager.schedule_update_check()