From 9eb8416422330237b4c8c5b38f485190adc2c846 Mon Sep 17 00:00:00 2001
From: Samuel Jones <samjones714@gmail.com>
Date: Tue, 4 Dec 2018 16:05:12 +0000
Subject: [PATCH] Re #24049 Interrim commit

---
 .../MantidAPI/AnalysisDataServiceObserver.h   | 47 +++++++++---
 .../API/src/AnalysisDataServiceObserver.cpp   | 71 ++++++++++++++++++-
 .../api/AnalysisDataServiceObserverAdapter.h  | 43 +++++++++++
 .../PythonInterface/mantid/api/CMakeLists.txt |  2 +
 .../Exports/AnalysisDataServiceObserver.cpp   | 18 ++---
 .../AnalysisDataServiceObserverAdapter.cpp    | 29 ++++++++
 .../workbench/workbench/app/mainwindow.py     |  3 +-
 qt/python/CMakeLists.txt                      |  5 ++
 qt/python/mantidqt/project/project.py         | 47 +++++++-----
 qt/python/mantidqt/project/projectloader.py   | 16 +++--
 qt/python/mantidqt/project/projectsaver.py    | 19 +++--
 .../project/test/test_projectloader.py        | 15 ++--
 .../project/test/test_projectsaver.py         | 59 ++++++++-------
 .../project/test/test_workspaceloader.py      | 24 +++----
 .../project/test/test_workspacesaver.py       | 28 ++++----
 qt/python/mantidqt/project/workspaceloader.py | 13 +++-
 qt/python/mantidqt/project/workspacesaver.py  |  4 +-
 17 files changed, 317 insertions(+), 126 deletions(-)
 create mode 100644 Framework/PythonInterface/inc/MantidPythonInterface/api/AnalysisDataServiceObserverAdapter.h
 create mode 100644 Framework/PythonInterface/mantid/api/src/Exports/AnalysisDataServiceObserverAdapter.cpp

diff --git a/Framework/API/inc/MantidAPI/AnalysisDataServiceObserver.h b/Framework/API/inc/MantidAPI/AnalysisDataServiceObserver.h
index d7bdfb8bba5..77dc2630bef 100644
--- a/Framework/API/inc/MantidAPI/AnalysisDataServiceObserver.h
+++ b/Framework/API/inc/MantidAPI/AnalysisDataServiceObserver.h
@@ -25,22 +25,51 @@ public:
 
   void observeAll(bool turnOn = true);
   void observeAdd(bool turnOn = true);
-  
+  void observeReplace(bool turnOn = true);
+  void observeDelete(bool turnOn = true);
+
   virtual void anyChangeHandle() {}
 
 protected:
-  virtual void addHandle(const std::string &wsName,
-                         const Workspace_sptr ws);
+  virtual void addHandle(const std::string &wsName, const Workspace_sptr ws);
+  virtual void replaceHandle(const std::string &wsName,
+                             const Workspace_sptr ws);
+  virtual void deleteHandle(const std::string &wsName, const Workspace_sptr ws);
+
 private:
-  bool m_observingAdd;
+  bool m_observingAdd, m_observingReplace, m_observingDelete;
+
+  void _addHandle(
+      const Poco::AutoPtr<AnalysisDataServiceImpl::AddNotification> &pNf);
+  void _replaceHandle(
+      const Poco::AutoPtr<AnalysisDataServiceImpl::AfterReplaceNotification>
+          &pNf);
+  void _deleteHandle(
+      const Poco::AutoPtr<AnalysisDataServiceImpl::PostDeleteNotification>
+          &pNf);
 
-  void _addHandle(const Poco::AutoPtr<AnalysisDataServiceImpl::AddNotification> &pNf){
-    this->anyChangeHandle();
-    this->addHandle(pNf->objectName(), pNf->object());
-  }
   /// Poco::NObserver for AddNotification.
-  Poco::NObserver<AnalysisDataServiceObserver, AnalysisDataServiceImpl::AddNotification>
+  Poco::NObserver<AnalysisDataServiceObserver,
+                  AnalysisDataServiceImpl::AddNotification>
       m_addObserver;
+
+  /// Poco::NObserver for ReplaceNotification.
+  Poco::NObserver<AnalysisDataServiceObserver,
+                  AnalysisDataServiceImpl::AfterReplaceNotification>
+      m_replaceObserver;
+
+  /// Poco::NObserver for DeleteNotification.
+  Poco::NObserver<AnalysisDataServiceObserver,
+                  AnalysisDataServiceImpl::PostDeleteNotification>
+      m_deleteObserver;
+
+  /// Poco::NObserver for ClearNotification
+
+  /// Poco::NObserver for RenameNotification
+
+  /// Poco::NObserver for GroupNotification
+
+  /// Poco::NObserver for UnGroupNotification
 };
 
 } // namespace API
diff --git a/Framework/API/src/AnalysisDataServiceObserver.cpp b/Framework/API/src/AnalysisDataServiceObserver.cpp
index 78fd69efa3b..b8d77ca181a 100644
--- a/Framework/API/src/AnalysisDataServiceObserver.cpp
+++ b/Framework/API/src/AnalysisDataServiceObserver.cpp
@@ -10,13 +10,20 @@
 AnalysisDataServiceObserver::AnalysisDataServiceObserver()
     : m_addObserver(*this, &AnalysisDataServiceObserver::_addHandle) {}
 
-AnalysisDataServiceObserver::~AnalysisDataServiceObserver(){
+AnalysisDataServiceObserver::~AnalysisDataServiceObserver() {
   // Turn off/remove all observers
   this->observeAll(false);
 }
 
+
+// ------------------------------------------------------------
+// Observe Methods
+// ------------------------------------------------------------
+
 void AnalysisDataServiceObserver::observeAll(bool turnOn) {
   this->observeAdd(turnOn);
+  this->observeReplace(turnOn);
+  this->observeDelete(turnOn);
 }
 
 void AnalysisDataServiceObserver::observeAdd(bool turnOn) {
@@ -30,8 +37,68 @@ void AnalysisDataServiceObserver::observeAdd(bool turnOn) {
   m_observingAdd = turnOn;
 }
 
+void AnalysisDataServiceObserver::observeReplace(bool turnOn) {
+  if (turnOn && !m_observingReplace) {
+    AnalysisDataService::Instance().notificationCenter.addObserver(
+        m_ReplaceObserver);
+  } else if (!turnOn && m_observingReplace) {
+    AnalysisDataService::Instance().notificationCenter.removeObserver(
+        m_ReplaceObserver);
+  }
+  m_observingReplace = turnOn;
+}
+
+void AnalysisDataServiceObserver::observeDelete(bool turnOn) {
+  if (turnOn && !m_observingDelete) {
+    AnalysisDataService::Instance().notificationCenter.addObserver(
+        m_deleteObserver);
+  } else if (!turnOn && m_observingDelete) {
+    AnalysisDataService::Instance().notificationCenter.removeObserver(
+        m_deleteObserver);
+  }
+  m_observingDelete = turnOn;
+}
+
+// ------------------------------------------------------------
+// Virtual Methods
+// ------------------------------------------------------------
 void AnalysisDataServiceObserver::addHandle(
     const std::string &wsName, const Mantid::API::Workspace_sptr ws) {
   UNUSED_ARG(wsName)
   UNUSED_ARG(ws)
-}
\ No newline at end of file
+}
+
+void AnalysisDataServiceObserver::replaceHandle(
+    const std::string &wsName, const Mantid::API::Workspace_sptr ws) {
+  UNUSED_ARG(wsName)
+  UNUSED_ARG(ws)
+}
+
+void AnalysisDataServiceObserver::deleteHandle(
+    const std::string &wsName, const Mantid::API::Workspace_sptr ws) {
+  UNUSED_ARG(wsName)
+  UNUSED_ARG(ws)
+}
+
+// ------------------------------------------------------------
+// Private Methods
+// ------------------------------------------------------------
+void AnalysisDataServiceObserver::_addHandle(
+    const Poco::AutoPtr<AnalysisDataServiceImpl::AddNotification> &pNf) {
+  this->anyChangeHandle();
+  this->addHandle(pNf->objectName(), pNf->object());
+}
+
+void AnalysisDataServiceObserver::_replaceHandle(
+    const Poco::AutoPtr<AnalysisDataServiceImpl::AfterReplaceNotification>
+        &pNf) {
+  this->anyChangeHandle();
+  this->replaceHandle(pNf->objectName(), pNf->object());
+}
+
+void AnalysisDataServiceObserver::_deleteHandle(
+    const Poco::AutoPtr<AnalysisDataServiceImpl::AfterReplaceNotification>
+        &pNf) {
+  this->anyChangeHandle();
+  this->deleteHandle(pNf->objectName(), pNf->object());
+}
diff --git a/Framework/PythonInterface/inc/MantidPythonInterface/api/AnalysisDataServiceObserverAdapter.h b/Framework/PythonInterface/inc/MantidPythonInterface/api/AnalysisDataServiceObserverAdapter.h
new file mode 100644
index 00000000000..0c478f9fea5
--- /dev/null
+++ b/Framework/PythonInterface/inc/MantidPythonInterface/api/AnalysisDataServiceObserverAdapter.h
@@ -0,0 +1,43 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2013 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#ifndef MANTID_PYTHONINTERFACE_ANALYSISDATASERVICEOBSERVERADAPTER_H_
+#define MANTID_PYTHONINTERFACE_ANALYSISDATASERVICEOBSERVERADAPTER_H_
+
+#include "MantidAPI/AnalysisDataServiceObserver.h"
+#include <boost/python/wrapper.hpp>
+
+namespace Mantid {
+namespace PythonInterface {
+
+/**
+A wrapper class helping to export AnalysisDataServiceObserver to python.
+It provides access from the C++ side to methods defined in python
+on subclasses of AnalysisDataServiceObserver.
+This allows the virtual methods to be overriden by python subclasses.
+ */
+
+class DLLExport AnalysisDataServiceObserverAdapter
+    : public API::AnalysisDataServiceObserver {
+public:
+  explicit AnalysisDataServiceObserverAdapter(PyObject *self);
+  AnalysisDataServiceObserverAdapter(
+      const AnalysisDataServiceObserverAdapter &) = delete;
+  AnalysisDataServiceObserverAdapter &
+  operator=(const AnalysisDataServiceObserverAdapter &) = delete;
+  void anyChangeHandle() override;
+
+private:
+  /// Return the PyObject that owns this wrapper, i.e. self
+  inline PyObject *getSelf() const { return m_self; }
+  /// Value of "self" used by python to refer to an instance of this class
+  PyObject *m_self;
+};
+
+} // namespace PythonInterface
+} // namespace Mantid
+
+#endif /*MANTID_PYTHONINTERFACE_ANALYSISDATASERVICEOBSERVERADAPTER_H_*/
\ No newline at end of file
diff --git a/Framework/PythonInterface/mantid/api/CMakeLists.txt b/Framework/PythonInterface/mantid/api/CMakeLists.txt
index a125a7c99d8..fb4911e15d1 100644
--- a/Framework/PythonInterface/mantid/api/CMakeLists.txt
+++ b/Framework/PythonInterface/mantid/api/CMakeLists.txt
@@ -100,6 +100,7 @@ set ( SRC_FILES
   src/PythonAlgorithm/DataProcessorAdapter.cpp
   src/CloneMatrixWorkspace.cpp
   src/ExtractWorkspace.cpp
+  src/Exports/AnalysisDataServiceObserverAdapter.cpp
 )
 
 set ( INC_FILES
@@ -110,6 +111,7 @@ set ( INC_FILES
   ${HEADER_DIR}/api/FitFunctions/IPeakFunctionAdapter.h
   ${HEADER_DIR}/api/PythonAlgorithm/AlgorithmAdapter.h
   ${HEADER_DIR}/api/PythonAlgorithm/DataProcessorAdapter.h
+  ${HEADER_DIR}/api/AnalysisDataServiceObserverAdapter.h
   ${HEADER_DIR}/api/BinaryOperations.h
   ${HEADER_DIR}/api/CloneMatrixWorkspace.h
   ${HEADER_DIR}/api/ExtractWorkspace.h
diff --git a/Framework/PythonInterface/mantid/api/src/Exports/AnalysisDataServiceObserver.cpp b/Framework/PythonInterface/mantid/api/src/Exports/AnalysisDataServiceObserver.cpp
index b6d61cf3fb9..817040ef5bd 100644
--- a/Framework/PythonInterface/mantid/api/src/Exports/AnalysisDataServiceObserver.cpp
+++ b/Framework/PythonInterface/mantid/api/src/Exports/AnalysisDataServiceObserver.cpp
@@ -5,26 +5,22 @@
 //     & Institut Laue - Langevin
 // SPDX - License - Identifier: GPL - 3.0 +
 #include "MantidAPI/AnalysisDataServiceObserver.h"
+#include "MantidPythonInterface/api/AnalysisDataServiceObserverAdapter.h"
 #include "MantidPythonInterface/kernel/GetPointer.h"
 
-#include <boost/python/class.hpp>
 #include <boost/python/bases.hpp>
-#include <boost/python/register_ptr_to_python.hpp>
+#include <boost/python/class.hpp>
 
 using namespace Mantid::API;
+using namespace Mantid::PythonInterface;
 using namespace boost::python;
 
 void export_AnalysisDataServiceObserver() {
-
-  boost::python::register_ptr_to_python<
-      boost::shared_ptr<AnalysisDataServiceObserver>>();
-
   boost::python::class_<AnalysisDataServiceObserver, bases<>,
-         boost::shared_ptr<AnalysisDataServiceObserver>, boost::noncopyable>(
+                        AnalysisDataServiceObserverAdapter, boost::noncopyable>(
       "AnalysisDataServiceObserver",
       "Observes AnalysisDataService notifications: all only")
-      .def("observeAll", &AnalysisDataServiceObserver::observeAll, (arg("self"), arg("on")),
-           "Observe AnalysisDataService for any changes")
-      .def("anyChangeHandle", &AnalysisDataServiceObserver::anyChangeHandle, arg("self"),
-           "Override this to change the effect of what happens when a change occurs in the ADS");
+      .def("observeAll", &AnalysisDataServiceObserverAdapter::observeAll,
+           (arg("self"), arg("on")),
+           "Observe AnalysisDataService for any changes");
 }
\ No newline at end of file
diff --git a/Framework/PythonInterface/mantid/api/src/Exports/AnalysisDataServiceObserverAdapter.cpp b/Framework/PythonInterface/mantid/api/src/Exports/AnalysisDataServiceObserverAdapter.cpp
new file mode 100644
index 00000000000..b006380e076
--- /dev/null
+++ b/Framework/PythonInterface/mantid/api/src/Exports/AnalysisDataServiceObserverAdapter.cpp
@@ -0,0 +1,29 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#include "MantidPythonInterface/api/AnalysisDataServiceObserverAdapter.h"
+#include "MantidAPI/AnalysisDataServiceObserver.h"
+#include "MantidPythonInterface/core/CallMethod.h"
+
+#include <iostream>
+
+namespace Mantid {
+namespace PythonInterface {
+
+AnalysisDataServiceObserverAdapter::AnalysisDataServiceObserverAdapter(
+    PyObject *self)
+    : API::AnalysisDataServiceObserver(), m_self(self) {}
+
+void AnalysisDataServiceObserverAdapter::anyChangeHandle() {
+  try {
+    return callMethod<void>(getSelf(), "anyChangeHandle");
+  } catch (UndefinedAttributeError &) {
+    return;
+  }
+}
+
+} // namespace PythonInterface
+} // namespace Mantid
diff --git a/qt/applications/workbench/workbench/app/mainwindow.py b/qt/applications/workbench/workbench/app/mainwindow.py
index fdb92c4fee0..86a876c51c1 100644
--- a/qt/applications/workbench/workbench/app/mainwindow.py
+++ b/qt/applications/workbench/workbench/app/mainwindow.py
@@ -203,8 +203,7 @@ class MainWindow(QMainWindow):
 
         # Set up the project object
         from mantidqt.project.project import Project
-        project_save_filename = "mantidsave.project"
-        self.project = Project(project_save_filename)
+        self.project = Project()
 
         # uses default configuration as necessary
         self.readSettings(CONF)
diff --git a/qt/python/CMakeLists.txt b/qt/python/CMakeLists.txt
index 68e6e4ef408..a4ab9bce24d 100644
--- a/qt/python/CMakeLists.txt
+++ b/qt/python/CMakeLists.txt
@@ -69,6 +69,11 @@ endif ()
     mantidqt/dialogs/test/test_algorithm_dialog.py
     mantidqt/dialogs/test/test_spectraselectiondialog.py
 
+    mantidqt/project/test/test_projectloader.py
+    mantidqt/project/test/test_projectsaver.py
+    mantidqt/project/test/test_workspaceloader.py
+    mantidqt/project/test/test_workspacesaver.py
+
     mantidqt/utils/test/test_async.py
     mantidqt/utils/test/test_modal_tester.py
     mantidqt/utils/test/test_qt_utils.py
diff --git a/qt/python/mantidqt/project/project.py b/qt/python/mantidqt/project/project.py
index 48d4728eaf1..23ba577d251 100644
--- a/qt/python/mantidqt/project/project.py
+++ b/qt/python/mantidqt/project/project.py
@@ -9,10 +9,9 @@
 from __future__ import (absolute_import, division, print_function, unicode_literals)
 
 import os
-import glob
+import re
 from qtpy.QtWidgets import QFileDialog, QMessageBox
 
-from mantid import logger
 from mantid.api import AnalysisDataService, AnalysisDataServiceObserver
 from mantidqt.io import open_a_file_dialog
 from mantidqt.project.projectloader import ProjectLoader
@@ -20,28 +19,24 @@ from mantidqt.project.projectsaver import ProjectSaver
 
 
 class Project(AnalysisDataServiceObserver):
-    def __init__(self, project_save_name):
+    def __init__(self):
+        super(Project, self).__init__()
         # Has the project been saved
         self.saved = False
 
         # Last save locations
         self.last_project_location = None
 
-        self.project_save_name = project_save_name
-        self.ads_observer = AnalysisDataServiceObserver()
-        self.ads_observer.observeAll(True)
+        self.observeAll(True)
+
+        self.project_file_ext = ".mtdproj"
 
     def save(self):
         if self.last_project_location is None:
-            self.save_project_as()
+            self.save_as()
         else:
-            # Clear directory before saving to remove old workspaces
-            files = glob.glob(self.last_project_location + '/.*')
-            for f in files:
-                try:
-                    os.remove(f)
-                except OSError as e:
-                    logger.debug("Whilst cleaning project directory error was thrown: " + e)
+            # Clear unused workspaces
+            self._clear_unused_workspaces(self.last_project_location)
             # Actually save
             workspaces_to_save = AnalysisDataService.getObjectNames()
             project_saver = ProjectSaver(self.project_save_name)
@@ -53,7 +48,8 @@ class Project(AnalysisDataServiceObserver):
         directory = None
         # Check if it exists
         first_pass = True
-        while first_pass or (not os.path.exists(directory) and os.path.exists(directory + "mantidsave.project")):
+        while first_pass or (not os.path.exists(directory) and os.path.exists(directory + (os.path.basename(directory)
+                                                                                           + ".mtdproj"))):
             first_pass = False
             directory = open_a_file_dialog(accept_mode=QFileDialog.AcceptSave, file_mode=QFileDialog.DirectoryOnly)
             if directory is None:
@@ -63,7 +59,7 @@ class Project(AnalysisDataServiceObserver):
         # todo: get a list of workspaces but to be implemented on GUI implementation
         self.last_project_location = directory
         workspaces_to_save = AnalysisDataService.getObjectNames()
-        project_saver = ProjectSaver(self.project_save_name)
+        project_saver = ProjectSaver(self.project_file_ext)
         project_saver.save_project(directory=directory, workspace_to_save=workspaces_to_save, interfaces_to_save=None)
         self.saved = True
 
@@ -77,7 +73,7 @@ class Project(AnalysisDataServiceObserver):
             if directory is None:
                 # Cancel close dialogs
                 return
-        project_loader = ProjectLoader(self.project_save_name)
+        project_loader = ProjectLoader(self.project_file_ext)
         project_loader.load_project(directory)
         self.last_project_location = directory
 
@@ -103,3 +99,20 @@ class Project(AnalysisDataServiceObserver):
 
     def anyChangeHandle(self):
         self.modified_project()
+
+    @staticmethod
+    def _clear_unused_workspaces(path):
+        files_to_remove = []
+        list_dir = os.listdir(path)
+        current_workspaces = AnalysisDataService.getObjectNames()
+        for item in list_dir:
+            # Don't count or check files that do not end in .nxs, and check that they are not in current workspaces
+            # without the .nxs
+            if bool(re.search('$.nxs', item)):
+                workspace_name = item.replace(".nxs", "")
+                if workspace_name not in current_workspaces:
+                    files_to_remove.append(item)
+
+        # Actually remove them
+        for filename in files_to_remove:
+            os.remove(path + "/" + filename)
diff --git a/qt/python/mantidqt/project/projectloader.py b/qt/python/mantidqt/project/projectloader.py
index cde196c214a..d848d92a38b 100644
--- a/qt/python/mantidqt/project/projectloader.py
+++ b/qt/python/mantidqt/project/projectloader.py
@@ -9,8 +9,9 @@
 from __future__ import (absolute_import, division, print_function, unicode_literals)
 
 import json
+import os
 
-import workspaceloader
+from mantidqt.project import workspaceloader
 from mantid import AnalysisDataService as ADS
 from mantid import logger
 
@@ -25,28 +26,29 @@ def _confirm_all_workspaces_loaded(workspaces_to_confirm):
 
 
 class ProjectLoader(object):
-    def __init__(self, project_load_name):
-        self.project_reader = ProjectReader(project_load_name)
+    def __init__(self, project_file_ext):
+        self.project_reader = ProjectReader(project_file_ext)
         self.workspace_loader = workspaceloader.WorkspaceLoader()
+        self.project_file_ext = project_file_ext
 
     def load_project(self, directory):
         # Read project
         self.project_reader.read_project(directory)
 
         # Load in the workspaces
-        self.workspace_loader.load_workspaces(directory=directory)
+        self.workspace_loader.load_workspaces(directory=directory, project_file_ext=self.project_file_ext)
         return _confirm_all_workspaces_loaded(workspaces_to_confirm=self.project_reader.workspace_names)
 
 
 class ProjectReader(object):
-    def __init__(self, project_load_name):
+    def __init__(self, project_file_ext):
         self.workspace_names = None
         self.interfaces_dicts = None
         self.plot_dicts = None
-        self.project_load_name = project_load_name
+        self.project_file_ext = project_file_ext
 
     def read_project(self, directory):
-        f = open(directory + "/" + self.project_load_name)
+        f = open(directory + "/" + (os.path.basename(directory) + self.project_file_ext))
         json_data = json.load(f)
         self.workspace_names = json_data["workspaces"]
         self.interfaces_dicts = json_data["interfaces"]
diff --git a/qt/python/mantidqt/project/projectsaver.py b/qt/python/mantidqt/project/projectsaver.py
index 357ff26499d..619ab148bbb 100644
--- a/qt/python/mantidqt/project/projectsaver.py
+++ b/qt/python/mantidqt/project/projectsaver.py
@@ -9,16 +9,15 @@
 from __future__ import (absolute_import, division, print_function, unicode_literals)
 
 from json import dump
-from os import makedirs
-from os.path import isdir
+import os
 
 from mantidqt.project import workspacesaver
 from mantid import logger
 
 
 class ProjectSaver(object):
-    def __init__(self, project_save_name):
-        self.project_save_name = project_save_name
+    def __init__(self, project_file_ext):
+        self.project_file_ext = project_file_ext
 
     def save_project(self, directory, workspace_to_save=None, interfaces_to_save=None):
         """
@@ -47,12 +46,12 @@ class ProjectSaver(object):
 
         # Pass dicts to Project Writer
         writer = ProjectWriter(dictionaries_to_save, directory, workspace_saver.get_output_list(),
-                               self.project_save_name)
+                               self.project_file_ext)
         writer.write_out()
 
 
 class ProjectWriter(object):
-    def __init__(self, dicts, save_location, workspace_names, project_save_name):
+    def __init__(self, dicts, save_location, workspace_names, project_file_ext):
         """
 
         :param dicts:
@@ -62,7 +61,7 @@ class ProjectWriter(object):
         self.dicts_to_save = dicts
         self.workspace_names = workspace_names
         self.directory = save_location
-        self.project_save_name = project_save_name
+        self.project_file_ext = project_file_ext
 
     def write_out(self):
         """
@@ -72,8 +71,8 @@ class ProjectWriter(object):
         workspace_interface_dict = {"workspaces": self.workspace_names, "interfaces": self.dicts_to_save}
 
         # Open file and save the string to it alongside the workspace_names
-        file_name = self.directory + '/' + self.project_save_name
-        if not isdir(self.directory):
-            makedirs(self.directory)
+        file_name = self.directory + '/' + (os.path.basename(self.directory) + self.project_file_ext)
+        if not os.path.isdir(self.directory):
+            os.makedirs(self.directory)
         f = open(file_name, 'w+')
         dump(obj=workspace_interface_dict, fp=f)
diff --git a/qt/python/mantidqt/project/test/test_projectloader.py b/qt/python/mantidqt/project/test/test_projectloader.py
index 852b44875c1..2dff9a20da7 100644
--- a/qt/python/mantidqt/project/test/test_projectloader.py
+++ b/qt/python/mantidqt/project/test/test_projectloader.py
@@ -9,23 +9,24 @@
 
 import unittest
 
-from os.path import isdir, expanduser
+from os.path import isdir
 from shutil import rmtree
+import tempfile
 
 from mantid.api import AnalysisDataService as ADS
 from mantid.simpleapi import CreateSampleWorkspace
 from mantidqt.project import projectloader, projectsaver
 
 
-project_file_name = "mantidsave.project"
-working_directory = expanduser("~") + "/project_loader_test"
+project_file_ext = ".mtdproj"
+working_directory = tempfile.mkdtemp()
 
 
 class ProjectLoaderTest(unittest.TestCase):
     def setUp(self):
         ws1_name = "ws1"
         ADS.addOrReplace(ws1_name, CreateSampleWorkspace(OutputWorkspace=ws1_name))
-        project_saver = projectsaver.ProjectSaver(project_file_name)
+        project_saver = projectsaver.ProjectSaver(project_file_ext)
         project_saver.save_project(workspace_to_save=[ws1_name], directory=working_directory)
 
     def tearDown(self):
@@ -34,7 +35,7 @@ class ProjectLoaderTest(unittest.TestCase):
             rmtree(working_directory)
 
     def test_project_loading(self):
-        project_loader = projectloader.ProjectLoader(project_file_name)
+        project_loader = projectloader.ProjectLoader(project_file_ext)
 
         self.assertTrue(project_loader.load_project(working_directory))
 
@@ -50,7 +51,7 @@ class ProjectReaderTest(unittest.TestCase):
     def setUp(self):
         ws1_name = "ws1"
         ADS.addOrReplace(ws1_name, CreateSampleWorkspace(OutputWorkspace=ws1_name))
-        project_saver = projectsaver.ProjectSaver(project_file_name)
+        project_saver = projectsaver.ProjectSaver(project_file_ext)
         project_saver.save_project(workspace_to_save=[ws1_name], directory=working_directory)
 
     def tearDown(self):
@@ -59,7 +60,7 @@ class ProjectReaderTest(unittest.TestCase):
             rmtree(working_directory)
 
     def test_project_reading(self):
-        project_reader = projectloader.ProjectReader(project_file_name)
+        project_reader = projectloader.ProjectReader(project_file_ext)
         project_reader.read_project(working_directory)
         self.assertEqual(["ws1"], project_reader.workspace_names)
         self.assertEqual({}, project_reader.interfaces_dicts)
diff --git a/qt/python/mantidqt/project/test/test_projectsaver.py b/qt/python/mantidqt/project/test/test_projectsaver.py
index d2407beb69d..0fddd3cad46 100644
--- a/qt/python/mantidqt/project/test/test_projectsaver.py
+++ b/qt/python/mantidqt/project/test/test_projectsaver.py
@@ -9,8 +9,7 @@
 import unittest
 import tempfile
 
-from os import listdir
-from os.path import isdir
+import os
 from shutil import rmtree
 
 from mantid.api import AnalysisDataService as ADS
@@ -18,7 +17,7 @@ from mantid.simpleapi import CreateSampleWorkspace
 from mantidqt.project import projectsaver
 
 
-project_file_name = "mantidsave.mtdproj"
+project_file_ext = ".mtdproj"
 working_directory = tempfile.mkdtemp()
 
 
@@ -28,14 +27,14 @@ class ProjectSaverTest(unittest.TestCase):
 
     def setUp(self):
         # In case it was hard killed and is still present
-        if isdir(working_directory):
+        if os.path.isdir(working_directory):
             rmtree(working_directory)
 
     def test_only_one_workspace_saving(self):
         ws1_name = "ws1"
         ADS.addOrReplace(ws1_name, CreateSampleWorkspace(OutputWorkspace=ws1_name))
-        project_saver = projectsaver.ProjectSaver(project_file_name)
-        file_name = working_directory + "/" + project_file_name
+        project_saver = projectsaver.ProjectSaver(project_file_ext)
+        file_name = working_directory + "/" + os.path.basename(working_directory) + project_file_ext
         saved_file = "{\"interfaces\": {}, \"workspaces\": [\"ws1\"]}"
 
         project_saver.save_project(workspace_to_save=[ws1_name], directory=working_directory)
@@ -45,10 +44,10 @@ class ProjectSaverTest(unittest.TestCase):
         self.assertEqual(f.read(), saved_file)
 
         # Check workspace is saved
-        list_of_files = listdir(working_directory)
+        list_of_files = os.listdir(working_directory)
         self.assertEqual(len(list_of_files), 2)
-        self.assertTrue(project_file_name in list_of_files)
-        self.assertTrue(ws1_name in list_of_files)
+        self.assertTrue(os.path.basename(working_directory) + project_file_ext in list_of_files)
+        self.assertTrue(ws1_name + ".nxs" in list_of_files)
 
     def test_only_multiple_workspaces_saving(self):
         ws1_name = "ws1"
@@ -61,8 +60,8 @@ class ProjectSaverTest(unittest.TestCase):
         CreateSampleWorkspace(OutputWorkspace=ws3_name)
         CreateSampleWorkspace(OutputWorkspace=ws4_name)
         CreateSampleWorkspace(OutputWorkspace=ws5_name)
-        project_saver = projectsaver.ProjectSaver(project_file_name)
-        file_name = working_directory + "/" + project_file_name
+        project_saver = projectsaver.ProjectSaver(project_file_ext)
+        file_name = working_directory + "/" + os.path.basename(working_directory) + project_file_ext
         saved_file = "{\"interfaces\": {}, \"workspaces\": [\"ws1\", \"ws2\", \"ws3\", \"ws4\", \"ws5\"]}"
 
         project_saver.save_project(workspace_to_save=[ws1_name, ws2_name, ws3_name, ws4_name, ws5_name],
@@ -73,14 +72,14 @@ class ProjectSaverTest(unittest.TestCase):
         self.assertEqual(f.read(), saved_file)
 
         # Check workspace is saved
-        list_of_files = listdir(working_directory)
+        list_of_files = os.listdir(working_directory)
         self.assertEqual(len(list_of_files), 6)
-        self.assertTrue(project_file_name in list_of_files)
-        self.assertTrue(ws1_name in list_of_files)
-        self.assertTrue(ws2_name in list_of_files)
-        self.assertTrue(ws3_name in list_of_files)
-        self.assertTrue(ws4_name in list_of_files)
-        self.assertTrue(ws5_name in list_of_files)
+        self.assertTrue(os.path.basename(working_directory) + project_file_ext in list_of_files)
+        self.assertTrue(ws1_name + ".nxs" in list_of_files)
+        self.assertTrue(ws2_name + ".nxs" in list_of_files)
+        self.assertTrue(ws3_name + ".nxs" in list_of_files)
+        self.assertTrue(ws4_name + ".nxs" in list_of_files)
+        self.assertTrue(ws5_name + ".nxs" in list_of_files)
 
     def test_only_saving_one_workspace_when_multiple_are_present_in_the_ADS(self):
         ws1_name = "ws1"
@@ -89,8 +88,8 @@ class ProjectSaverTest(unittest.TestCase):
         CreateSampleWorkspace(OutputWorkspace=ws1_name)
         CreateSampleWorkspace(OutputWorkspace=ws2_name)
         CreateSampleWorkspace(OutputWorkspace=ws3_name)
-        project_saver = projectsaver.ProjectSaver()
-        file_name = working_directory + "/" + project_file_name
+        project_saver = projectsaver.ProjectSaver(project_file_ext)
+        file_name = working_directory + "/" + os.path.basename(working_directory) + project_file_ext
         saved_file = "{\"interfaces\": {}, \"workspaces\": [\"ws1\"]}"
 
         project_saver.save_project(workspace_to_save=[ws1_name], directory=working_directory)
@@ -100,10 +99,10 @@ class ProjectSaverTest(unittest.TestCase):
         self.assertEqual(f.read(), saved_file)
 
         # Check workspace is saved
-        list_of_files = listdir(working_directory)
+        list_of_files = os.listdir(working_directory)
         self.assertEqual(len(list_of_files), 2)
-        self.assertTrue(project_file_name in list_of_files)
-        self.assertTrue(ws1_name in list_of_files)
+        self.assertTrue(os.path.basename(working_directory) + project_file_ext in list_of_files)
+        self.assertTrue(ws1_name + ".nxs" in list_of_files)
 
     #def test_only_one_interface_saving(self):
 
@@ -126,14 +125,14 @@ class ProjectWriterTest(unittest.TestCase):
 
     def setUp(self):
         # In case it was hard killed and is still present
-        if isdir(working_directory):
+        if os.path.isdir(working_directory):
             rmtree(working_directory)
 
     def test_write_out_on_just_dicts(self):
         workspace_list = []
         small_dict = {"interface1": {"value1": 2, "value2": 3}, "interface2": {"value3": 4, "value4": 5}}
-        project_writer = projectsaver.ProjectWriter(small_dict, working_directory, workspace_list, project_file_name)
-        file_name = working_directory + "/" + project_file_name
+        project_writer = projectsaver.ProjectWriter(small_dict, working_directory, workspace_list, project_file_ext)
+        file_name = working_directory + "/" + os.path.basename(working_directory) + project_file_ext
         saved_file = "{\"interfaces\": {\"interface1\": {\"value2\": 3, \"value1\": 2}, \"interface2\": {\"value4\"" \
                      ": 5, \"value3\": 4}}, \"workspaces\": []}"
 
@@ -145,8 +144,8 @@ class ProjectWriterTest(unittest.TestCase):
     def test_write_out_on_just_workspaces(self):
         workspace_list = ["ws1", "ws2", "ws3", "ws4"]
         small_dict = {}
-        project_writer = projectsaver.ProjectWriter(small_dict, working_directory, workspace_list, project_file_name)
-        file_name = working_directory + "/" + project_file_name
+        project_writer = projectsaver.ProjectWriter(small_dict, working_directory, workspace_list, project_file_ext)
+        file_name = working_directory + "/" + os.path.basename(working_directory) + project_file_ext
         saved_file = "{\"interfaces\": {}, \"workspaces\": [\"ws1\", \"ws2\", \"ws3\", \"ws4\"]}"
 
         project_writer.write_out()
@@ -157,8 +156,8 @@ class ProjectWriterTest(unittest.TestCase):
     def test_write_out_on_both_workspaces_and_dicts(self):
         workspace_list = ["ws1", "ws2", "ws3", "ws4"]
         small_dict = {"interface1": {"value1": 2, "value2": 3}, "interface2": {"value3": 4, "value4": 5}}
-        project_writer = projectsaver.ProjectWriter(small_dict, working_directory, workspace_list, project_file_name)
-        file_name = working_directory + "/" + project_file_name
+        project_writer = projectsaver.ProjectWriter(small_dict, working_directory, workspace_list, project_file_ext)
+        file_name = working_directory + "/" + os.path.basename(working_directory) + project_file_ext
         saved_file = "{\"interfaces\": {\"interface1\": {\"value2\": 3, \"value1\": 2}, \"interface2\": {\"value4\":" \
                      " 5, \"value3\": 4}}, \"workspaces\": [\"ws1\", \"ws2\", \"ws3\", \"ws4\"]}"
         project_writer.write_out()
diff --git a/qt/python/mantidqt/project/test/test_workspaceloader.py b/qt/python/mantidqt/project/test/test_workspaceloader.py
index 2900d9cd076..bc45e2cb789 100644
--- a/qt/python/mantidqt/project/test/test_workspaceloader.py
+++ b/qt/python/mantidqt/project/test/test_workspaceloader.py
@@ -9,30 +9,30 @@
 
 import unittest
 
-from os.path import isdir, expanduser
+from os.path import isdir
 from shutil import rmtree
+import tempfile
 
 from mantid.api import AnalysisDataService as ADS
 from mantid.simpleapi import CreateSampleWorkspace
 from mantidqt.project import projectsaver, workspaceloader
 
 
-working_directory = expanduser("~") + "/workspace_loader_test"
-
-
 class WorkspaceLoaderTest(unittest.TestCase):
     def setUp(self):
-        ws1_name = "ws1"
-        ADS.addOrReplace(ws1_name, CreateSampleWorkspace(OutputWorkspace=ws1_name))
-        project_saver = projectsaver.ProjectSaver()
-        project_saver.save_project(workspace_to_save=[ws1_name], directory=working_directory)
+        self.working_directory = tempfile.mkdtemp()
+        self.ws1_name = "ws1"
+        self.project_ext = ".mtdproj"
+        ADS.addOrReplace(self.ws1_name, CreateSampleWorkspace(OutputWorkspace=self.ws1_name))
+        project_saver = projectsaver.ProjectSaver(self.project_ext)
+        project_saver.save_project(workspace_to_save=[self.ws1_name], directory=self.working_directory)
 
     def tearDown(self):
         ADS.clear()
-        if isdir(working_directory):
-            rmtree(working_directory)
+        if isdir(self.working_directory):
+            rmtree(self.working_directory)
 
     def test_workspace_loading(self):
         workspace_loader = workspaceloader.WorkspaceLoader()
-        workspace_loader.load_workspaces(working_directory)
-        self.assertEqual(ADS.getObjectNames(), ["ws1"])
+        workspace_loader.load_workspaces(self.working_directory, self.project_ext)
+        self.assertEqual(ADS.getObjectNames(), [self.ws1_name])
diff --git a/qt/python/mantidqt/project/test/test_workspacesaver.py b/qt/python/mantidqt/project/test/test_workspacesaver.py
index 66dece07dbc..b77237fb761 100644
--- a/qt/python/mantidqt/project/test/test_workspacesaver.py
+++ b/qt/python/mantidqt/project/test/test_workspacesaver.py
@@ -9,9 +9,10 @@
 
 import unittest
 
-from os import listdir, mkdir
-from os.path import isdir, expanduser
+from os import listdir
+from os.path import isdir
 from shutil import rmtree
+import tempfile
 
 from mantid.api import AnalysisDataService as ADS, IMDEventWorkspace  # noqa
 from mantid.dataobjects import MDHistoWorkspace, MaskWorkspace  # noqa
@@ -19,33 +20,30 @@ from mantidqt.project import workspacesaver
 from mantid.simpleapi import (CreateSampleWorkspace, CreateMDHistoWorkspace, LoadMD, LoadMask, MaskDetectors,  # noqa
                               ExtractMask)  # noqa
 
-working_directory = expanduser("~") + "/workspace_saver_test_directory"
-
 
 class WorkspaceSaverTest(unittest.TestCase):
     def setUp(self):
-        if not isdir(working_directory):
-            mkdir(working_directory)
+        self.working_directory = tempfile.mkdtemp()
 
     def tearDown(self):
         ADS.clear()
-        if isdir(working_directory):
-            rmtree(working_directory)
+        if isdir(self.working_directory):
+            rmtree(self.working_directory)
 
     def test_saving_single_workspace(self):
-        ws_saver = workspacesaver.WorkspaceSaver(working_directory)
+        ws_saver = workspacesaver.WorkspaceSaver(self.working_directory)
         ws1 = CreateSampleWorkspace()
         ws1_name = "ws1"
 
         ADS.addOrReplace(ws1_name, ws1)
         ws_saver.save_workspaces([ws1_name])
 
-        list_of_files = listdir(working_directory)
+        list_of_files = listdir(self.working_directory)
         self.assertEqual(len(list_of_files), 1)
         self.assertEqual(list_of_files[0], ws1_name)
 
     def test_saving_multiple_workspaces(self):
-        ws_saver = workspacesaver.WorkspaceSaver(working_directory)
+        ws_saver = workspacesaver.WorkspaceSaver(self.working_directory)
         ws1 = CreateSampleWorkspace()
         ws1_name = "ws1"
         ws2 = CreateSampleWorkspace()
@@ -55,13 +53,13 @@ class WorkspaceSaverTest(unittest.TestCase):
         ADS.addOrReplace(ws2_name, ws2)
         ws_saver.save_workspaces([ws1_name, ws2_name])
 
-        list_of_files = listdir(working_directory)
+        list_of_files = listdir(self.working_directory)
         self.assertEqual(len(list_of_files), 2)
         self.assertEqual(list_of_files[0], ws2_name)
         self.assertEqual(list_of_files[1], ws1_name)
 
     def test_when_MDWorkspace_is_in_ADS(self):
-        ws_saver = workspacesaver.WorkspaceSaver(working_directory)
+        ws_saver = workspacesaver.WorkspaceSaver(self.working_directory)
         ws1 = CreateMDHistoWorkspace(SignalInput='1,2,3,4,5,6,7,8,9', ErrorInput='1,1,1,1,1,1,1,1,1',
                                      Dimensionality='2', Extents='-1,1,-1,1', NumberOfBins='3,3', Names='A,B',
                                      Units='U,T')
@@ -70,13 +68,13 @@ class WorkspaceSaverTest(unittest.TestCase):
         ADS.addOrReplace(ws1_name, ws1)
         ws_saver.save_workspaces([ws1_name])
 
-        list_of_files = listdir(working_directory)
+        list_of_files = listdir(self.working_directory)
         self.assertEqual(len(list_of_files), 1)
         self.assertEqual(list_of_files[0], ws1_name)
         self._load_MDWorkspace_and_test_it(ws1_name)
 
     def _load_MDWorkspace_and_test_it(self, save_name):
-        filename = working_directory + '/' + save_name
+        filename = self.working_directory + '/' + save_name
         ws = LoadMD(Filename=filename)
         ws_is_a_mdworkspace = isinstance(ws, IMDEventWorkspace) or isinstance(ws, MDHistoWorkspace)
         self.assertEqual(ws_is_a_mdworkspace, True)
diff --git a/qt/python/mantidqt/project/workspaceloader.py b/qt/python/mantidqt/project/workspaceloader.py
index 6729f875b2c..55f3e38c775 100644
--- a/qt/python/mantidqt/project/workspaceloader.py
+++ b/qt/python/mantidqt/project/workspaceloader.py
@@ -13,13 +13,22 @@ from os import path, listdir
 from mantid import logger
 
 
+# List of extensions to ignore
+ignore_exts = [".py"]
+
+
 class WorkspaceLoader(object):
-    def load_workspaces(self, directory):
+    @staticmethod
+    def load_workspaces(directory, project_file_ext):
+        # Include passed project file extension in ignore_exts from projects
+        ignore_exts.append(project_file_ext)
+
         from mantid.simpleapi import Load  # noqa
         filenames = listdir(directory)
         for filename in filenames:
             workspace_name, file_ext = path.splitext(filename)
-            if file_ext != u'.project':
+            # if file_ext not in [".mtdproj", ".py"]:
+            if file_ext not in ignore_exts:
                 try:
                     Load(directory + "/" + filename, OutputWorkspace=workspace_name)
                 except BaseException as exception:
diff --git a/qt/python/mantidqt/project/workspacesaver.py b/qt/python/mantidqt/project/workspacesaver.py
index 29a37c431fd..b7cc1c4d833 100644
--- a/qt/python/mantidqt/project/workspacesaver.py
+++ b/qt/python/mantidqt/project/workspacesaver.py
@@ -51,10 +51,10 @@ class WorkspaceSaver(object):
 
             if isinstance(workspace, MDHistoWorkspace) or isinstance(workspace, IMDEventWorkspace):
                 # Save normally using SaveMD
-                SaveMD(InputWorkspace=workspace_name, Filename=place_to_save_workspace)
+                SaveMD(InputWorkspace=workspace_name, Filename=place_to_save_workspace + ".nxs")
             else:
                 # Save normally using SaveNexusProcessed
-                SaveNexusProcessed(InputWorkspace=workspace_name, Filename=place_to_save_workspace)
+                SaveNexusProcessed(InputWorkspace=workspace_name, Filename=place_to_save_workspace + ".nxs")
 
             self.output_list.append(workspace_name)
 
-- 
GitLab