def __init__(self, **kwargs): super(DownloadBookTask, self).__init__(**kwargs) self._book = None self._priority = 'medium' self._library = kwargs['library'] self.identifier = kwargs['identifier'] self.logger.info('Download books: Downloading {}'.format( self.identifier)) self.notifications_manager = NotificationManager() self._download_type = None
class NotificationsCleanerTask(TaskBase): def __init__(self, **kwargs): super(NotificationsCleanerTask, self).__init__(**kwargs) self._notification_manager = NotificationManager() def create_pipeline(self): return [ self._clean_expired_notifications, ] def _clean_expired_notifications(self): for notification in self._notification_manager.get_all_notifications(): if notification.is_expired() and not notification.is_sticky: self._notification_manager.remove_notification(notification)
def __init__(self, **kwargs): self._help_opened = False self._help_key_down = False self.timeout_event = None super(ScribeWidget, self).__init__(**kwargs) try: self.config = Scribe3Configuration() self.config.validate() self.init_pid_file() except CredentialsError as e: # if this is raised, we show the wizard msg = '[b]It looks like there is an issue with your credentials[/b].\n' \ '\n{}\nClick the button to begin the wizard.'.format(e.msg) popup = InfoPopup(title='Logged out', message=msg, auto_dismiss=False) popup.bind(on_submit=self.begin_wizard) popup.open() return except ScribeException as e: # if this is raised, the app can no longer continue msg = 'Scribe3 could not initialize\nbecause of the following error:' \ '\n\n[b]{}[/b].\n\nClick the button to close.'.format(str(e)) popup = InfoPopup(title='Initialization error', message=msg, auto_dismiss=False) app = App.get_running_app() popup.bind(on_submit=app.stop) popup.open() return #self.config.subscribe(get_adapter('config')) md_version = {'tts_version': scribe_globals.BUILD_NUMBER} set_sc_metadata(md_version) screen_manager = self.ids._screen_manager screen_manager.bind(current=self._on_current_screen) self._init_top_bar(self.config) self.help = Help() self.help.fbind('on_open', self._on_help_open) self.help.fbind('on_dismiss', self._on_help_dismiss) self.notifications_manager = NotificationManager() Clock.schedule_once(self._postponed_init, -1)
) from ia_scribe.tasks.book_tasks.petabox import get_pending_catalog_tasks from ia_scribe.book.scandata import ScanData from ia_scribe.ia_services.iabdash import push_event from ia_scribe import scribe_globals from ia_scribe.config.config import Scribe3Configuration from ia_scribe.exceptions import ScribeException from ia_scribe.book.upload_status import UploadStatus from ia_scribe.ia_services.btserver import get_ia_session from ia_scribe.notifications.notifications_manager import NotificationManager from os.path import join config = Scribe3Configuration() notifications_manager = NotificationManager() def _get_email(): return config.get('email') def _set_upload_lock_file(book, Logger): Logger.debug('Setting upload lock file') with open(join(book['path'], "upload_lock"), 'w+') as fp: pid_path = os.path.join(scribe_globals.CONFIG_DIR, 'scribe_pid') with open(pid_path) as f: pid_num = str(f.read().strip()) fp.write(pid_num)
def __init__(self, **kwargs): super(NotificationsCleanerTask, self).__init__(**kwargs) self._notification_manager = NotificationManager()
from ia_scribe.config.config import Scribe3Configuration from ia_scribe.notifications.notifications_manager import NotificationManager nm = NotificationManager() fun = nm.add_notification config = Scribe3Configuration() LIBRARY_EVENTS = [ 'book_created', 'book_deleted', ] BOOK_EVENTS = [ 'identifier-changed', 'state_change', 'book_update', ] IGNORE_EVENTS = [ 'message-updated', 'reloaded_metadata', 'reloaded_scandata', ] def book_adapter(book, event_type): if event_type in IGNORE_EVENTS: return elif event_type in BOOK_EVENTS: if config.is_true('show_book_notifications'): fun(title='{}'.format(book.name_human_readable()), message='{}'.format(book.last_activity),
class NotificationCenterApp(App): nm = ObjectProperty() def build(self): root = NotificationCenterWidget( pos_hint={'x': 0.0, 'center_y': 0.5}, size_hint=(1.0, 1.0) ) return root def on_start(self): super(NotificationCenterApp, self).on_start() self.root_window.size = (1000, 600) self.nm = NotificationManager() self.create_dummy_notifications() self.root.attach(self.nm, book_handler=self.book_handler) @staticmethod def book_handler(*args): pass def create_dummy_notifications(self): self.nm.add_notification(title='Test 1', message='Test notification ' * 20) self.nm.add_notification(title='Test 1', message='') self.nm.add_notification(title='Command and control', message='I have connected') self.nm.add_notification(title='Command and control', message='I have Joined a channel') self.nm.add_notification(title='Command and control', message='I have done something else') self.nm.add_notification(title='Command and control', message='I have just received a message from someone') self.nm.add_notification(title='A rather long title for a notification, that is for sure', message='I have just recieved a message from Central Command') self.nm.add_notification(title='Test 2', message='This is a book notification' * 10, book=object) self.nm.add_notification(title='Test 3', message='This is a system error notification' * 30, is_error=True) self.nm.add_notification(title='Test 4', message='This is a book error notification', book=object, is_error=True) for i in range(1, 30): self.nm.add_notification(title='AUtomated test notification number {}'.format(i), message='Automated notification') msg = '''Distinctio rem tenetur cumque. Est dicta dolores necessitatibus laudantium. Optio consequatur provident voluptatibus. Quia architecto debitis error. Eveniet expedita quia odio culpa. Cumque inventore voluptate reprehenderit. Repellat aut molestias natus. Magnam nihil nam delectus sit distinctio earum non fuga. Est fugit quia a architecto sit expedita. Non voluptas dolores voluptatem quia enim. Est autem ut ratione modi nisi. Debitis dolorem quae vero qui aut laboriosam. Non dolorem sit suscipit eligendi. Vel nihil consequatur numquam. Tenetur est placeat dignissimos voluptas corrupti voluptatem doloribus. Sed eligendi ducimus eveniet itaque dolorem eum sunt. Similique et suscipit ut perspiciatis et omnis. Itaque occaecati commodi rerum doloremque non quis ut voluptatem. Harum tempore officiis et atque animi possimus. Et reiciendis quis molestiae qui voluptatem adipisci aperiam. Dolorem natus officia impedit. Cupiditate aspernatur doloremque harum ex reiciendis est. ''' self.nm.add_notification(title='Sticky notifcation', message=msg, is_sticky=True)
def on_start(self): super(NotificationCenterApp, self).on_start() self.root_window.size = (1000, 600) self.nm = NotificationManager() self.create_dummy_notifications() self.root.attach(self.nm, book_handler=self.book_handler)
class DownloadCDTask(TaskBase): def __init__(self, **kwargs): super(DownloadCDTask, self).__init__(**kwargs) self._cd = None self._priority = 'medium' self._library = kwargs['library'] self.identifier = kwargs['identifier'] self.logger.info('Download CD: Downloading {}'.format(self.identifier)) self.notifications_manager = NotificationManager() self._download_type = None self.start_time = None self.total_time = None def create_pipeline(self): return [ self._begin, self._get_ia_session, self._load_item, self._verify_is_CD, self._verify_is_stub_item, self._create_stub_CD, self._create_files, self._create_scandata, self._set_states, self._release_lock, self._send_stats, ] def _begin(self): self.start_time = time.time() def handle_event(self, event_name, *args, **kwargs): if event_name == 'on_state' and self.state == CANCELLED_WITH_ERROR: if self._cd: self._cd.do_move_to_trash() self._cd.do_delete_anyway() def _get_ia_session(self): self.dispatch_progress('Getting IA session') self._ia_session = get_ia_session() def _load_item(self): self.dispatch_progress('Loading item') self.item = self._ia_session.get_item(self.identifier) self.logger.info('Download CD: target item: {}'.format( self.item.identifier)) def _verify_is_CD(self): self.dispatch_progress('Verifying this is an ArchiveCD item') mediatype = self.item.metadata.get('mediatype') software_version = self.item.metadata.get('software_version') assert mediatype == 'audio', 'This is not an audio item. It is {}.'.format( mediatype) assert software_version is not None, 'This item was not created with ArchiveCD' assert 'ArchiveCD' in software_version, 'This item was not created with ArchiveCD' def _verify_is_stub_item(self): self.dispatch_progress('Verifying this is a stub item') stub_file = self.item.get_file('stub.txt') #if not stub_file.exists: # raise Exception('No stub file found!') def _create_stub_CD(self): self.dispatch_progress('Creating local CD') message = "This CD is being downloaded and no actions are available just yet." cd_id = str(uuid4()) self._cd = self._library.new_cd(cd_id, status='download_incomplete', error=message) self._cd.set_lock() self._cd.logger.info('Download CD: Created stub CD {}'.format( self._cd)) def _create_files(self): self.dispatch_progress('Downloading files') ret = [] with open(os.path.join(self._cd.path, 'identifier.txt'), 'w+') as fp: fp.write(self.item.identifier) ret.append(fp.name) self._cd.logger.info('Download CD: Created {}'.format(fp.name)) with open(os.path.join(self._cd.path, 'downloaded'), 'w+') as fp: fp.write('True') ret.append(fp.name) self._cd.logger.info('Download CD: Created {}'.format(fp.name)) with open(os.path.join(self._cd.path, 'uuid'), 'w+') as fp: fp.write(self._cd.uuid) ret.append(fp.name) self._cd.logger.info('Download CD: Created {}'.format(fp.name)) self.item.get_file(self.item.identifier + '_meta.xml') \ .download(file_path=self._cd.path + '/metadata.xml') ret.append('{}'.format(self._cd.path + '/metadata.xml')) self._cd.logger.info('Download CD: Created metadata.xml') self._cd.reload_metadata() if not os.path.exists(os.path.join(self._cd.path, 'thumbs')): os.makedirs(os.path.join(self._cd.path, 'thumbs')) ret.append('{}'.format(self._cd.path + '/thumbs')) self._cd.logger.info('Download CD: Created thumbs directory') self.item.get_file(self.item.identifier + '_itemimage.png') \ .download(file_path=self._cd.path + '/cover.png') ret.append('{}'.format(self._cd.path + '/cover.png')) self._cd.logger.info('Download CD: Downloaded cover') self._files = ret self._cd.logger.info('Download CD: Created files.') def _create_scandata(self): self.dispatch_progress('Creating scandata') self._scandata=\ ScanData(self._cd.path) self._scandata.save() self._cd.reload_scandata() self._cd.logger.info('Download CD: Created scandata.') def _set_states(self): self.dispatch_progress('Setting states') self._cd.do_finish_download() def _send_stats(self): self.dispatch_progress('Notifying iabdash') payload = { 'files': self._files, 'total_time': self.total_time, } push_event('tts-cd-downloaded', payload, 'cd', self.identifier, os.path.join(self._cd.path, "iabdash.log")) self.notifications_manager.add_notification( title='Downloaded', message="CD {} has been downloaded.".format(self.identifier), show_system_tile=False, book=self._cd) def _release_lock(self): self.total_time = time.time() - self.start_time self._cd.logger.info('Download CD: ------ DONE. Downloaded {0} in ' '{1}s ----------'.format(self.identifier, self.total_time)) self._cd.release_lock()
class DownloadBookTask(TaskBase): def __init__(self, **kwargs): super(DownloadBookTask, self).__init__(**kwargs) self._book = None self._priority = 'medium' self._library = kwargs['library'] self.identifier = kwargs['identifier'] self.logger.info('Download books: Downloading {}'.format( self.identifier)) self.notifications_manager = NotificationManager() self._download_type = None def create_pipeline(self): return [ self._get_ia_session, self._load_item, self._validate_repub_state, self._create_stub_book, self._load_book_metadata, self._create_files, self._create_scandata, self._get_checkout_information, self._write_claimer_file, self._download_proxies, self._set_states, self._release_lock, self._send_stats, ] def handle_event(self, event_name, *args, **kwargs): if event_name == 'on_state' and self.state == CANCELLED_WITH_ERROR: if self._book: self._book.do_move_to_trash() self._book.do_delete_anyway() def _get_ia_session(self): self.dispatch_progress('Getting IA session') self._ia_session = get_ia_session() def _load_item(self): self.dispatch_progress('Loading item') try: self.item = self._ia_session.get_item(self.identifier) assert self.item.metadata['repub_state'] is not None except Exception as e: self.logger.error('No repub_state or item darkened. Skipping...') raise e self.logger.info( 'Download book: target item: {} (repub_state = {})'.format( self.item.identifier, self.item.metadata['repub_state'])) def _validate_repub_state(self): self.dispatch_progress('Validating repub state') is_repub_state_valid = lambda x: int(x.metadata[ 'repub_state']) in scribe_globals.ALLOWED_DOWNLOAD_REPUB_STATES self.logger.info('Validating repub_state {}'.format( int(self.item.metadata['repub_state']))) if not is_repub_state_valid(self.item): msg = 'Download book: Repub state is not 31 or 34 or 41' \ '(is {1}), refusing to download item {0}' \ .format(self.item.identifier, self.item.metadata['repub_state']) self.logger.error(msg) raise Exception(msg) def _create_stub_book(self): self.dispatch_progress('Creating local book') message = "This books is being downloaded and no actions are available just yet." book_id = str(uuid4()) self._book = self._library.new_book(book_id, status='download_incomplete', error=message) self._book.set_lock() self._book.logger.info('Download book: Created stub book {}'.format( self._book)) def _load_book_metadata(self): self.dispatch_progress('Loading metadata') md_url = ('https://{}/RePublisher/RePublisher-viewScanData.php' '?id={}'.format(self.item.d1, self.identifier)) self._md = requests.get(md_url, timeout=5) self._book.logger.info( 'Download book: Fetch scandata from cluster: {}'.format( self._md.status_code)) def _create_files(self): self.dispatch_progress('Downloading files') ret = [] with open(os.path.join(self._book.path, 'identifier.txt'), 'w+') as fp: fp.write(self.item.identifier) ret.append(fp.name) self._book.logger.info('Download book: Created {}'.format(fp.name)) with open(os.path.join(self._book.path, 'downloaded'), 'w+') as fp: fp.write('True') ret.append(fp.name) self._book.logger.info('Download book: Created {}'.format(fp.name)) with open(os.path.join(self._book.path, 'uuid'), 'w+') as fp: fp.write(self._book.uuid) ret.append(fp.name) self._book.logger.info('Download book: Created {}'.format(fp.name)) with open(os.path.join(self._book.path, 'scandata.xml'), 'w+') as fp: fp.write(self._md.content.decode()) ret.append(fp.name) self._book.logger.info('Download book: Created {}'.format(fp.name)) self.item.get_file(self.item.identifier + '_meta.xml') \ .download(file_path=self._book.path + '/metadata.xml') ret.append('{}'.format(self._book.path + '/metadata.xml')) self._book.logger.info('Download book: Created metadata.xml') self._book.reload_metadata() if not os.path.exists(os.path.join(self._book.path, 'reshooting')): os.makedirs(os.path.join(self._book.path, 'reshooting')) ret.append('{}'.format(self._book.path + '/reshooting')) self._book.logger.info('Download book: Created reshooting directory') self._files = ret self._book.logger.info('Download book: Created files, now converting ' 'scandata from RePublisher XML to Scribe3 JSON') def _create_scandata(self): self.dispatch_progress('Creating scandata') sc_path = os.path.join(self._book.path, 'scandata.xml') tree = book_helpers.validate_scandata_xml(sc_path, self._book) scandata_xml = book_helpers.create_normalized_scandata( tree, self._book) json_data = book_helpers.convert_normalized_scandata_to_json( scandata_xml) json_new = {} self._book.logger.info('Download book: Now converting to Scribe3 JSON') json_new['bookData'] = book_helpers.build_bookdata( json_data, self._book) json_new['pageData'] = book_helpers.build_pagedata( json_data, self._book) with open(os.path.join(self._book.path, 'scandata.json'), 'w') as outfile: json.dump(json_new, outfile) self._book.logger.info('Download book: Created {}'.format( outfile.name)) self._scandata = ScanData(self._book.path) self._scandata.save() self._book.reload_scandata() self._book.logger.info('Download book: Created scandata.') def _get_checkout_information(self): self.dispatch_progress('Pulling checkout information') book_checkout_url = ('https://{}/RePublisher/RePublisher-' 'checkoutBook.php?peek=true&id={}'.format( self.item.d1, self._book.identifier)) self._book.logger.info( 'Getting checkout information from {}'.format(book_checkout_url)) ret = self._ia_session.get(book_checkout_url) self._book.logger.info('Got {} ({})'.format(ret.text, ret.status_code)) self._checkout_info = json.loads(ret.text) def _write_claimer_file(self): self.dispatch_progress('Writing claimer file') if 'claimed_by' in self._checkout_info and self._checkout_info[ 'claimed_by'] != False: claimer = self._checkout_info['claimed_by'] else: claimer = '-' with open(os.path.join(self._book.path, 'claimer'), 'w+') as fp: fp.write(claimer) self._claimer = claimer self._book.logger.info('This book was claimed by {}'.format(claimer)) def _download_proxies(self): self.dispatch_progress('Downloading proxies') all_ok = True counter = 0 page_data = self._scandata.dump_raw()['pageData'] for i, page in enumerate(page_data): self.dispatch_progress('Downloading proxies [{}/{}]'.format( i, len(page_data))) if int(page) != i: self._book.logger.error('Download book: Download Proxies: ' 'CRITICAL MISMATCH') break short_msg = 'Download pics | {percent:.1f}% | {n}/{total}'.format( percent=i * 100 / len(page_data), n=i, total=len(page_data), ) self._book.update_message(short_msg) url = book_helpers.get_cluster_proxy_url_by_leaf( self._scandata, self.item, page) res = self.download_proxy_image(page, self._book, url) all_ok = all_ok and res counter += 1 if res: self._book.logger.debug( 'Download book: Got proxies for leaf #{0}'.format(page)) else: self._book.logger.error( 'Download book: Error downloading leaf #{0}'.format(page)) try: leafnr = self._scandata.get_page_num(page)['num'] except Exception: pass self._book.logger.info( 'Download book: Downloaded {} proxy images.'.format(counter)) return all_ok def _set_states(self): self.dispatch_progress('Setting states') self._book.error = None if int(self.item.metadata['repub_state']) == 31: book_final_repub_state = 32 self._download_type = 'corrections' self._book.do_end_download_correction() elif int(self.item.metadata['repub_state']) == 41: book_final_repub_state = 42 self._download_type = 'foldouts' self._book.do_end_download_foldout() else: self._book.logger( 'Error while processing item in repub_state {}'.format( self.item.metadata['repub_state'])) raise Exception( 'remote repub state in inconsistent with book download') self._book.logger.info( 'Setting remote repub_state to {}'.format(book_final_repub_state)) mdapi_response = self.item.modify_metadata( {'repub_state': book_final_repub_state}) self._book.logger.info( 'Response from MDAPI: {}'.format(mdapi_response)) if mdapi_response: self._mdapi_response_text = mdapi_response.text self._book.logger.info('Body of MDAPI: {}'.format( self._mdapi_response_text)) if mdapi_response.status_code != 200: raise Exception( 'MDAPI response was not OK! - Got this instead: {} - {}'. format(mdapi_response.status_code, mdapi_response.text)) self._book.logger.info( 'Download book: Set book repub_state to {}'.format( book_final_repub_state)) self._book_final_repub_state = book_final_repub_state else: raise Exception('No response from MDAPI. Aborting download.') def _send_stats(self): self.dispatch_progress('Notifying iabdash') payload = { 'repub_state': self._book_final_repub_state, 'checkout_info': self._checkout_info, 'claimer': self._claimer, 'files': self._files, } push_event('tts-book-downloaded', payload, 'book', self.identifier, os.path.join(self._book.path, "iabdash.log")) self.notifications_manager.add_notification( title='Downloaded', message="{} has been downloaded and is ready for {}.".format( self.identifier, self._download_type), show_system_tile=False, book=self._book) def _release_lock(self): total_time = 100 self._book.logger.info('Download book: ------ DONE. Downloaded {0} in ' '{1}s ----------'.format( self.identifier, total_time)) self._book.release_lock() def download_proxy_image( self, page, book, url, ): def is_proxy_valid(proxy_path): return True file_target = '{n:04d}.jpg'.format(n=int(page)) dest = os.path.os.path.join(book.path, "thumbnails", file_target) if url is not None: image = self._ia_session.get(url).content with open(dest, 'wb+') as proxy: book.logger.debug('Writing {}'.format(dest)) proxy.write(image) book.logger.info('Download book: Written {}'.format( proxy.name)) else: import shutil book.logger.debug('Page {} has no proxy, adding missing ' 'image at {}'.format(page, dest)) shutil.copyfile(scribe_globals.MISSING_IMAGE, dest) ret = is_proxy_valid(dest) return ret
class ScribeWidget(BoxLayout): books_db = ObjectProperty() task_scheduler = ObjectProperty() config = ObjectProperty() notifications_manager = ObjectProperty() rcs = ObjectProperty() top_bar = ObjectProperty() _cameras_status = StringProperty() def __init__(self, **kwargs): self._help_opened = False self._help_key_down = False self.timeout_event = None super(ScribeWidget, self).__init__(**kwargs) try: self.config = Scribe3Configuration() self.config.validate() self.init_pid_file() except CredentialsError as e: # if this is raised, we show the wizard msg = '[b]It looks like there is an issue with your credentials[/b].\n' \ '\n{}\nClick the button to begin the wizard.'.format(e.msg) popup = InfoPopup(title='Logged out', message=msg, auto_dismiss=False) popup.bind(on_submit=self.begin_wizard) popup.open() return except ScribeException as e: # if this is raised, the app can no longer continue msg = 'Scribe3 could not initialize\nbecause of the following error:' \ '\n\n[b]{}[/b].\n\nClick the button to close.'.format(str(e)) popup = InfoPopup(title='Initialization error', message=msg, auto_dismiss=False) app = App.get_running_app() popup.bind(on_submit=app.stop) popup.open() return #self.config.subscribe(get_adapter('config')) md_version = {'tts_version': scribe_globals.BUILD_NUMBER} set_sc_metadata(md_version) screen_manager = self.ids._screen_manager screen_manager.bind(current=self._on_current_screen) self._init_top_bar(self.config) self.help = Help() self.help.fbind('on_open', self._on_help_open) self.help.fbind('on_dismiss', self._on_help_dismiss) self.notifications_manager = NotificationManager() Clock.schedule_once(self._postponed_init, -1) def _postponed_init(self, *args): self.cameras = Cameras() self.cameras.fbind('camera_ports', self.on_camera_ports) self.on_camera_ports(self.cameras, self.cameras.camera_ports) self.cameras.initialize() self.left_queue, \ self.right_queue, \ self.foldout_queue = _setup_camera_threads(self.cameras) manager = self.ids._screen_manager if manager.current == 'upload_screen': self.ids._upload_screen.use_tooltips = True self.help.target_widget = manager md_screen = self.ids._book_metadata_screen md_screen.camera_system = self.cameras md_screen.bind(on_done=self._on_book_metadata_screen_done, on_cancel=self._on_book_metadata_screen_cancel) md_screen.backend.bind(on_new_book_created=self._on_new_book_created) capture_screen = self.ids._capture_screen capture_screen.bind(on_book_reset=self._on_book_reset, on_start_new_book=self._on_start_new_book, on_edit_metadata=self._on_book_edit_metadata) Window.bind(on_key_down=self._on_key_down) Window.bind(on_key_up=self._on_key_up) self.task_scheduler.start() self.setup_rcs() extensions_screen = self.ids['_extensions_screen'] extensions_screen.task_scheduler = self.task_scheduler extensions_screen.library = self.books_db user_switch_screen = self.ids['_user_switch_screen'] user_switch_screen.task_scheduler = self.task_scheduler if not self.config.is_true('stats_disabled'): self.setup_stats_backend() self.setup_notification_center() if self.config.is_true('enable_c2'): self.setup_command_and_control() if self.config.is_true('enable_webapi'): self.setup_webapi() logout_timeout = self.config.get_numeric_or_none('logout_timeout') if logout_timeout: Logger.info('Detected logout timeout of {} seconds; initializing scheduler'.format(logout_timeout)) self.timeout_event = Clock.schedule_interval(self.do_timeout, logout_timeout) def refresh_timeout(self): timeout = self.config.get_numeric_or_none('logout_timeout') if self.timeout_event: self.timeout_event.cancel() if timeout: self.timeout_event = Clock.schedule_interval(self.do_timeout, timeout) def do_timeout(self, dt): current = self.get_current_screen() if self.config.is_true('do_not_logout_from_capture_screens'): IGNORE_LOGOUT_SCREENS.extend(['capture_screen', 'reshoot_screen']) if current not in IGNORE_LOGOUT_SCREENS: self.show_user_switch_screen() def setup_rcs(self): self.rcs = RCS() self.rcs.attach_scheduler(self.task_scheduler) self.rcs.schedule_sync() def setup_stats_backend(self): from ia_scribe.breadcrumbs.api import get_adapter, log_event, process_stats self.books_db.subscribe(get_adapter('library')) self.ids._screen_manager.bind(current=get_adapter('screen_manager')) self.top_bar.bind(on_option_select=get_adapter('top_bar')) self.cameras.fbind('camera_ports', get_adapter('cameras')) log_event('app', 'started', None, None) stats_processor = GenericFunctionTask( name='Stats cruncher', function=process_stats, periodic=False ) self.task_scheduler.schedule(stats_processor) def setup_notification_center(self): self.books_db.subscribe(notifications_book_adapter) self.books_db.subscribe(notifications_book_error_adapter, topic='errors') self.notifications_manager.bind(on_notification_added=self.top_bar.highlight_notification) interval = self.config.get_numeric_or_none('notification_cleanup_interval') if interval: task = NotificationsCleanerTask(periodic=True, interval=interval) self.task_scheduler.schedule(task) def setup_command_and_control(self): # fix for pyinstaller packages app to avoid ReactorAlreadyInstalledError import sys if 'twisted.internet.reactor' in sys.modules: del sys.modules['twisted.internet.reactor'] # install twisted reactor install_twisted_reactor() from ia_scribe.ia_services import command_and_control self.c2 = command_and_control.S3C2() # Attach settings screen widget to c2 self.ids._screen_manager.get_screen('settings_screen').screens['c2'].ids['c2_widget'].attach(self.c2) if not command_and_control.registration_ok: self.notifications_manager.add_notification(title='C2 error', message='Command and control service failed to load. ' 'The error was: [b]{}[/b] | Contact an admin to resolve.'.format( command_and_control.registration_error), is_sticky=True, is_error=True, notification_type='system', ) return self.c2.callback = partial(self.notifications_manager.add_notification, title='Command and Control', notification_type='system') self.c2.connect_to_server() def setup_webapi(self): from ia_scribe.services.webserver import start_webserver start_webserver() def toggle_worker(self): self.ids._upload_widget.toggle_worker(self.ids.button_toggle_worker, self.ids.button_task_manager) def show_task_status(self): root = StakhanovWidget() root.new_manager.attach_scheduler(self.task_scheduler) popup = Popup( title='Tasks list', content=root, size_hint=(None, None), size=('1040dp', '800dp') ) popup.bind(on_dismiss=root.release_refs) popup.open() def show_notification_center(self): root = NotificationCenterWidget() root.attach(self.notifications_manager, self.ids._upload_widget._book_handler) popup = Popup( title='Notifications', content=root, size_hint=(None, None), size=('1080dp', '880dp') ) popup.bind(on_dismiss=root.detach) popup.open() self.top_bar.remove_highlight_notification() def show_help_center(self): manager = self.ids._screen_manager manager.transition.direction = 'left' manager.current = 'help_center_screen' def show_cameras_status(self): self.show_camera_screen() def init_pid_file(self): Logger.info('PID Init: Begin') config_dir = scribe_globals.CONFIG_DIR if not os.path.exists(config_dir): os.makedirs(config_dir) if not os.access(config_dir, os.W_OK | os.X_OK): raise ScribeException('Config dir "{}" not writable' .format(config_dir)) # Check to see if another copy of the app is running # and if needed, remove stale pidfiles path = os.path.join(config_dir, 'scribe_pid') Logger.info('PID Init: Looking for pidfile at {}'.format(path)) if os.path.exists(path): f = open(path) old_pid = f.read().strip() f.close() pid_dir = os.path.join('/proc', old_pid) if os.path.exists(pid_dir): Logger.error('There seems to be a pid file at {}. Try ' 'removing it and relaunching ' 'the application.'.format(str(pid_dir))) raise ScribeException('Another copy of the Scribe application ' 'is running!') else: os.unlink(path) pid = os.getpid() f = open(path, 'w') f.write(str(pid)) f.close() Logger.info('PID Init: End') def begin_wizard(self, *args, **kwargs): if len(args) >1: try: args[0].dismiss() except: pass self.ids._screen_manager.transition.direction = 'right' self.ids._screen_manager.current = 'wizard_screen' def back_to_library(self): '''Set the screen_manager go back to the library UI view.''' manager = self.ids._screen_manager if manager.current != 'upload_screen': manager.transition.direction = 'right' manager.current = 'upload_screen' def _init_top_bar(self, config): self.top_bar = top_bar = TopBar() meta = get_sc_metadata() top_bar.username = meta.get('operator', None) or '' top_bar.machine_id = meta.get('scanner', None) or '' top_bar.bind(on_option_select=self._on_top_bar_option_select) self.config.subscribe(self._on_config_change) # TODO: Better way to check if user is logged in if 's3' in self.config: self.add_widget(top_bar, len(self.children)) def _on_config_change(self, event_type, payload): key, value = payload # though we read this value from metadata.xml, we use the shadow # value in configuration to get a change notification if event_type=='key_set' and key == 'operator': meta = get_sc_metadata() self.top_bar.username = meta.get('operator', None) or '' def _on_top_bar_option_select(self, top_bar, option): # TODO: Don't hardcode top bar options if option == 'home': self.back_to_library() elif option == 'change_user': self.show_user_switch_screen() elif option == 'settings': self.show_settings_screen() elif option == 'logout': self.show_logout_screen() elif option == 'calibrate_cameras': self.show_calibration_screen(target_screen='upload_screen') elif option == 'help': link = 'https://wiki.archive.org/twiki/bin/view/BooksDigitization/WebHome' webbrowser.open(link) elif option == 'visit': link = 'https://archive.org' webbrowser.open(link) elif option == 'extensions': self.show_extensions_screen() elif option == 'cli': self.show_cli_popup() elif option == 'stats': self.show_stats_screen() elif option == 'notification_center': self.show_notification_center() elif option == 'help_center': self.show_help_center() elif option == 'exit': self.exit_app() def _on_current_screen(self, screen_manager, current): self.top_bar.use_back_button = current not in ['upload_screen', 'user_switch_screen'] def _on_book_metadata_screen_done(self, md_screen): capture_screen = self.ids._capture_screen capture_screen.target_extra = md_screen.target_extra capture_screen.book_dir = md_screen.backend.book_path manager = self.ids._screen_manager manager.transition.direction = 'up' manager.current = capture_screen.name def _on_book_metadata_screen_cancel(self, md_screen): manager = self.ids._screen_manager if md_screen.target_extra: manager.transition.direction = 'up' self.ids._capture_screen.target_extra = md_screen.target_extra manager.current = self.ids._capture_screen.name else: manager.transition.direction = 'left' manager.current = self.ids._upload_screen.name def _on_book_reset(self, capture_screen): self.back_to_library() def _on_start_new_book(self, capture_screen): md_screen = self.ids._book_metadata_screen md_screen.backend.create_new_book() manager = self.ids._screen_manager manager.transition.direction = 'left' manager.current = md_screen.name def _on_book_edit_metadata(self, capture_screen): md_screen = self.ids._book_metadata_screen md_screen.target_extra = capture_screen.create_state() md_screen.backend.book_path = capture_screen.book_dir manager = self.ids._screen_manager manager.transition.direction = 'down' manager.current = md_screen.name def _on_new_book_created(self, md_screen_backend, book): pass def _on_key_down(self, window, keycode, scancode, codepoint=None, modifiers=None, **kwargs): if scancode == 58 and not self._help_key_down: self._help_key_down = True self.help.dismiss() if self._help_opened else self.help.open() return True def _on_key_up(self, window, keycode, scancode, codepoint=None, modifiers=None, **kwargs): if scancode == 58 and self._help_key_down: self._help_key_down = False return True def _on_help_open(self, help): manager = self.ids._screen_manager manager.current_screen.disabled = True self._help_key_down = False self._help_opened = True def _on_help_dismiss(self, help): manager = self.ids._screen_manager manager.current_screen.disabled = False self._help_key_down = False self._help_opened = False def on_camera_ports(self, cameras, camera_ports): num_cams = cameras.get_num_cameras() temp = ['| {} camera(s)'.format(num_cams)] if num_cams != 0: for side, metadata in cameras.get_active_cameras().items(): temp.append('{}: {}'.format(side, metadata['model'])) self._cameras_status = ' | '.join(temp) def show_user_switch_screen(self): manager = self.ids._screen_manager manager.transition.direction = 'left' manager.current = 'user_switch_screen' def show_upload_screen(self): manager = self.ids._screen_manager manager.transition.direction = 'left' manager.current = 'upload_screen' def show_metadata_screen(self): manager = self.ids._screen_manager manager.get_screen('settings_screen').setup_manager(manager) manager.get_screen('settings_screen').go_screen('metadata') manager.transition.direction = 'left' manager.current = 'settings_screen' def show_camera_screen(self): manager = self.ids._screen_manager manager.get_screen('settings_screen').setup_manager(manager) manager.get_screen('settings_screen').go_screen('camera') manager.transition.direction = 'left' manager.current = 'settings_screen' def show_settings_screen(self): manager = self.ids._screen_manager manager.get_screen('settings_screen').setup_manager(manager) manager.transition.direction = 'left' manager.current = 'settings_screen' def show_logout_screen(self): # Only using logout screen method `archive_logout` # which logouts the user and restarts the app manager = self.ids._screen_manager login_screen = manager.get_screen('login_screen') login_screen.archive_logout() def show_calibration_screen(self, target_screen='capture_screen', extra=None): calibration_screen = self.ids['_calibration_screen'] calibration_screen.target_screen = target_screen calibration_screen.target_extra = extra manager = self.ids._screen_manager manager.transition.direction = 'left' manager.current = 'calibration_screen' def show_extensions_screen(self): manager = self.ids._screen_manager manager.transition.direction = 'left' manager.current = 'extensions_screen' def show_stats_screen(self): manager = self.ids._screen_manager manager.transition.direction = 'left' manager.current = 'stats_screen' def show_cli_popup(self): app = App.get_running_app() root = app.get_popup(CLIWidgetPopup, size_hint=(.90, .90)) root.screen_manager = self.ids._screen_manager root.open() def get_current_screen(self): return self.ids._screen_manager.current def get_current_user(self): return self.top_bar.username def exit_app(self): app = App.get_running_app() app.stop()