def __establish_profile( self, profile_path: pathlib.Path ) -> typing.Tuple[typing.Optional[Profile.Profile], bool]: assert profile_path.is_absolute( ) # prevents tests from creating temporary files in test directory create_new_profile = not profile_path.exists() if create_new_profile: logging.getLogger("loader").info( f"Creating new profile {profile_path}") profile_json = json.dumps({ "version": FileStorageSystem.PROFILE_VERSION, "uuid": str(uuid.uuid4()) }) profile_path.write_text(profile_json, "utf-8") else: logging.getLogger("loader").info( f"Using existing profile {profile_path}") storage_system = FileStorageSystem.FilePersistentStorageSystem( profile_path) storage_system.load_properties() cache_path = profile_path.parent / pathlib.Path( profile_path.stem + " Cache").with_suffix(".nscache") logging.getLogger("loader").info(f"Using cache {cache_path}") storage_cache = Cache.DbStorageCache(cache_path) profile = Profile.Profile(storage_system=storage_system, storage_cache=storage_cache) profile.read_profile() return profile, create_new_profile
def __init__(self, file_path): file_path = pathlib.Path(file_path) if file_path.suffix == ".nsproj": r = Profile.IndexProjectReference() r.project_path = file_path else: r = Profile.FolderProjectReference() r.project_folder_path = file_path r.persistent_object_context = Persistence.PersistentObjectContext() r.load_project(None) #r.project._raw_properties["version"] = 3 r.project.read_project() r.project.read_project() self.project = r.project self._data_items_properties = [ di.properties for di in self.project.data_items ]
def start(self, skip_choose=False, fixed_workspace_dir=None): """ Start the application. Looks for workspace_location persistent string. If it doesn't find it, uses a default workspace location. Then checks to see if that workspace exists. If not and if skip_choose has not been set to True, asks the user for a workspace location. User may choose new folder or existing location. This works by putting up the dialog which will either call start again or exit. Creates workspace in location if it doesn't exist. Migrates database to latest version. Creates document model, resources path, etc. """ logging.getLogger("migration").setLevel(logging.INFO) if fixed_workspace_dir: workspace_dir = fixed_workspace_dir else: documents_dir = self.ui.get_document_location() workspace_dir = os.path.join(documents_dir, "Nion Swift Libraries") workspace_dir = self.ui.get_persistent_string("workspace_location", workspace_dir) welcome_message_enabled = fixed_workspace_dir is None profile, is_created = Profile.create_profile(pathlib.Path(workspace_dir), welcome_message_enabled, skip_choose) if not profile: self.choose_library() return True self.workspace_dir = workspace_dir DocumentModel.DocumentModel.computation_min_period = 0.2 DocumentModel.DocumentModel.computation_min_factor = 1.0 document_model = DocumentModel.DocumentModel(profile=profile) document_model.create_default_data_groups() document_model.start_dispatcher() # parse the hardware aliases file alias_path = os.path.join(self.workspace_dir, "aliases.ini") HardwareSource.parse_hardware_aliases_config_file(alias_path) # create the document controller document_controller = self.create_document_controller(document_model, "library") if self.__resources_path is not None: document_model.create_sample_images(self.__resources_path) workspace_history = self.ui.get_persistent_object("workspace_history", list()) if workspace_dir in workspace_history: workspace_history.remove(workspace_dir) workspace_history.insert(0, workspace_dir) self.ui.set_persistent_object("workspace_history", workspace_history) self.ui.set_persistent_string("workspace_location", workspace_dir) if welcome_message_enabled: logging.info("Welcome to Nion Swift.") if is_created and len(document_model.display_items) > 0: document_controller.selected_display_panel.set_display_panel_display_item(document_model.display_items[0]) document_controller.selected_display_panel.perform_action("set_fill_mode") return True
def add_clicked() -> None: assert self.__profile add_dir = self.ui.get_persistent_string("import_directory", "") file_paths, filter_str, directory = self.get_file_paths_dialog(_("Add Scripts"), add_dir, "Python Files (*.py)", "Python Files (*.py)") self.ui.set_persistent_string("import_directory", directory) items = list(self.scripts_list_widget.items) for file_path_str in file_paths: script_item = Profile.FileScriptItem(pathlib.Path(file_path_str)) self.__profile.append_script_item(script_item) script_list_item = ScriptListItem(file_path_str) script_list_item.script_item = script_item items.append(script_list_item) self.update_scripts_list(items)
def get_storage_system(workspace_dir): # This function is adapted from Swift's profile workspace_dir = pathlib.Path(workspace_dir) library_path = Profile._migrate_library(workspace_dir, do_logging=True) this_storage_version = DataItem.DataItem.storage_version auto_migrations = list() auto_migrations.append( Profile.AutoMigration( pathlib.Path(workspace_dir) / "Nion Swift Workspace.nslib", [pathlib.Path(workspace_dir) / "Nion Swift Data"])) auto_migrations.append( Profile.AutoMigration( pathlib.Path(workspace_dir) / "Nion Swift Workspace.nslib", [pathlib.Path(workspace_dir) / "Nion Swift Data 10"])) auto_migrations.append( Profile.AutoMigration( pathlib.Path(workspace_dir) / "Nion Swift Workspace.nslib", [pathlib.Path(workspace_dir) / "Nion Swift Data 11"])) # Attemp at being future proof if this_storage_version > 12: for storage_version in range(12, this_storage_version): auto_migrations.append( Profile.AutoMigration( pathlib.Path(workspace_dir) / f"Nion Swift Library {storage_version}.nslib", [ pathlib.Path(workspace_dir) / f"Nion Swift Data {storage_version}" ])) # NOTE: when adding an AutoMigration here, also add the corresponding file # copy in _migrate_library storage_system = FileStorageSystem.FileStorageSystem( library_path, [ pathlib.Path(workspace_dir) / f"Nion Swift Data {this_storage_version}" ], auto_migrations=auto_migrations) return storage_system
def create_profile(self, add_project: bool = True) -> Profile.Profile: if not self.__profile: library_properties = {"version": FileStorageSystem.PROFILE_VERSION} storage_system = self.__storage_system storage_system.set_library_properties(library_properties) profile = Profile.Profile(storage_system=storage_system, storage_cache=self.storage_cache) profile.storage_system = storage_system profile.profile_context = self if add_project: add_project_memory(profile, self.project_uuid) self.__profile = profile self.__items_exit.append(profile.close) return profile else: storage_system = self.__storage_system storage_system.load_properties() profile = Profile.Profile(storage_system=storage_system, storage_cache=self.storage_cache) profile.storage_system = storage_system profile.profile_context = self self.__items_exit.append(profile.close) return profile
def test_adding_same_project_raises_error_during_append(self): # create two data items in different projects. select the two items in the data panel # and create a computation from the two inputs. compute and make sure no errors occur. with create_memory_profile_context() as profile_context: profile = profile_context.create_profile() profile.add_project_memory() document_model = DocumentModel.DocumentModel(profile=profile) document_controller = DocumentController.DocumentController( self.app.ui, document_model, workspace_id="library") with contextlib.closing(document_controller): project_reference = Profile.MemoryProjectReference() project_reference.project_uuid = document_model.profile.projects[ 0].uuid with self.assertRaises(Exception): document_model.profile.append_project_reference( project_reference)
def add_folder_clicked() -> None: assert self.__profile add_dir = self.ui.get_persistent_string("import_directory", "") existing_directory, directory = self.ui.get_existing_directory_dialog(_("Add Scripts Folder"), add_dir) if existing_directory: folder_list_item = FolderListItem(existing_directory) folder_list_item.update_content_from_file_system(filter_pattern=self.script_filter_pattern) full_path = folder_list_item.full_path if full_path not in sys.path: sys.path.append(full_path) self.__new_path_entries.append(full_path) items = list(self.scripts_list_widget.items) script_item = Profile.FolderScriptItem(pathlib.Path(existing_directory)) self.__profile.append_script_item(script_item) folder_list_item.script_item = script_item items.append(folder_list_item) self.update_scripts_list(items) else: self.rebuild_scripts_list()
def create_memory_profile_context(): return Profile.MemoryProfileContext()
def start(self, *, profile_dir: pathlib.Path = None, profile: Profile.Profile = None) -> bool: """Start the application. Creates the profile object using profile_path parameter (for testing), the profile path constructed from the name stored in preferences, or a default profile path. Attaches recent projects if profile is freshly created. The recent projects will initially be disabled and will require the user to explicitly upgrade them. Creates the document model with the profile. Creates the document window (aka document controller) with the document model. """ logging.getLogger("migration").setLevel(logging.INFO) logging.getLogger("loader").setLevel(logging.INFO) # create or load the profile object. allow test to override profile. is_created = False if not profile: # determine the profile_path if profile_dir: profile_path = profile_dir / pathlib.Path( "Profile").with_suffix(".nsproj") else: data_dir = pathlib.Path(self.ui.get_data_location()) profile_name = pathlib.Path( self.ui.get_persistent_string("profile_name", "Profile")) profile_path = data_dir / profile_name.with_suffix(".nsproj") # create the profile profile, is_created = self.__establish_profile(profile_path) self.__profile = profile # test code to reset script items # self.__profile.script_items_updated = False # while self.__profile.script_items: # self.__profile.remove_script_item(self.__profile.script_items[-1]) # migrate the interactive scripts persistent object if not self.__profile.script_items_updated: items_str = self.ui.get_persistent_string("interactive_scripts_1") if items_str: for item_dict in json.loads(items_str): item_type = item_dict.get("__type__", None) if item_type == "FolderListItem": folder_path_str = item_dict.get("full_path", None) folder_path = pathlib.Path( folder_path_str) if folder_path_str else None if folder_path: profile.append_script_item( Profile.FolderScriptItem( pathlib.Path(folder_path))) elif item_type == "ScriptListItem" and item_dict.get( "indent_level", None) == 0: script_path_str = item_dict.get("full_path", None) script_path = pathlib.Path( script_path_str) if script_path_str else None if script_path: profile.append_script_item( Profile.FileScriptItem( pathlib.Path(script_path))) else: items_old = self.ui.get_persistent_object( "interactive_scripts_0", list()) for script_path_str in items_old: script_path = pathlib.Path( script_path_str) if script_path_str else None if script_path: profile.append_script_item( Profile.FileScriptItem(pathlib.Path(script_path))) self.__profile.script_items_updated = True # configure the document model object. DocumentModel.DocumentModel.computation_min_period = 0.1 DocumentModel.DocumentModel.computation_min_factor = 1.0 # if it was created, it probably means it is migrating from an old version. so add all recent projects. # they will initially be disabled and the user will have to explicitly upgrade them. if is_created: # present a dialog for showing progress while finding existing projects u = Declarative.DeclarativeUI() task_message = u.create_label( text=_("Looking for existing projects...")) progress = u.create_progress_bar( value="@binding(progress_value_model.value)", width=300 - 24) progress_message = u.create_label( text="@binding(message_str_model.value)") main_column = u.create_column(task_message, progress, progress_message, spacing=8, width=300) window = u.create_window(main_column, title=_("Locating Existing Projects"), margin=12, window_style="tool") # handler for the progress window. defines two value models: progress, an int 0-100; and message, a string. class FindExistingProjectsWindowHandler(Declarative.WindowHandler): def __init__(self, *, completion_fn: typing.Optional[typing.Callable[ [], None]] = None): super().__init__(completion_fn=completion_fn) self.progress_value_model = Model.PropertyModel(0) self.message_str_model = Model.PropertyModel(str()) # construct the window handler and run it. when the dialog closes, it will continue by # calling open default project. window_handler = FindExistingProjectsWindowHandler( completion_fn=functools.partial(self.__open_default_project, profile_dir, is_created)) window_handler.run(window, app=self) # define an async routine that will perform the finding of the existing projects. # this is just a loop that yields via asyncio.sleep periodically. the loop loads # the projects and updates the progress and message value models in the dialog. # when finished, it asks the window to close on its next periodic call. it is # necessary to close this way because the close request may close the event loop # in which we're executing. so queueing the close request avoids that. async def find_existing_projects(): recent_library_paths = self.get_recent_library_paths() for index, library_path in enumerate(recent_library_paths): window_handler.progress_value_model.value = 100 * index // len( recent_library_paths) window_handler.message_str_model.value = str( library_path.name) logging.getLogger("loader").info( f"Adding existing project {index + 1}/{len(recent_library_paths)} {library_path}" ) await asyncio.sleep(0) profile.add_project_folder(pathlib.Path(library_path), load=False) window_handler.progress_value_model.value = 100 window_handler.message_str_model.value = _("Finished") await asyncio.sleep(1) window_handler.window.queue_request_close() # launch the find existing projects task asynchronously. window_handler.window.event_loop.create_task( find_existing_projects()) return True else: # continue with opening the default project return self.__open_default_project(profile_dir, is_created)