def save_data(profile_name, data, _compress=True): """Handle the safe saving of profiles. Instead of overwriting, it will save as a temprary file and attempt to rename. At any point in time there are two copies of the save. """ #This is to allow pre-compressed data to be sent in if _compress: data = prepare_file(data) paths = _get_paths(profile_name) if create_folder(paths['BackupFolder'], is_file=False): hide_file(paths['BackupFolder']) if create_folder(paths['TempFolder'], is_file=False): hide_file(paths['TempFolder']) with open(paths['Temp'], 'wb') as f: f.write(data) remove_file(paths['Backup']) rename_file(paths['Main'], paths['Backup']) if rename_file(paths['Temp'], paths['Main']): return True else: remove_file(paths['Temp']) return False
def save(self): """Save config with currently loaded values.""" extra_items = list(set(self._default_data.keys()) - set(self.order)) output = [] for group in self.order + extra_items: variables = self._default_data[group] if output: output.append('') output.append('[{}]'.format(group)) if '__note__' in variables: for note in variables.pop('__note__'): output.append('// {}'.format(note)) for variable in sorted(variables.keys()): if variable.startswith('_'): continue defaults = variables[variable] try: value = self.data[group][variable] except KeyError: value = defaults[0] output.append('{} = {}'.format(variable, value)) try: if isinstance(defaults[-1], str) and defaults[-1]: output[-1] += ' // {}'.format(defaults[-1]) except IndexError: pass try: with open(self.file_name, 'w') as f: f.write('\n'.join(output)) except IOError: create_folder(self.file_name) with open(self.file_name, 'w') as f: f.write('\n'.join(output))
def speed(self, last_session=False, file_name=None): """Render speed track image.""" track_data = self.data.get_speed() if track_data is None: Message('No tracking data found.') return None top_resolution, (min_value, max_value), tracks = track_data output_resolution, upscale_resolution = calculate_resolution( tracks.keys(), top_resolution) upscaled_arrays = upscale_arrays_to_resolution(tracks, upscale_resolution) colour_range = self._get_colour_range(min_value, max_value, 'GenerateSpeed') image_output = arrays_to_colour(colour_range, upscaled_arrays) image_output = image_output.resize(output_resolution, Image.ANTIALIAS) if file_name is None: file_name = self.name.generate('Speed', reload=True) if self.save: create_folder(file_name) Message('Saving image to "{}"...'.format(file_name)) image_output.save(file_name) Message('Finished saving.')
def save(self, file_name=CONFIG_PATH): """Save the config to a file.""" output = self._build_for_file() create_folder(file_name) with open(file_name, 'w') as f: f.write(output) return self
def keyboard(self, image_name): _print('Generating CSV from keyboard...') result = ['Key,Count,Time'] for key in self.data['Keys']['All']['Pressed']: result.append('{},{},{}'.format( key, self.data['Keys']['All']['Pressed'][key], self.data['Keys']['All']['Held'][key])) file_name = image_name.generate('csv-keyboard', reload=True) create_folder(file_name) with open(file_name, 'w') as f: f.write('\n'.join(result))
def _generate_end(self, image_name, image_output, resize=True): resolution = (CONFIG['GenerateImages']['OutputResolutionX'], CONFIG['GenerateImages']['OutputResolutionY']) if resize: image_output = image_output.resize(resolution, Image.ANTIALIAS) if self.save: create_folder(image_name) _print('Saving image to "{}"...'.format(image_name)) image_output.save(image_name) _print('Finished saving.') return image_output
def tracks(self, image_name): _print('Generating CSV from tracks...') for resolution in self.data['Maps']['Tracks']: CONFIG['GenerateImages']['_TempResolutionX'], CONFIG[ 'GenerateImages']['_TempResolutionY'] = resolution result = self._generate(resolution, self.data['Maps']['Tracks'][resolution]) if result is not None: file_name = image_name.generate('csv-tracks', reload=True) create_folder(file_name) with open(file_name, 'w') as f: f.write(result)
def keyboard(self, last_session=False, file_name=None): """Render keyboard image.""" kb = DrawKeyboard(self.profile, self.data, last_session=last_session) image_output = kb.draw_image() if file_name is None: file_name = self.name.generate('Keyboard', reload=True) if self.save: create_folder(file_name) Message('Saving image to "{}"...'.format(file_name)) image_output.save(file_name) Message('Finished saving.')
def clicks(self, last_session=False, file_name=None, _double_click=False): """Render heatmap of clicks.""" top_resolution, (min_value, max_value), clicks = self.data.get_clicks( session=last_session, double_click=_double_click) output_resolution, upscale_resolution = calculate_resolution( clicks.keys(), top_resolution) lmb = CONFIG['GenerateHeatmap']['_MouseButtonLeft'] mmb = CONFIG['GenerateHeatmap']['_MouseButtonMiddle'] rmb = CONFIG['GenerateHeatmap']['_MouseButtonRight'] skip = [] if lmb or mmb or rmb: if not lmb: skip.append(0) if not mmb: skip.append(1) if not rmb: skip.append(2) upscaled_arrays = upscale_arrays_to_resolution(clicks, upscale_resolution, skip=skip) (min_value, max_value), heatmap = arrays_to_heatmap( upscaled_arrays, gaussian_size=gaussian_size(upscale_resolution[0], upscale_resolution[1]), clip=1 - CONFIG['Advanced']['HeatmapRangeClipping']) colour_range = self._get_colour_range(min_value, max_value, 'GenerateHeatmap') image_output = Image.fromarray(colour_range.convert_to_rgb(heatmap)) image_output = image_output.resize(output_resolution, Image.ANTIALIAS) if file_name is None: file_name = self.name.generate('Clicks', reload=True) if self.save: create_folder(file_name) Message('Saving image to "{}"...'.format(file_name)) image_output.save(file_name) Message('Finished saving.')
def clicks(self, image_name): mouse_buttons = ['LMB', 'MMB', 'RMB'] _print('Generating CSV from clicks...') for i, mouse_button in enumerate(self.data['Maps']['Click']['Single']): CONFIG['GenerateHeatmap']['_MouseButtonLeft'] = i == 0 CONFIG['GenerateHeatmap']['_MouseButtonMiddle'] = i == 1 CONFIG['GenerateHeatmap']['_MouseButtonRight'] = i == 2 for resolution, array in get_items( self.data['Maps']['Click']['Single'][mouse_button]): CONFIG['GenerateImages']['_TempResolutionX'], CONFIG[ 'GenerateImages']['_TempResolutionY'] = resolution result = self._generate(resolution, array) if result is not None: file_name = image_name.generate('csv-clicks', reload=True) create_folder(file_name) with open(file_name, 'w') as f: f.write(result)
def save_program(program_name, data, compress=True): if compress: compressed_data = prepare_file(data) else: compressed_data = data paths = _get_paths(program_name) if create_folder(paths['BackupFolder']): hide_file(paths['BackupFolder']) if create_folder(paths['TempFolder']): hide_file(paths['TempFolder']) with open(paths['Temp'], 'wb') as f: f.write(compressed_data) remove_file(paths['Backup']) rename_file(paths['Main'], paths['Backup']) if rename_file(paths['Temp'], paths['Main']): return True else: remove_file(paths['Temp']) return False
def load_data(profile_name=None, _reset_sessions=True, _update_metadata=True, _create_new=True): """Read a profile (or create new one) and run it through the update. Use LoadData class instead of this. """ paths = _get_paths(profile_name) new_file = False #Load the main file try: with CustomOpen(paths['Main'], 'rb') as f: loaded_data = decode_file(f, legacy=f.zip is None) #Load backup if file is corrupted except (zlib.error, ValueError): try: with CustomOpen(paths['Backup'], 'rb') as f: loaded_data = decode_file(f, legacy=f.zip is None) except (IOError, zlib.error, ValueError): new_file = True #Move corrupt file into a folder instead of just silently delete if create_folder(paths['CorruptedFolder'], is_file=False): hide_file(paths['CorruptedFolder']) rename_file(paths['Main'], '{}.{}'.format(paths['Corrupted'], int(time.time()))) #Don't load backup if file has been deleted except IOError: new_file = True #Create empty data if new_file: if _create_new: loaded_data = {} else: return None return upgrade_version(loaded_data, reset_sessions=_reset_sessions, update_metadata=_update_metadata)