Пример #1
0
class ProjectController(bc.BaseController):
    """
    Displays pages which deals with Project data management.
    """

    PRROJECTS_FOR_LINK_KEY = "projectsforlink"
    PRROJECTS_LINKED_KEY = "projectslinked"
    KEY_OPERATION_FILTERS = "operationfilters"

    def __init__(self):
        super(ProjectController, self).__init__()
        self.project_service = ProjectService()
        self.import_service = ImportService()

    @cherrypy.expose
    @using_template('base_template')
    @bc.settings()
    @logged()
    def index(self):
        """
        Display project main-menu. Choose one project to work with.
        """
        current_project = bc.get_current_project()
        if current_project is None:
            raise cherrypy.HTTPRedirect("/project/viewall")
        template_specification = dict(mainContent="project_submenu",
                                      title="TVB Project Menu")
        return self.fill_default_attributes(template_specification)

    @cherrypy.expose
    @using_template('base_template')
    @bc.settings()
    @logged()
    def viewall(self, create=False, page=1, selected_project_id=None, **_):
        """
        Display all existent projects. Choose one project to work with.
        """
        page = int(page)
        if cherrypy.request.method == 'POST' and create:
            raise cherrypy.HTTPRedirect('/project/editone')
        current_user_id = bc.get_logged_user().id

        ## Select project if user choose one.
        if selected_project_id is not None:
            try:
                selected_project = self.project_service.find_project(
                    selected_project_id)
                self._mark_selected(selected_project)
            except ProjectServiceException, excep:
                self.logger.error(excep)
                self.logger.warning("Could not select project: " +
                                    str(selected_project_id))
                bc.set_error_message("Could not select project: " +
                                     str(selected_project_id))

        #Prepare template response
        prjs, pages_no = self.project_service.retrieve_projects_for_user(
            current_user_id, page)
        template_specification = dict(mainContent="project/viewall",
                                      title="Available TVB Projects",
                                      projectsList=prjs,
                                      page_number=page,
                                      total_pages=pages_no)
        return self.fill_default_attributes(template_specification, 'list')
class ProjectServiceTest(TransactionalTestCase):
    """
    This class contains tests for the tvb.core.services.projectservice module.
    """
    def setUp(self):
        """
        Reset the database before each test.
        """
        EVENTS_FOLDER = ''
        #        self.clean_database()
        self.project_service = ProjectService()
        self.structure_helper = FilesHelper()
        self.test_user = TestFactory.create_user()

    def tearDown(self):
        """
        Remove project folders and clean up database.
        """
        created_projects = dao.get_projects_for_user(self.test_user.id)
        for project in created_projects:
            self.structure_helper.remove_project_structure(project.name)
        self.delete_project_folders()


#        self.clean_database()

    def test_create_project_happy_flow(self):
        """
        Standard flow for creating a new project.
        """
        user1 = TestFactory.create_user('test_user1')
        user2 = TestFactory.create_user('test_user2')
        initial_projects = dao.get_projects_for_user(self.test_user.id)
        self.assertEqual(len(initial_projects), 0,
                         "Database reset probably failed!")
        TestFactory.create_project(self.test_user,
                                   'test_project',
                                   users=[user1.id, user2.id])
        resulting_projects = dao.get_projects_for_user(self.test_user.id)
        self.assertEqual(len(resulting_projects), 1,
                         "Project with valid data not inserted!")
        project = resulting_projects[0]
        if project.name == "test_project":
            self.assertEqual(project.description, "description",
                             "Description do no match")
            users_for_project = dao.get_members_of_project(project.id)
            for user in users_for_project:
                self.assertTrue(user.id in [user1.id, user2.id],
                                "Users not stored properly.")
        self.assertTrue(
            os.path.exists(
                os.path.join(cfg.TVB_STORAGE, FilesHelper.PROJECTS_FOLDER,
                             "test_project")),
            "Folder for project was not created")

    def test_create_project_empty_name(self):
        """
        Creating a project with an empty name.
        """
        data = dict(name="", description="test_description", users=[])
        initial_projects = dao.get_projects_for_user(self.test_user.id)
        self.assertEqual(len(initial_projects), 0,
                         "Database reset probably failed!")
        self.assertRaises(ProjectServiceException,
                          self.project_service.store_project, self.test_user,
                          True, None, **data)

    def test_edit_project_happy_flow(self):
        """
        Standard flow for editing an existing project.
        """
        selected_project = TestFactory.create_project(self.test_user,
                                                      'test_proj')
        proj_root = self.structure_helper.get_project_folder(selected_project)
        initial_projects = dao.get_projects_for_user(self.test_user.id)
        self.assertEqual(len(initial_projects), 1,
                         "Database initialization probably failed!")

        edited_data = dict(name="test_project",
                           description="test_description",
                           users=[])
        edited_project = self.project_service.store_project(
            self.test_user, False, selected_project.id, **edited_data)
        self.assertFalse(os.path.exists(proj_root),
                         "Previous folder not deleted")
        proj_root = self.structure_helper.get_project_folder(edited_project)
        self.assertTrue(os.path.exists(proj_root), "New folder not created!")
        self.assertNotEqual(selected_project.name, edited_project.name,
                            "Project was no changed!")

    def test_edit_project_unexisting(self):
        """
        Trying to edit an un-existing project.
        """
        selected_project = TestFactory.create_project(self.test_user,
                                                      'test_proj')
        self.structure_helper.get_project_folder(selected_project)
        initial_projects = dao.get_projects_for_user(self.test_user.id)
        self.assertEqual(len(initial_projects), 1,
                         "Database initialization probably failed!")
        data = dict(name="test_project",
                    description="test_description",
                    users=[])
        self.assertRaises(ProjectServiceException,
                          self.project_service.store_project, self.test_user,
                          False, 99, **data)

    def test_find_project_happy_flow(self):
        """
        Standard flow for finding a project by it's id.
        """
        initial_projects = dao.get_projects_for_user(self.test_user.id)
        self.assertEqual(len(initial_projects), 0,
                         "Database reset probably failed!")
        inserted_project = TestFactory.create_project(self.test_user,
                                                      'test_project')
        self.assertTrue(
            self.project_service.find_project(inserted_project.id) is not None,
            "Project not found !")
        dao_returned_project = dao.get_project_by_id(inserted_project.id)
        service_returned_project = self.project_service.find_project(
            inserted_project.id)
        self.assertEqual(
            dao_returned_project.id, service_returned_project.id,
            "Data returned from service is different from data returned by DAO."
        )
        self.assertEqual(
            dao_returned_project.name, service_returned_project.name,
            "Data returned from service is different than  data returned by DAO."
        )
        self.assertEqual(
            dao_returned_project.description,
            service_returned_project.description,
            "Data returned from service is different from data returned by DAO."
        )
        self.assertEqual(
            dao_returned_project.members, service_returned_project.members,
            "Data returned from service is different from data returned by DAO."
        )

    def test_find_project_unexisting(self):
        """
        Searching for an un-existing project.
        """
        data = dict(name="test_project",
                    description="test_description",
                    users=[])
        initial_projects = dao.get_projects_for_user(self.test_user.id)
        self.assertEqual(len(initial_projects), 0,
                         "Database reset probably failed!")
        self.project_service.store_project(self.test_user, True, None, **data)
        self.assertRaises(ProjectServiceException,
                          self.project_service.find_project, 99)

    def test_retrieve_projects_for_user(self):
        """
        Test for retrieving the projects for a given user. One page only.
        """
        initial_projects = self.project_service.retrieve_projects_for_user(
            self.test_user.id)[0]
        self.assertEqual(len(initial_projects), 0,
                         "Database was not reset properly!")
        TestFactory.create_project(self.test_user, 'test_proj')
        TestFactory.create_project(self.test_user, 'test_proj1')
        TestFactory.create_project(self.test_user, 'test_proj2')
        user1 = TestFactory.create_user('another_user')
        TestFactory.create_project(user1, 'test_proj3')
        projects = self.project_service.retrieve_projects_for_user(
            self.test_user.id)[0]
        self.assertEqual(len(projects), 3, "Projects not retrieved properly!")
        for project in projects:
            self.assertNotEquals(
                project.name, "test_project3",
                "This project should not have been retrieved")

    def test_retrieve_1project_3usr(self):
        """
        One user as admin, two users as members, getting projects for admin and for any of
        the members should return one.
        """
        member1 = TestFactory.create_user("member1")
        member2 = TestFactory.create_user("member2")
        TestFactory.create_project(self.test_user,
                                   'Testproject',
                                   users=[member1.id, member2.id])
        projects = self.project_service.retrieve_projects_for_user(
            self.test_user.id, 1)[0]
        self.assertEqual(len(projects), 1, "Projects not retrieved properly!")
        projects = self.project_service.retrieve_projects_for_user(
            member1.id, 1)[0]
        self.assertEqual(len(projects), 1, "Projects not retrieved properly!")
        projects = self.project_service.retrieve_projects_for_user(
            member2.id, 1)[0]
        self.assertEqual(len(projects), 1, "Projects not retrieved properly!")

    def test_retrieve_3projects_3usr(self):
        """
        Three users, 3 projects. Structure of db:
        proj1: {admin: user1, members: [user2, user3]}
        proj2: {admin: user2, members: [user1]}
        proj3: {admin: user3, members: [user1, user2]}
        Check valid project returns for all the users.
        """
        member1 = TestFactory.create_user("member1")
        member2 = TestFactory.create_user("member2")
        member3 = TestFactory.create_user("member3")
        TestFactory.create_project(member1,
                                   'TestProject1',
                                   users=[member2.id, member3.id])
        TestFactory.create_project(member2, 'TestProject2', users=[member1.id])
        TestFactory.create_project(member3,
                                   'TestProject3',
                                   users=[member1.id, member2.id])
        projects = self.project_service.retrieve_projects_for_user(
            member1.id, 1)[0]
        self.assertEqual(len(projects), 3, "Projects not retrieved properly!")
        projects = self.project_service.retrieve_projects_for_user(
            member2.id, 1)[0]
        self.assertEqual(len(projects), 3, "Projects not retrieved properly!")
        projects = self.project_service.retrieve_projects_for_user(
            member3.id, 1)[0]
        self.assertEqual(len(projects), 2, "Projects not retrieved properly!")

    def test_retrieve_projects_random(self):
        """
        Generate a large number of users/projects, and validate the results.
        """
        ExtremeTestFactory.generate_users(NR_USERS, MAX_PROJ_PER_USER)
        for i in range(NR_USERS):
            current_user = dao.get_user_by_name("gen" + str(i))
            expected_projects = ExtremeTestFactory.VALIDATION_DICT[
                current_user.id]
            if expected_projects % PROJECTS_PAGE_SIZE == 0:
                expected_pages = expected_projects / PROJECTS_PAGE_SIZE
                exp_proj_per_page = PROJECTS_PAGE_SIZE
            else:
                expected_pages = expected_projects / PROJECTS_PAGE_SIZE + 1
                exp_proj_per_page = expected_projects % PROJECTS_PAGE_SIZE
            if expected_projects == 0:
                expected_pages = 0
                exp_proj_per_page = 0
            projects, pages = self.project_service.retrieve_projects_for_user(
                current_user.id, expected_pages)
            self.assertEqual(
                len(projects), exp_proj_per_page,
                "Projects not retrieved properly! Expected:" +
                str(exp_proj_per_page) + "but got:" + str(len(projects)))
            self.assertEqual(pages, expected_pages,
                             "Pages not retrieved properly!")
        for folder in os.listdir(cfg.TVB_STORAGE):
            full_path = os.path.join(cfg.TVB_STORAGE, folder)
            if os.path.isdir(full_path) and folder.startswith('Generated'):
                shutil.rmtree(full_path)

    def test_retrieve_projects_page2(self):
        """
        Test for retrieving the second page projects for a given user.
        """
        for i in range(PROJECTS_PAGE_SIZE + 3):
            TestFactory.create_project(self.test_user, 'test_proj' + str(i))
        projects, pages = self.project_service.retrieve_projects_for_user(
            self.test_user.id, 2)
        self.assertEqual(len(projects),
                         (PROJECTS_PAGE_SIZE + 3) % PROJECTS_PAGE_SIZE,
                         "Pagination inproper.")
        self.assertEqual(pages, 2, 'Wrong number of pages retrieved.')

    def test_retrieve_projects_and_del(self):
        """
        Test for retrieving the second page projects for a given user.
        """
        created_projects = []
        for i in range(PROJECTS_PAGE_SIZE + 1):
            created_projects.append(
                TestFactory.create_project(self.test_user,
                                           'test_proj' + str(i)))
        projects, pages = self.project_service.retrieve_projects_for_user(
            self.test_user.id, 2)
        self.assertEqual(len(projects),
                         (PROJECTS_PAGE_SIZE + 1) % PROJECTS_PAGE_SIZE,
                         "Pagination improper.")
        self.assertEqual(pages,
                         (PROJECTS_PAGE_SIZE + 1) / PROJECTS_PAGE_SIZE + 1,
                         'Wrong number of pages')
        self.project_service.remove_project(created_projects[1].id)
        projects, pages = self.project_service.retrieve_projects_for_user(
            self.test_user.id, 2)
        self.assertEqual(len(projects), 0, "Pagination improper.")
        self.assertEqual(pages, 1, 'Wrong number of pages retrieved.')
        projects, pages = self.project_service.retrieve_projects_for_user(
            self.test_user.id, 1)
        self.assertEqual(len(projects), PROJECTS_PAGE_SIZE,
                         "Pagination improper.")
        self.assertEqual(pages, 1, 'Wrong number of pages retrieved.')

    def test_get_linkable_projects(self):
        """
        Test for retrieving the projects for a given user.
        """
        initial_projects = self.project_service.retrieve_projects_for_user(
            self.test_user.id)[0]
        self.assertEqual(len(initial_projects), 0, "Database was not reseted!")
        test_proj = []
        user1 = TestFactory.create_user("another_user")
        for i in range(4):
            test_proj.append(
                TestFactory.create_project(self.test_user if i < 3 else user1,
                                           'test_proj' + str(i)))

        project_storage = self.structure_helper.get_project_folder(
            test_proj[0])
        result_meta = {
            DataTypeMetaData.KEY_OPERATION_TYPE: "Upload",
            DataTypeMetaData.KEY_AUTHOR: "John Doe",
            DataTypeMetaData.KEY_SUBJECT: "subj1",
            DataTypeMetaData.KEY_STATE: "test_state",
            DataTypeMetaData.KEY_NODE_TYPE: "test_data",
            DataTypeMetaData.KEY_DATE: "test_date",
            DataTypeMetaData.KEY_GID: generate_guid()
        }

        entity = dao.store_entity(model.AlgorithmCategory("category"))
        entity = dao.store_entity(
            model.AlgorithmGroup("module", "classname", entity.id))
        entity = dao.store_entity(model.Algorithm(entity.id, "algo"))
        operation = model.Operation(self.test_user.id, test_proj[0].id,
                                    entity.id, "")
        operation = dao.store_entity(operation)
        project_storage = os.path.join(project_storage, str(operation.id))
        os.makedirs(project_storage)
        entity = DataTypeMetaData(result_meta)
        datatype = dao.store_entity(
            model.DataType(module="test_data",
                           subject="subj1",
                           state="test_state",
                           operation_id=operation.id))
        linkable = self.project_service.get_linkable_projects_for_user(
            self.test_user.id, str(datatype.id))[0]
        self.assertEqual(len(linkable), 2,
                         "Wrong count of link-able projects!")
        proj_names = [project.name for project in linkable]
        self.assertTrue(test_proj[1].name in proj_names)
        self.assertTrue(test_proj[2].name in proj_names)
        self.assertFalse(test_proj[3].name in proj_names)

    def test_remove_project_happy_flow(self):
        """
        Standard flow for deleting a project.
        """
        inserted_project = TestFactory.create_project(self.test_user,
                                                      'test_proj')
        project_root = self.structure_helper.get_project_folder(
            inserted_project)
        projects = dao.get_projects_for_user(self.test_user.id)
        self.assertEqual(len(projects), 1, "Initializations failed!")
        self.assertTrue(os.path.exists(project_root),
                        "Something failed at insert time!")
        self.project_service.remove_project(inserted_project.id)
        projects = dao.get_projects_for_user(self.test_user.id)
        self.assertEqual(len(projects), 0, "Project was not deleted!")
        self.assertFalse(os.path.exists(project_root),
                         "Root folder not deleted!")

    def test_remove_project_wrong_id(self):
        """
        Flow for deleting a project giving an un-existing id.
        """
        TestFactory.create_project(self.test_user, 'test_proj')
        projects = dao.get_projects_for_user(self.test_user.id)
        self.assertEqual(len(projects), 1, "Initializations failed!")
        self.assertRaises(ProjectServiceException,
                          self.project_service.remove_project, 99)

    @staticmethod
    def _create_value_wrapper(test_user, test_project=None):
        """
        Creates a ValueWrapper dataType, and the associated parent Operation.
        This is also used in ProjectStructureTest.
        """
        if test_project is None:
            test_project = TestFactory.create_project(test_user, 'test_proj')
        operation = TestFactory.create_operation(test_user=test_user,
                                                 test_project=test_project)
        value_wrapper = ValueWrapper(data_value=5.0, data_name="my_value")
        value_wrapper.type = "ValueWrapper"
        value_wrapper.module = "tvb.datatypes.mapped_values"
        value_wrapper.subject = "John Doe"
        value_wrapper.state = "RAW_STATE"
        value_wrapper.set_operation_id(operation.id)
        adapter_instance = StoreAdapter([value_wrapper])
        OperationService().initiate_prelaunch(operation, adapter_instance, {})
        all_value_wrappers = FlowService().get_available_datatypes(
            test_project.id, "tvb.datatypes.mapped_values.ValueWrapper")
        if len(all_value_wrappers) != 1:
            raise Exception("Should be only one value wrapper.")
        result_vw = ABCAdapter.load_entity_by_gid(all_value_wrappers[0][2])
        return test_project, result_vw.gid, operation.gid

    def __check_meta_data(self, expected_meta_data, new_datatype):
        """Validate Meta-Data"""
        mapp_keys = {
            DataTypeMetaData.KEY_SUBJECT: "subject",
            DataTypeMetaData.KEY_STATE: "state"
        }
        for key, value in expected_meta_data.iteritems():
            if key in mapp_keys:
                self.assertEqual(value, getattr(new_datatype, mapp_keys[key]))
            elif key == DataTypeMetaData.KEY_OPERATION_TAG:
                if DataTypeMetaData.KEY_OP_GROUP_ID in expected_meta_data:
                    ## We have a Group to check
                    op_group = new_datatype.parent_operation.fk_operation_group
                    op_group = dao.get_generic_entity(OperationGroup,
                                                      op_group)[0]
                    self.assertEqual(value, op_group.name)
                else:
                    self.assertEqual(value,
                                     new_datatype.parent_operation.user_group)

    def test_remove_project_node(self):
        """
        Test removing of a node from a project.
        """
        inserted_project, gid, gid_op = self._create_value_wrapper(
            self.test_user)
        project_to_link = model.Project("Link", self.test_user.id, "descript")
        project_to_link = dao.store_entity(project_to_link)
        exact_data = dao.get_datatype_by_gid(gid)
        dao.store_entity(model.Links(exact_data.id, project_to_link.id))
        self.assertTrue(
            dao.get_datatype_by_gid(gid) is not None,
            "Initialization problem!")

        operation_id = dao.get_generic_entity(model.Operation, gid_op,
                                              'gid')[0].id
        op_folder = self.structure_helper.get_project_folder(
            "test_proj", str(operation_id))
        self.assertTrue(os.path.exists(op_folder))
        sub_files = os.listdir(op_folder)
        self.assertEqual(2, len(sub_files))
        ### Validate that no more files are created than needed.

        self.project_service._remove_project_node_files(
            inserted_project.id, gid)
        sub_files = os.listdir(op_folder)
        self.assertEqual(1, len(sub_files))
        ### Validate that Operation GID_file is still there.

        op_folder = self.structure_helper.get_project_folder(
            "Link", str(operation_id + 1))
        sub_files = os.listdir(op_folder)
        self.assertEqual(1, len(sub_files))
        self.assertTrue(
            dao.get_datatype_by_gid(gid) is not None,
            "Data should still be there because of links")
        self.project_service._remove_project_node_files(
            project_to_link.id, gid)
        self.assertTrue(dao.get_datatype_by_gid(gid) is None)
        sub_files = os.listdir(op_folder)
        self.assertEqual(0, len(sub_files))

    def test_update_meta_data_simple(self):
        """
        Test the new update metaData for a simple data that is not part of a group.
        """
        inserted_project, gid, _ = self._create_value_wrapper(self.test_user)
        new_meta_data = {
            DataTypeOverlayDetails.DATA_SUBJECT: "new subject",
            DataTypeOverlayDetails.DATA_STATE: "second_state",
            DataTypeOverlayDetails.CODE_GID: gid,
            DataTypeOverlayDetails.CODE_OPERATION_TAG: 'new user group'
        }
        self.project_service.update_metadata(new_meta_data)

        new_datatype = dao.get_datatype_by_gid(gid)
        self.__check_meta_data(new_meta_data, new_datatype)

        op_path = FilesHelper().get_operation_meta_file_path(
            inserted_project.name, new_datatype.parent_operation.id)
        op_meta = XMLReader(op_path).read_metadata()
        self.assertEqual(op_meta['user_group'], 'new user group',
                         'UserGroup not updated!')

    def test_update_meta_data_group(self):
        """
        Test the new update metaData for a group of dataTypes.
        """
        datatypes, group_id = TestFactory.create_group(
            self.test_user, subject="test-subject-1")

        new_meta_data = {
            DataTypeOverlayDetails.DATA_SUBJECT: "new subject",
            DataTypeOverlayDetails.DATA_STATE: "updated_state",
            DataTypeOverlayDetails.CODE_OPERATION_GROUP_ID: group_id,
            DataTypeOverlayDetails.CODE_OPERATION_TAG: 'newGroupName'
        }
        self.project_service.update_metadata(new_meta_data)

        for datatype in datatypes:
            new_datatype = dao.get_datatype_by_id(datatype.id)
            self.assertEqual(group_id,
                             new_datatype.parent_operation.fk_operation_group)
            new_group = dao.get_generic_entity(model.OperationGroup,
                                               group_id)[0]
            self.assertEqual(new_group.name, "newGroupName")
            self.__check_meta_data(new_meta_data, new_datatype)

    def _create_datatypes(self, dt_factory, nr_of_dts):
        for idx in range(nr_of_dts):
            dt = Datatype1()
            dt.row1 = "value%i" % (idx, )
            dt.row2 = "value%i" % (idx + 1, )
            dt_factory._store_datatype(dt)

    def test_retrieve_project_full(self):
        """
        Tests full project information is retrieved by method `ProjectService.retrieve_project_full(...)`
        """
        dt_factory = datatypes_factory.DatatypesFactory()
        self._create_datatypes(dt_factory, 3)
        _, ops_nr, _, operations, pages_no = self.project_service.retrieve_project_full(
            dt_factory.project.id)
        self.assertEqual(
            ops_nr, 1,
            "DataType Factory should only use one operation to store all it's datatypes."
        )
        self.assertEqual(
            pages_no, 1,
            "DataType Factory should only use one operation to store all it's datatypes."
        )
        resulted_dts = operations[0]['results']
        self.assertEqual(len(resulted_dts), 3,
                         "3 datatypes should be created.")

    def test_get_project_structure(self):
        """
        Tests project structure is as expected and contains all datatypes
        """
        dt_factory = datatypes_factory.DatatypesFactory()
        self._create_datatypes(dt_factory, 3)
        node_json = self.project_service.get_project_structure(
            dt_factory.project, None, 'Data_State', 'Data_Subject', None)
        encoder = JSONEncoder()
        encoder.iterencode(node_json)
        # No exceptions were raised so far.
        project_dts = dao.get_datatypes_for_project(dt_factory.project.id)
        for dt in project_dts:
            self.assertTrue(
                dt.gid in node_json,
                "Should have all datatypes present in resulting json.")