def get_img_data(data_type, file_info, img_info, **kwargs): """ Get data from an image or FITS file """ if file_info['ext'] == 'fits': hdulist = get_file(file_info) data = hdulist[int(img_info['frame'])].data else: try: from PIL import Image except ImportError: raise ToyzJobError( "You must have PIL (Python Imaging Library) installed to " "open files of this type") img = get_file(file_info) data = np.array(img) if data_type == 'data': if 'scale' in kwargs: width = int(kwargs['width'] / 2 / img_info['viewer']['scale']) height = int(kwargs['height'] / 2 / img_info['viewer']['scale']) else: width = int(kwargs['width'] / 2) height = int(kwargs['height'] / 2) x0 = max(0, kwargs['x'] - width) y0 = max(0, kwargs['y'] - height) xf = min(data.shape[1], kwargs['x'] + width) yf = min(data.shape[0], kwargs['y'] + height) if 'scale' in kwargs: tile_data = { 'x0_idx': x0, 'y0_idx': y0, 'xf_idx': xf, 'yf_idx': yf } data = scale_data(file_info, img_info, tile_data, data) else: data = data[y0:yf, x0:xf] response = { 'id': 'data', 'min': float(data.min()), 'max': float(data.max()), 'mean': float(data.mean()), 'median': float(np.median(data)), 'std_dev': float(np.std(data)), 'data': data.tolist() } elif data_type == 'datapoint': if (kwargs['x'] < data.shape[1] and kwargs['y'] < data.shape[0] and kwargs['x'] >= 0 and kwargs['y'] >= 0): response = { 'id': 'datapoint', 'px_value': float(data[kwargs['y'], kwargs['x']]) } else: response = {'id': 'datapoint', 'px_value': 0} else: raise ToyzJobError( "Loading that data type has not been implemented yet") return response
def load_workspace(toyz_settings, tid, params): """ Load a workspace """ core.check4keys(params, ['work_id']) user_id = tid['user_id'] if 'user_id' in params and params['user_id'] != tid['user_id']: permissions = core.get_workspace_permissions(toyz_settings, tid, params) print('permissions', permissions) if permissions['view']: user_id = params['user_id'] else: raise ToyzJobError("You do not have permission to load {0}".format( params['work_id'])) workspaces = db_utils.get_param(toyz_settings.db, 'workspaces', user_id=user_id) if params['work_id'] not in workspaces: raise ToyzJobError("{0} not found in your workspaces".format( params['work_id'])) response = { 'id': 'workspace', 'work_id': params['work_id'], 'settings': workspaces[params['work_id']] } return response
def create_tile(file_info, img_info, tile_info): try: from PIL import Image except ImportError: raise ToyzJobError( "You must have PIL (Python Imaging Library) installed to " "open files of this type") if file_info['ext'] == 'fits': try: from matplotlib import cm as cmap from matplotlib.colors import Normalize, LinearSegmentedColormap except ImportError: raise ToyzJobError( "You must have matplotlib installed to load FITS images") hdulist = get_file(file_info) data = hdulist[int(img_info['frame'])].data # If no advanced resampling algorithm is used, scale the data as quickly as possible. # Otherwise crop the data. if file_info['resampling'] == 'NEAREST': data = scale_data(file_info, img_info, tile_info, data) else: data = data[tile_info['y0_idx']:tile_info['yf_idx'], tile_info['x0_idx']:tile_info['xf_idx']] # FITS images have a flipped y-axis from what browsers and other image formats expect if img_info['invert_y']: data = np.flipud(data) if img_info['invert_x']: data = np.fliplr(data) norm = Normalize(img_info['colormap']['px_min'], img_info['colormap']['px_max'], True) colormap_name = img_info['colormap']['name'] if img_info['colormap']['invert_color']: colormap_name = colormap_name + '_r' colormap = getattr(cmap, colormap_name) cm = cmap.ScalarMappable(norm, colormap) img = np.uint8(cm.to_rgba(data) * 255) img = Image.fromarray(img) if file_info['resampling'] != 'NEAREST': img = img.resize((tile_info['width'], tile_info['height']), getattr(Image, file_info['resampling'])) else: img = get_file(file_info) img = img.crop((tile_info['x0_idx'], tile_info['y0_idx'], tile_info['xf_idx'], tile_info['yf_idx'])) img = img.resize((tile_info['width'], tile_info['height']), getattr(Image, file_info['resampling'])) width, height = img.size if width > 0 and height > 0: path = os.path.dirname(tile_info['new_filepath']) core.create_paths([path]) img.save(tile_info['new_filepath'], format=img_formats[file_info['tile_format']]) else: return False, '' return True, tile_info
def create_paths(toyz_settings, tid, params): """ Creates a new path on the server (if it does not already exist). Parameters - toyz_settings ( :py:class:`toyz.utils.core.ToyzSettings`): Settings for the toyz application - tid (*string* ): Task ID of the client user running the task - params (*dict* ): Any parameters sent by the client (see *params* below) Params - path (*string* ): path to create on the server Response id: 'create_folder', status (*string* ): 'success', path (*string* ): path created on the server """ core.check4keys(params, ['path', 'new_folder']) path = os.path.join(params['path'], params['new_folder']) permissions = file_access.get_parent_permissions(toyz_settings.db, path, user_id=tid['user_id']) if 'w' not in permissions and 'x' not in permissions: raise ToyzJobError( "You do not have permission to create path {0}".format(path)) core.create_paths(path) response = { 'id': 'notification', 'msg': 'Created path'.format(path), 'func': 'create_dir' } return response
def get_toyz_module(toyz_settings, user_id, module): """ Get a toyz module from either installed modules or one in a users toyz paths. An uninstalled toy will take precedence if it is in the toy paths as opposed to installed modules. Parameters toyz_settings ( :py:class:`toyz.utils.core.ToyzSettings` ): - Settings for the application runnning the job (may be needed to load user info or check permissions) user_id ( *string* ): id of the current user module ( *string* ): name of the module to search for Returns toyz_module ( *module* ): python module if it exists, otherwise ``None``. Raises Raises a :py:class:`toyz.utils.errors.ToyzJobError` if the module is not found in the users approved modules """ toyz_module = get_user_toyz(toyz_settings, user_id, module) if toyz_module is not None: return toyz_module if toyz_module is None and check_user_modules(toyz_settings, user_id, module): return importlib.import_module(module) else: raise ToyzJobError(module + " not found in " + user_id + "'s approved modules")
def get_file_info(toyz_settings, tid, params): """ Get information about an image file """ import toyz.web.viewer as viewer core.check4keys(params, ['file_info', 'img_info']) if tid['user_id'] != 'admin': permissions = file_access.get_parent_permissions( toyz_settings.db, params['file_info']['filepath'], user_id=tid['user_id']) if permissions is None or 'r' not in permissions: raise ToyzJobError( 'You do not have permission to view the requested file.' 'Please contact your network administrator if you believe this is an error.' ) file_info = viewer.get_file_info(params['file_info']) img_info = file_info['images'][file_info['frame']] img_info.update(params['img_info']) # Get the tile map for the first image result = get_img_info(toyz_settings, tid, { 'file_info': file_info, 'img_info': img_info }) file_info['images'][file_info['frame']] = result['img_info'] response = { 'id': 'file info', 'file_info': file_info, 'new_tiles': result['new_tiles'] } return response
def get_tile_info(toyz_settings, tid, params): """ Get new tiles that need to be loaded """ import toyz.web.viewer as viewer core.check4keys(params, ['img_info', 'file_info']) if tid['user_id'] != 'admin': permissions = file_access.get_parent_permissions( toyz_settings.db, params['file_info']['filepath'], user_id=tid['user_id']) if 'r' not in permissions: raise ToyzJobError( 'You do not have permission to view the requested file.' 'Please contact your network administrator if you believe this is an error.' ) all_tiles, new_tiles = viewer.get_tile_info(params['file_info'], params['img_info']) #print('all tile:', all_tiles) response = { 'id': 'tile info', 'all_tiles': all_tiles, 'new_tiles': new_tiles } return response
def save_workspace(toyz_settings, tid, params): """ Save a workspace for later use """ core.check4keys(params, ['workspaces', 'overwrite']) workspaces = db_utils.get_param(toyz_settings.db, 'workspaces', user_id=tid['user_id']) work_id = params['workspaces'].keys()[0] if work_id in workspaces and params['overwrite'] is False: response = {'id': 'verify', 'func': 'save_workspace'} else: user_id = tid['user_id'] if 'user_id' in params and params['user_id'] != tid['user_id']: params['work_id'] = work_id permissions = core.get_workspace_permissions( toyz_settings, tid, params) if permissions['modify']: user_id = params['user_id'] else: raise ToyzJobError( "You do not have permission to save {0}".format( params['work_id'])) db_utils.update_param(toyz_settings.db, 'workspaces', workspaces=params['workspaces'], user_id=user_id) response = { 'id': 'notification', 'msg': 'Workspace saved successfully', 'func': 'save_workspace' } return response
def get_img_tile(toyz_settings, tid, params): """ Load a tile from a larger image and notify the client it has been created """ import toyz.web.viewer as viewer core.check4keys(params, ['img_info', 'file_info', 'tile_info']) if tid['user_id'] != 'admin': permissions = file_access.get_parent_permissions( toyz_settings.db, params['file_info']['filepath'], user_id=tid['user_id']) if 'r' not in permissions: raise ToyzJobError( 'You do not have permission to view the requested file.' 'Please contact your network administrator if you believe this is an error.' ) created, tile_info = viewer.create_tile(params['file_info'], params['img_info'], params['tile_info']) response = { 'id': 'tile created', 'success': created, 'tile_info': tile_info } return response
def change_pwd(toyz_settings, tid, params): """ Change a users password. Parameters - toyz_settings ( :py:class:`toyz.utils.core.ToyzSettings`): Settings for the toyz application - tid (*string* ): Task ID of the client user running the task - params (*dict* ): Any parameters sent by the client (see *params* below) Params - current_pwd (*string* ): Users current password. Must match the password on file or an exception is raised - new_pwd (*string* ): New password - confirm_pwd (*string* ): Confirmation of the the new password. If new_pwd and confirm_pwd do not match, an exception is raised Response - id: 'notification' - func: 'change_pwd' - msg: 'Password changed successfully' """ core.check4keys(params, ['current_pwd', 'new_pwd', 'confirm_pwd']) if core.check_pwd(toyz_settings, tid['user_id'], params['current_pwd']): if params['new_pwd'] == params['confirm_pwd']: pwd_hash = core.encrypt_pwd(toyz_settings, params['new_pwd']) else: raise ToyzJobError("New passwords did not match") else: raise ToyzJobError("Invalid user_id or password") db_utils.update_param(toyz_settings.db, 'pwd', user_id=tid['user_id'], pwd=pwd_hash) response = { 'id': 'notification', 'func': 'change_pwd', 'msg': 'Password changed successfully', } return response
def get_file_info(file_info): file_split = file_info['filepath'].split('.') file_info['filename'] = os.path.basename(file_split[0]) if 'fits' in file_split[1:]: file_info['ext'] = 'fits' else: file_info['ext'] = file_split[-1] if 'tile_width' not in file_info: file_info['tile_width'] = 400 if 'tile_height' not in file_info: file_info['tile_height'] = 200 if 'img_type' not in file_info: file_info['img_type'] == 'image' if file_info['ext'] == 'fits': file_info['file_type'] = 'img_array' hdulist = get_file(file_info) file_info['hdulist'] = [hdu.__class__.__name__ for hdu in hdulist] if 'images' not in file_info: if len(hdulist) > 1: file_info['images'] = OrderedDict( [[str(n), { 'frame': str(n) }] for n, hdu in enumerate(hdulist) if 'imagehdu' in hdu.__class__.__name__.lower()]) else: file_info['images'] = {'0': {'frame': '0'}} if len(file_info['images']) == 0: raise ToyzJobError( "FITS file does not contain any recognized image hdu's") else: file_info['file_type'] = 'img' file_info['images'] = {'0': {'frame': '0'}} file_defaults = { 'frame': next(iter(file_info['images'])), 'resampling': 'NEAREST', 'invert_x': False, 'invert_y': False, 'tile_format': 'png', 'colormap': { 'name': 'Spectral', 'color_scale': 'linear', 'invert_color': False, 'set_bounds': False } } for default in file_defaults: if default not in file_info: file_info[default] = file_defaults[default] return file_info
def import_fits(): try: import astropy.io.fits as pyfits except ImportError: try: import pyfits except ImportError: raise ToyzJobError( "You must have astropy or pyfits installed to view FITS images" ) return pyfits
def reset_pwd(toyz_settings, tid, params): """ Reset a users password """ user = core.get_user_type(params) if 'user_id' in params: user_id = params['user_id'] elif 'group_id' in params: user_id = group_id else: raise ToyzJobError( "Must specify a user_id or group_id to reset password") pwd_hash = core.encrypt_pwd(toyz_settings, user_id) db_utils.update_param(toyz_settings.db, 'pwd', pwd=pwd_hash, **user) response = { 'id': 'notification', 'func': 'reset_pwd', 'msg': 'Password reset successfully', } return response
def scale_data(file_info, img_info, tile_info, data): if img_info['scale'] == 1: data = data[tile_info['y0_idx']:tile_info['yf_idx'], tile_info['x0_idx']:tile_info['xf_idx']] else: try: import scipy.ndimage data = data[tile_info['y0_idx']:tile_info['yf_idx'], tile_info['x0_idx']:tile_info['xf_idx']] data = scipy.ndimage.zoom(data, img_info['scale'], order=0) except ImportError: if img_info['scale'] > 1: data = data[tile_info['y0_idx']:tile_info['yf_idx'], tile_info['x0_idx']:tile_info['xf_idx']] data = np.kron(data, np.ones((img_info['scale'], img_info['scale']))) #data = zoom(data, img_info['scale'], order=0) elif img_info['scale'] < 1 and img_info['scale'] > 0: tile_width = min( file_info['tile_width'], int((img_info['width'] - tile_info['x0_idx']) * img_info['scale']) - 1) tile_height = min( file_info['tile_height'], int((img_info['height'] - tile_info['y0_idx']) * img_info['scale']) - 1) xmax = min(img_info['width'] - 1, tile_info['xf_idx']) ymax = min(img_info['height'] - 1, tile_info['yf_idx']) xIdx = np.linspace(tile_info['x0_idx'], xmax, tile_width) yIdx = np.linspace(tile_info['y0_idx'], ymax, tile_height) xIdx = np.array(xIdx, np.int) yIdx = np.reshape(np.array(yIdx, np.int), (yIdx.size, 1)) data = data[yIdx, xIdx] else: raise ToyzJobError('Scale must be a positive number') return data
def get_file(file_info): """ If the image has already been loaded into memory, access it here. Otherwise, store the image for later use """ if session_vars.filepath == file_info['filepath']: img_file = session_vars.img_file else: print('loading', file_info['filepath']) if file_info['ext'] == 'fits': print('Detected fits image type') pyfits = import_fits() img_file = pyfits.open(file_info['filepath']) else: try: from PIL import Image except ImportError: raise ToyzJobError( "You must have PIL (Python Imaging Library) installed to " "open files of this type") img_file = Image.open(file_info['filepath']) session_vars.filepath = file_info['filepath'] session_vars.img_file = img_file return img_file
def get_img_info(toyz_settings, tid, params): """ Map a large image into a set of tiles that make up the larger image """ import toyz.web.viewer as viewer print('************************************************') core.check4keys(params, ['img_info', 'file_info']) if tid['user_id'] != 'admin': permissions = file_access.get_parent_permissions( toyz_settings.db, params['file_info']['filepath'], user_id=tid['user_id']) if 'r' not in permissions: raise ToyzJobError( 'You do not have permission to view the requested file.' 'Please contact your network administrator if you believe this is an error.' ) shortcuts = db_utils.get_param(toyz_settings.db, 'shortcuts', user_id=tid['user_id']) save_path = os.path.join(shortcuts['temp'], tid['session_id'], 'images') params['img_info']['save_path'] = save_path img_info = viewer.get_img_info(params['file_info'], params['img_info']) result = get_tile_info(toyz_settings, tid, { 'file_info': params['file_info'], 'img_info': img_info }) img_info['tiles'] = result['new_tiles'] response = { 'id': 'img info', 'img_info': img_info, 'new_tiles': result['new_tiles'] } print('************************************************') return response
def load_directory(toyz_settings, tid, params): """ Used by the file browser to load the folders and files in a given path. Parameters - toyz_settings ( :py:class:`toyz.utils.core.ToyzSettings`): Settings for the toyz application - tid (*string* ): Task ID of the client user running the task - params (*dict* ): Any parameters sent by the client (see *params* below) Params - path (*string* ): Path to search Response - id: 'directory' - path (*string* ): path passed to the function - shortcuts (*dict* ): Dictionary of ``shortcut_name: shortcut_path`` 's for the user - folders (*list* of strings): folders contained in the path - files (*list* of strings): files contained in the path - parent (*string* ): parent directory of current path """ core.check4keys(params, ['path']) show_hidden = False # If the path is contained in a set of dollar signs (for example `$images$`) then # search in the users shortcuts for the given path shortcuts = db_utils.get_param(toyz_settings.db, 'shortcuts', user_id=tid['user_id']) if params['path'][0] == '$' and params['path'][-1] == '$': shortcut = params['path'][1:-1] if shortcut not in shortcuts: raise ToyzJobError("Shortcut '{0}' not found for user {1}".format( shortcut, tid['user_id'])) params['path'] = shortcuts[shortcut] if not os.path.isdir(params['path']): parent = os.path.dirname(params['path']) if not os.path.isdir(parent): raise ToyzJobError("Path '{0}' not found".format(params['path'])) params['path'] = parent if 'show_hidden' in params and params['show_hidden']: show_hidden = True # Keep separate lists of the files and directories for the current path. # Only include the files and directories the user has permissions to view files = [] folders = [] groups = db_utils.get_param(toyz_settings.db, 'groups', user_id=tid['user_id']) if 'admin' in groups or tid['user_id'] == 'admin': admin = True else: admin = False for f in os.listdir(params['path']): if (f[0] != '.' or show_hidden): f_path = os.path.join(params['path'], f) if admin: permission = True else: permissions = file_access.get_parent_permissions( toyz_settings.db, f_path, user_id=tid['user_id']) if permissions is None: permissions = '' permission = 'f' in permissions if permission: if os.path.isfile(f_path): files.append(str(f)) elif os.path.isdir(f_path): folders.append(str(f)) else: print("no access to", f) files.sort(key=lambda v: v.lower()) folders.sort(key=lambda v: v.lower()) response = { 'id': 'directory', 'path': os.path.join(params['path'], ''), 'shortcuts': shortcuts.keys(), 'folders': folders, 'files': files, 'parent': os.path.abspath(os.path.join(params['path'], os.pardir)) } #print('path info:', response) return response
def save_user_info(toyz_settings, tid, params): """ Save a users info. If any admin settings are being changed, ensures that the user is in the admin group. Parameters - toyz_settings ( :py:class:`toyz.utils.core.ToyzSettings`): Settings for the toyz application - tid (*string* ): Task ID of the client user running the task - params (*dict* ): Any parameters sent by the client (see *params* below) Params can be any settings the user has permission to set on the server. Response - id: 'notification' - func: 'save_user_info' - msg: 'Settings saved for <user_id>' """ groups = db_utils.get_param(toyz_settings.db, 'groups', user_id=tid['user_id']) # check that user is in the admin group if 'paths' in params and tid[ 'user_id'] != 'admin' and 'admin' not in groups: raise ToyzJobError( "You must be in the admin group to modify path permissions") if (('modules' in params or 'toyz' in params or 'third_party' in params) and tid['user_id'] != 'admin' and 'admin' not in groups and 'modify_toyz' not in groups): raise ToyzJobError( "You must be an administrator or belong to the 'modify_toyz' " "group to modify a users toyz or module access") # Conditions is a dict that contains any conditional parameters from a gui # parameter list. We don't use any of those for this function, so we remove them if 'conditions' in params: del params['conditions'] user = core.get_user_type(params) #update_fields = dict(params) #if 'user_id' in params: # del update_fields['user_id'] #elif 'group_id' in params: # del update_fields['group_id'] for field in params: if field in db_utils.param_formats: field_dict = {field: params[field]} field_dict.update(user) db_utils.update_all_params(toyz_settings.db, field, **field_dict) if 'user_id' in user: msg = params['user_id'] else: msg = params['group_id'] response = { 'id': 'notification', 'func': 'save_user_info', 'msg': 'Settings saved for ' + msg } return response
def run_job(toyz_settings, pipe, job): """ Loads modules and runs a job (function) sent from a client. Any errors will be trapped and flagged as a :py:class:`toyz.utils.errors.ToyzError` and sent back to the client who initiated the job. All job functions will take exactly 3 parameters: **toyz_settings**, **tid**, **params**. The **tid** is the task id (*user*, *session*, and *request_id* ) information (see below), and **params** is a dictionary of parameters sent by the client. Each job is run as a new process, so any modules imported should be removed from memory when the job has completed. Parameters toyz_settings ( :py:class:`toyz.utils.core.ToyzSettings` ): - Settings for the application runnning the job (may be needed to load user info or check permissions) pipe: *multiprocessing.Pipe* - Communication with the parent process. - It may be useful to pass progress notifications to a client as a job is run. To send a notifaction the pipe requires a dictionary that is of the same form as the result (see Returns below), but usually with ``id='notification'``. job: *dict* - The job received from the user. Each job will contain the following keys: - ``id`` (*dict*): Unique values for the given job. These are the **user_id**, the id of the user loaded from a secure cookie; the **session_id**, a unique identifier for the websocket; and the **request_id**, a unique identifier for the current request, sent from the client - ``module`` (*str*): Name of the Python module that contains the function called by the client. In order for a module to work for a user, he/she must either have permissions set to view the module or belong to a group with permissions set to view the module (including *all_users*). - ``task`` (*str*): Name of the function called by the client - ``parameters`` (*dict*): Required and optional parameters passed to the function. Returns result: *dict* - The result is returned to the application running the job and is composed of two keys: an ``id``: the job id for the completed job, and a ``response`` that is sent to the client. - The response is either an empty dictionary or one that contains (at a minimum) the key **id**, which is used by the client to identify the type of response it is receiving. Including the key **request_completed** with a *True* value tells the client that the current request has finished and may be removed from the queue. Example A client might send the following job to the server: .. code-block:: javascript job = { id : { user_id : 'Iggy', session_id : '12', request_id : 305 }, module : 'toyz.web.tasks', task : 'load_directory', parameters : { path: '~/images' } } In this case, after receiving the job, this function will import the :py:mod:`toyz.web.tasks` module (if it has not been imported already) and run the function ``load_directory(toyz_settings, job['id'],job['parameters'])``. If there are any errors in loading the directory, a response of the form .. code-block:: python response = { 'id' : 'ERROR', 'error' : 'Error message here for unable to lead directory', 'traceback' : traceback.format_exec() } is sent. If the directory is loaded correctly a response of the form .. code-block:: python response={ 'id': 'directory', 'path': '~/images/proj1', 'shortcuts': ['user', 'temp', 'home'], 'folders': ['proj1', 'proj2'], 'files': [], 'parent': '~/images' } is sent to the client. """ # TODO: Eventually a job should be added to the jobs dictionary and removed after the response has been sent import traceback session_vars.toyz_settings = toyz_settings session_vars.pipe = pipe response = {} try: try: if job['module'].split('.')[-1] == 'tasks': toyz_module = get_toyz_module(toyz_settings, job['id']['user_id'], job['module']) else: raise ImportError() except ImportError: ToyzJobError(job['module'] + " not found in " + job['id']['user_id'] + "'s approved modules") task = getattr(toyz_module, job["task"]) response = task(toyz_settings, job['id'], job['parameters']) except ToyzJobError as error: response = { 'id': "ERROR", 'error': error.msg, 'traceback': traceback.format_exc() } except Exception as error: response = { 'id': "ERROR", 'error': "PYTHON ERROR:" + type(error).__name__ + str(error.args), 'traceback': traceback.format_exc() } print(traceback.format_exc()) if response != {}: response['request_id'] = job['id']['request_id'] #self.write_message(response) result = { 'id': job['id'], 'response': response, } #logging.info("sent message:%r",response['id']) return result
def get_img_info(file_info, img_info): if file_info['ext'] == 'fits': hdulist = get_file(file_info) data = hdulist[int(img_info['frame'])].data height, width = data.shape if ('colormap' not in img_info): if (file_info['colormap']['set_bounds']): px_min = file_info['px_min'] px_max = file_info['px_max'] else: px_min = float(data.min()) px_max = float(data.max()) img_info['colormap'] = file_info['colormap'] if not file_info['colormap']['set_bounds']: img_info['colormap']['px_min'] = float(data.min()) img_info['colormap']['px_max'] = float(data.max()) else: # For non-FITS formats, only a single large image is loaded, which try: from PIL import Image except ImportError: raise ToyzJobError( "You must have PIL (Python Imaging Library) installed to " "open files of this type") img = get_file(file_info) img_info['colormap'] = { 'name': 'none', 'px_min': 0, 'px_max': 255, 'invert_color': False } width, height = img.size img_info['width'] = width img_info['height'] = height if 'scale' not in img_info: if 'viewer' not in img_info or 'scale' not in img_info['viewer']: raise ToyzJobError( "You must either supply a scale or image viewer parameters") if img_info['viewer']['scale'] < 0: img_info['viewer'] = get_best_fit(width, height, img_info['viewer']) else: img_info['viewer'] = get_window(img_info['viewer']) else: img_info['viewer'] = get_window(img_info['viewer']) img_info['scale'] = img_info['viewer']['scale'] img_info['scaled_width'] = int(math.ceil(width * img_info['scale'])) img_info['scaled_height'] = int(math.ceil(height * img_info['scale'])) img_info['columns'] = int( math.ceil(img_info['scaled_width'] / file_info['tile_width'])) img_info['rows'] = int( math.ceil(img_info['scaled_height'] / file_info['tile_height'])) img_defaults = {'invert_x': False, 'invert_y': False, 'tiles': {}} for default in img_defaults: if default not in img_info: if default in file_info: img_info[default] = file_info[default] else: img_info[default] = img_defaults[default] #print('img_info:', img_info) return img_info