def dump(scanarium, file): local_file = None if file == 'dynamic/command-log.json': local_file = os.path.join(scanarium.get_dynamic_directory(), 'command-log.json') elif file == 'dynamic/config.json': local_file = os.path.join(scanarium.get_dynamic_directory(), 'config.json') else: parts = file.split('/', 3) if parts[0:2] == ['dynamic', 'scenes'] and \ parts[3] in ['actors.json', 'actors-latest.json']: parts[2] = re.sub('[^a-zA-Z]', '-', parts[2]) local_file = os.path.join(scanarium.get_dynamic_directory(), *parts[1:]) if local_file is None: raise ScanariumError('SE_DYNAMIC_CONFIG_NOT_AVAILABLE', 'Dynamic config "{file}" is not available', {'file': file}) try: with open(local_file, 'r') as f: return json.load(f) except Exception: raise ScanariumError('SE_DYNAMIC_CONFIG_UNREADABLE', 'Dynamic config "{file}" cannot be read', {'file': file})
def update_password(scanarium, old_password, new_password): pod = os.environ['INSTANCE_NAME'] username = os.environ['REMOTE_USER'] stdin = f'{pod}\n{username}\n{old_password}\n{new_password}\n' cmd = scanarium.get_config('cgi:update-password', 'delegate') try: scanarium.run([cmd], input=stdin) except ScanariumError as e: if e.code == 'SE_RETURN_VALUE': stderr_lines = e.private_parameters['stderr'].split('\n') if len(stderr_lines) >= 2 \ and stderr_lines[-2].startswith('RuntimeError: ') \ and stderr_lines[-1] == '': msg = stderr_lines[-2][14:] raise ScanariumError('SE_PWD_UPDATE_BACKEND_MSG', msg) raise ScanariumError('SE_PWD_UPDATE_RETURN_VALUE', 'Backend failed') if e.code == 'SE_TIMEOUT': raise ScanariumError('SE_PWD_UPDATE_TIMEOUT', 'Update process timed out') raise e return {}
def scan_image_no_outer_logging(scanarium): image = scanarium.get_image() qr_rect = None data = None iteration = 1 minimal_width = scanarium.get_config('scan', 'min_raw_width_trip', kind='int') fine_grained_errors = scanarium.get_config('debug', 'fine_grained_errors', kind='boolean') while qr_rect is None: try: (qr_rect, data) = scanarium.extract_qr(image) except ScanariumError as e: if e.code == 'SE_SCAN_NO_QR_CODE': # QR code could not get scanned. Probably, because the image # is too skew. We try to rectify on the images biggest rect # (probably the paper sheet). This should undistort the QR # code to be scanable in the next round. if iteration > 3: if fine_grained_errors: raise ScanariumError( 'SE_SCAN_IMAGE_TOO_MANY_ITERATIONS', 'Taken too many extraction tries from scanned ' 'image') else: raise e try: image = scanarium.rectify_to_biggest_rect(image) except ScanariumError: raise e if image.shape[1] < minimal_width: # The image that we're homing in on is really small. It's # unlikely to be a proper A4 image, but rather the camera # did not detect a proper sheet rect and we're homing in on # an (unrelated) small rectangular part of the image. So we # abort. if fine_grained_errors: raise ScanariumError( 'SE_SCAN_IMAGE_GREW_TOO_SMALL', 'Failed to identify sheet on scanned image') else: raise e else: raise e iteration += 1 return scanarium.process_image_with_qr_code(image, qr_rect, data)
def test_dump_simple_no_file(self): dumped = self.run_dump(payload='foo', exc_info=(None, ScanariumError( 'QUUX', 'QUUUX {xyz}', {'xyz': 'abc'})), command='bar', parameters=['baz']) entry = dumped[0] self.assertEqual(entry['command'], 'bar') self.assertEqual(entry['parameters'], ['baz']) self.assertEqual(entry['payload'], 'foo') self.assertEqual(entry['error_code'], 'QUUX') self.assertEqual(entry['error_message'], 'QUUUX abc') self.assertEqual(entry['error_template'], 'QUUUX {xyz}') self.assertEqual(entry['error_parameters'], {'xyz': 'abc'}) self.assertLenIs(dumped, 1)
def _bailout_mode(self, mode): mode = mode.strip() if mode == 'exit': os.kill(os.getpid(), signal.SIGKILL) elif mode.startswith('restart-service:'): service = re.sub('[^a-zA-Z0-9_-]', '-', mode[16:]) command = [ '/usr/bin/sudo', '--non-interactive', '/usr/sbin/service', service, 'restart' ] timeout = self.scanarium.get_config( 'service:continuous-scanning', 'bailout_service_restart_timeout', kind='int') self.scanarium.run(command, timeout=timeout) else: raise ScanariumError('SE_CONT_SCAN_UNKNOW_BAILOUT', 'Unknown bail out method')
def report_feedback(scanarium, message, email, lastFailedUpload, userAgent): target = scanarium.get_config('cgi:report-feedback', 'target') if target == 'stderr': print(message, file=sys.stderr) elif target == 'log': filename_base = scanarium.get_log_filename('feedback') scanarium.dump_text(filename_base + '.txt', message) if email: scanarium.dump_text(filename_base + '-email.txt', email) if lastFailedUpload: img_filename = filename_base + '-last-failed-upload' with open(img_filename, 'wb') as f: f.write(base64.standard_b64decode(lastFailedUpload)) format = scanarium.guess_image_format(img_filename) if format: os.rename(img_filename, f'{img_filename}.{format}') if userAgent: scanarium.dump_text(filename_base + '-user-agent.txt', userAgent) else: raise ScanariumError('SE_UNKNOWN_FEEDBACK_TARGET', 'Unknown feedback target "{feedback_target}"', {'feedback_target': target}) return True
def assert_directory(dir): if not os.path.isdir(dir): raise ScanariumError('E_NO_DIR', 'Is not a directory "{file_name}"', {'file_name': dir})
def filter_svg_tree(scanarium, tree, command, parameter, variant, localizer, command_label, parameter_label, decoration_version, href_adjustment=None): (localized_command, localized_parameter, localized_variant, localized_parameter_with_variant) = localize_command_parameter_variant( localizer, command, parameter, variant) localized_command_label = localizer.localize_parameter( 'command_label', command_label) localized_parameter_label = localizer.localize_parameter( 'parameter_label', parameter_label) template_parameters = { 'actor_name': localized_parameter, 'command_label': localized_command_label, 'command_name': localized_command, 'command_name_raw': command, 'parameter_label': localized_parameter_label, 'parameter_name': localized_parameter, 'parameter_with_variant_name': localized_parameter_with_variant, 'parameter_name_raw': parameter, 'scene_name': localized_command, 'variant_name': localized_variant, } def filter_text(text): if text is not None: text = localizer.localize(text, template_parameters) return text for element in tree.iter(): if element.tag == '{http://www.w3.org/2000/svg}g': if element.get('{http://www.inkscape.org/namespaces/inkscape}' 'groupmode') == 'layer': layer_name = extract_layer_name(element) style_enforcings = SVG_VARIANT_SETTINGS\ .get(variant, {})\ .get('layer-settings', {})\ .get(layer_name, {}) element.text = filter_text(element.text) element.tail = filter_text(element.tail) for key in element.keys(): value = filter_text(element.get(key)) if key == 'style' and value is not None: style = {} for setting in value.split(';'): setting = setting.strip() if setting: k, v = setting.split(':', 1) style[k.strip()] = v for k, v in style_enforcings.items(): style[k] = str(v) value = ';'.join(':'.join(i) for i in style.items()) if key == 'transform' and value is not None: if value.split('(', 1)[0] not in ['translate', 'rotate']: raise ScanariumError('E_SVG_TRANSFORM_SCALE', 'SVG uses unknown transformation') if key == '{http://www.w3.org/1999/xlink}href': if value and not value.startswith('/') and '://' not in value \ and href_adjustment: value = href_adjustment + '/' + value element.set(key, value) for qr_element in list(tree.iter("{http://www.w3.org/2000/svg}rect")): qr_pixel = qr_element.attrib.get('qr-pixel', None) if qr_pixel is not None: if qr_pixel == command_label: qr_data = f'{command}:{parameter}:d_{decoration_version}' expand_qr_pixel_to_qr_code(scanarium, qr_element, qr_data) else: # We want to remove this qr-pixel, as it does not match the # needed type. But as it might be linked to other elements, we # instead make it invisible to avoid messing with the layout. # # As setting `visibility` does not seem to do the trick (at # least in Inkscape 0.92.3), we instead set opacity. This works # reliably in Inkscape. qr_element.set('style', 'opacity:0')
def generate_pdf(scanarium, dir, file, force, metadata={}): dpi = 150 quality = 75 svg_source = os.path.join(dir, file) pdf_name = None metadata['dpi'] = dpi formats = ['pdf'] for format in ['png', 'jpg']: if scanarium.get_config('cgi:regenerate-static-content', f'generate_{format}', kind='boolean'): formats.append(format) for format in formats: target = os.path.join(dir, file.rsplit('.', 1)[0] + '.' + format) target_tmp = target + '.tmp.' + format # Appending format to make sure # `convert` and colleagues can figure out the expected file format if format in ['pdf', 'png']: source = svg_source if scanarium.file_needs_update(target, [source], force): inkscape_args = [ '--export-area-page', f'--export-dpi={dpi}', '--export-%s=%s' % (format, target_tmp), ] if format == 'png': inkscape_args.append('--export-background=white') inkscape_args.append(source) run_inkscape(scanarium, inkscape_args) if format == 'pdf': pdf_name = target else: source = os.path.join(dir, file.rsplit('.', 1)[0] + '.png') if not scanarium.get_config('cgi:regenerate-static-content', 'generate_png', kind='boolean'): raise ScanariumError( 'SE_REGENERATE_NO_SOURCE_FOR_TARGET', 'You need to enable ' '`cgi:regenerate-static-content.generate_png` to generate ' 'the target file {target_file}.', { 'source_file': source, 'target_file': target }) if scanarium.file_needs_update(target, [source], force): command = [ scanarium.get_config('programs', 'convert'), source, '-units', 'pixelsperinch', '-background', 'white', '-flatten', '-density', str(dpi), '-quality', str(quality), target_tmp ] scanarium.run(command) if scanarium.file_needs_update(target, [source], force): if scanarium.get_config('cgi:regenerate-static-content', 'embed_metadata', kind='boolean'): embed_metadata(scanarium, target_tmp, metadata) shutil.move(target_tmp, target) return pdf_name