Exemplo n.º 1
0
    def get(self, request, project_id=None, name=None, uuid=None):
        """

        :return:
        :rtype: JsonResponse
        """
        ag = request.user.agave_oauth.client
        try:
            if name is not None and name != 'all':
                model = project_lookup_model(name=name)
                resp = model._meta.model_manager.list(ag, project_id)
                resp_list = [r.to_body_dict() for r in resp]
                resp_list = sorted(resp_list, key=lambda x: x['created'])
                return JsonResponse(resp_list, safe=False)
            elif name == 'all':
                prj_obj = ag.meta.getMetadata(uuid=project_id)
                prj = project_lookup_model(prj_obj)(**prj_obj)
                prj.manager().set_client(ag)
                resp_list = [
                    ent.to_body_dict() for ent in prj.related_entities()
                ]
                return JsonResponse(resp_list, safe=False)
            elif uuid is not None:
                meta = ag.meta.getMetadata(uuid=uuid)
                model = project_lookup_model(meta)
                resp = model(**meta)
                return JsonResponse(resp.to_body_dict(), safe=False)
        except ValueError:
            return HttpResponseBadRequest('Entity not valid.')
Exemplo n.º 2
0
    def put(self, request, uuid):
        """
        Update Project Related Metadata
        This should create a new entity related to a project

        :param request:
        :return:
        """
        client = request.user.agave_oauth.client

        if request.is_ajax():
            post_data = json.loads(request.body)
        else:
            post_data = request.POST.copy()
        entity = post_data.get('entity')

        try:
            model_cls = project_lookup_model(entity)
            model = model_cls(**entity)
            saved = model.save(client)
            resp = model_cls(**saved)
        except ValueError:
            return HttpResponseBadRequest('Entity not valid.')

        return JsonResponse(resp.to_body_dict(), safe=False)
Exemplo n.º 3
0
    def post(self, request, project_id):
        """

        :param request:
        :return:
        """
        ag = get_service_account_client()

        if request.is_ajax():
            post_data = json.loads(request.body)
        else:
            post_data = request.POST.copy()

        meta_obj = ag.meta.getMetadata(uuid=project_id)
        cls = project_lookup_model(meta_obj)
        logger.info('post_data: %s', post_data)
        p = cls(value=post_data, uuid=project_id)

        new_pi = post_data.get('pi', p.pi)
        new_co_pis = post_data.get('coPis', p.co_pis)
        new_team_members = post_data.get('teamMembers', p.team_members)

        # we need to compare the existing project data with the updated project data
        if new_pi and new_pi != 'null' and meta_obj.value['pi'] != new_pi:
            names = list(
                set(meta_obj.value['teamMembers'] + meta_obj.value['coPis'] +
                    [meta_obj.value['pi']]))
            updatednames = list(set(new_team_members + new_co_pis + [new_pi]))
        else:
            names = list(
                set(meta_obj.value['teamMembers'] + meta_obj.value['coPis']))
            updatednames = list(set(new_team_members + new_co_pis))

        add_perm_usrs = [u for u in updatednames if u not in names]
        rm_perm_usrs = [u for u in names if u not in updatednames]

        # remove permissions for users not on project and add permissions for new members
        p.manager().set_client(ag)
        if rm_perm_usrs:
            if p.pi not in rm_perm_usrs:
                p._remove_team_members_pems(rm_perm_usrs)
        if add_perm_usrs:
            p._add_team_members_pems(add_perm_usrs)

        tasks.check_project_files_meta_pems.apply_async(args=[p.uuid],
                                                        queue='api')
        tasks.set_facl_project.apply_async(args=[p.uuid, add_perm_usrs],
                                           queue='api')
        tasks.email_collaborator_added_to_project.apply_async(args=[
            p.project_id, p.uuid, p.title,
            request.build_absolute_uri('{}/projects/{}/'.format(
                reverse('designsafe_data:data_depot'), p.uuid)), add_perm_usrs,
            []
        ])

        p.save(ag)
        return JsonResponse(p.to_body_dict())
Exemplo n.º 4
0
    def post(self, request, project_id):
        """

        :param request:
        :return:
        """
        ag = get_service_account_client()

        if request.is_ajax():
            post_data = json.loads(request.body)
        else:
            post_data = request.POST.copy()

        meta_obj = ag.meta.getMetadata(uuid=project_id)
        cls = project_lookup_model(meta_obj)
        logger.info('post_data: %s', post_data)
        p = cls(value=post_data, uuid=project_id)

        new_pi = post_data.get('pi', p.pi)
        new_co_pis = post_data.get('copi', p.co_pis)
        new_team_members = post_data.get('teamMembers', p.team_members)

        if new_pi and new_pi != 'null' and p.pi != new_pi:
            names = list(set(p.team_members + p.co_pis + [p.pi]))
            updatednames = list(set(new_team_members + new_co_pis + [new_pi]))
            p.pi = new_pi
        else:
            names = list(set(p.team_members + p.co_pis))
            updatednames = list(set(new_team_members + new_co_pis))

        add_perm_usrs = [u for u in updatednames if u not in names]
        rm_perm_usrs = [u for u in names if u not in updatednames]

        # set new members on project metadata
        p.co_pis = new_co_pis
        p.team_members = new_team_members

        # remove permissions for users not on project and add permissions for new members
        if rm_perm_usrs:
            if p.pi not in rm_perm_usrs:
                p._remove_team_members_pems(rm_perm_usrs)
        if add_perm_usrs:
            p._add_team_members_pems(add_perm_usrs)

        tasks.check_project_files_meta_pems.apply_async(args=[p.uuid],
                                                        queue='api')
        tasks.set_facl_project.apply_async(args=[p.project_id, add_perm_usrs],
                                           queue='api')
        tasks.email_collaborator_added_to_project.apply_async(
            args=[p.title, add_perm_usrs, []])

        p.save(ag)
        return JsonResponse(p.to_body_dict())
Exemplo n.º 5
0
    def get(self, request, project_id):
        """

        :return:
        :rtype: JsonResponse
        """
        ag = request.user.agave_oauth.client
        #project = Project.from_uuid(agave_client=ag, uuid=project_id)
        meta_obj = ag.meta.getMetadata(uuid=project_id)
        cls = project_lookup_model(meta_obj)
        project = cls(**meta_obj)
        return JsonResponse(project.to_body_dict(), safe=False)
Exemplo n.º 6
0
    def delete(self, request, uuid):
        """

        :return:
        :rtype: JsonResponse
        """
        ag = get_service_account_client()
        meta_obj = ag.meta.getMetadata(uuid=uuid)
        model = project_lookup_model(meta_obj)
        meta = model(**meta_obj)
        ag.meta.deleteMetadata(uuid=uuid)
        return JsonResponse(meta.to_body_dict(), safe=False)
Exemplo n.º 7
0
    def get_entity_by_uuid(self, entity_uuid):
        """Get entity by uuid.

        Returns an entity using the correct entity class based on a uuid.

        :param str entity_uuid: Entity uuid.
        :return: Entity instance.
        """
        entity_json = self._ac.meta.getMetadata(uuid=entity_uuid)
        entity_cls = project_lookup_model(entity_json)
        entity = entity_cls(**entity_json)
        entity.manager().set_client(self._ac)
        return entity
Exemplo n.º 8
0
    def get(self, request, project_id):
        """

        :return:
        :rtype: JsonResponse
        """
        client = request.user.agave_oauth.client
        meta_obj = client.meta.getMetadata(uuid=project_id)
        cls = project_lookup_model(meta_obj)
        project = cls(**meta_obj)
        project_dict = project.to_body_dict()

        return JsonResponse(project_dict)
Exemplo n.º 9
0
    def post(self, request, project_id, name):
        """

        :param request:
        :return:
        """
        ag = get_service_account_client()

        if request.is_ajax():
            post_data = json.loads(request.body)
        else:
            post_data = request.POST.copy()
        entity = post_data.get('entity')
        try:
            model_cls = project_lookup_model(name=name)
            model = model_cls(value=entity)
            file_uuids = []
            if 'filePaths' in entity:
                file_paths = entity.get('filePaths', [])
                project_system = ''.join(['project-', project_id])
                user_ag = request.user.agave_oauth.client
                for file_path in file_paths:
                    file_obj = BaseFileResource.listing(
                        user_ag, project_system, file_path)

                    file_uuids.append(file_obj.uuid)
                for file_uuid in file_uuids:
                    model.files.add(file_uuid)
                model.associate(file_uuids)
            model.project.add(project_id)
            model.associate(project_id)
            saved = model.save(ag)
            resp = model_cls(**saved)
            #TODO: This should happen in a celery task and implemented in a manager
            #Get project's metadata permissions
            pems = BaseMetadataPermissionResource.list_permissions(
                project_id, ag)
            #Loop permissions and set them in whatever metadata object we're saving
            for pem in pems:
                _pem = BaseMetadataPermissionResource(resp.uuid, ag)
                _pem.username = pem.username
                _pem.read = pem.read
                _pem.write = pem.write
                _pem.save()

        except ValueError:
            return HttpResponseBadRequest('Entity not valid.')

        return JsonResponse(resp.to_body_dict(), safe=False)
Exemplo n.º 10
0
    def post(self, request, project_id, name):
        """
        Create project related metadata object.

        :param request:
        :return:
        """
        sa_client = get_service_account_client()

        if request.is_ajax():
            post_data = json.loads(request.body)
        else:
            post_data = request.POST.copy()
        entity = post_data.get('entity')
        try:
            model_cls = project_lookup_model(name=name)
            model = model_cls(value=entity)
            file_uuids = []
            if 'filePaths' in entity:
                file_paths = entity.get('filePaths', [])
                project_system = ''.join(['project-', project_id])
                client = request.user.agave_oauth.client
                for file_path in file_paths:
                    file_obj = BaseFileResource.listing(
                        client, project_system, file_path)

                    file_uuids.append(file_obj.uuid)
                for file_uuid in file_uuids:
                    model.files.add(file_uuid)
                model.associate(file_uuids)
            model.project.add(project_id)
            model.associate(project_id)
            saved = model.save(sa_client)
            resp = model_cls(**saved)
            # TODO: We should stop using these "Resources" and just use agavepy methods.
            pems = BaseMetadataPermissionResource.list_permissions(
                project_id, sa_client)
            # Loop permissions and set them in whatever metadata object we're saving
            for pem in pems:
                _pem = BaseMetadataPermissionResource(resp.uuid, sa_client)
                _pem.username = pem.username
                _pem.read = pem.read
                _pem.write = pem.write
                _pem.save()

        except ValueError:
            return HttpResponseBadRequest('Entity not valid.')

        return JsonResponse(resp.to_body_dict(), safe=False)
Exemplo n.º 11
0
    def get(self, request, project_id):
        """

        :return:
        :rtype: JsonResponse
        """
        ag = request.user.agave_oauth.client
        #project = Project.from_uuid(agave_client=ag, uuid=project_id)
        meta_obj = ag.meta.getMetadata(uuid=project_id)
        cls = project_lookup_model(meta_obj)
        project = cls(**meta_obj)
        project_dict = project.to_body_dict()

        # serialization can change the PI order
        try:
            project_dict['value']['coPis'] = meta_obj['value']['coPis']
        except KeyError:
            pass
        return JsonResponse(project_dict)
Exemplo n.º 12
0
    def delete(self, request, uuid):
        """
        Delete metadata. This will delete associated metadata objects.
        This should not be used to delete a project (only metadata related
        to a project).

        :return:
        :rtype: JsonResponse
        """
        try:
            client = request.user.agave_oauth.client
            meta_obj = client.meta.getMetadata(uuid=uuid)
            model = project_lookup_model(meta_obj)
            meta = model(**meta_obj)
        except Exception as e:
            logger.exception('Unable to delete project metadata: %s', e)
            logger.exception('Meta UUID: %s', uuid)

        sa_client = get_service_account_client()
        sa_client.meta.deleteMetadata(uuid=uuid)
        return JsonResponse(meta.to_body_dict(), safe=False)
Exemplo n.º 13
0
    def get_project_by_id(self, project_id):
        """Get project by ID.

        Returns a project instance using the correct project class based
        on a project id.

        :param str project_id: Project id.
        :return: Project instance.
        """
        metas = self._ac.meta.listMetadata(
            q=json.dumps({"value.projectId": project_id}))
        assert metas, "No project with id: {project_id} found.".format(
            project_id=project_id)
        if len(metas) > 1:
            LOG.info(
                "More than one record with project id: %(project_id)s found."
                "Using only the first one", {project_id: project_id})

        prj_json = metas[0]
        prj_cls = project_lookup_model(prj_json)
        prj = prj_cls(**prj_json)
        prj.manager().set_client(self._ac)
        return prj
Exemplo n.º 14
0
    def put(self, request, uuid):
        """

        :param request:
        :return:
        """
        ag = get_service_account_client()

        if request.is_ajax():
            post_data = json.loads(request.body)
        else:
            post_data = request.POST.copy()
        entity = post_data.get('entity')
        category = Category.objects.get_or_create_from_json(
            uuid=entity['uuid'], dict_obj=entity['_ui'])
        try:
            model_cls = project_lookup_model(entity)
            model = model_cls(**entity)
            saved = model.save(ag)
            resp = model_cls(**saved)
        except ValueError:
            return HttpResponseBadRequest('Entity not valid.')

        return JsonResponse(resp.to_body_dict(), safe=False)
Exemplo n.º 15
0
    def post(self, request, project_id):
        """
        Update a Project. Projects and the root File directory for a Project should
        be owned by the portal, with roles/permissions granted to the creating user.

        1. Get the metadata record for the project
        2. Get the metadata permissions for the project
        3. Update the metadata record with changes from the post data and initilize the
           appropriate project class.
        4. Use the metadata permissions and the metadata record to determine which users
           to add and/or remove from the project
        5. Update file metadata permissions
        6. Set ACLs on the project
        7. Email users who have been added to the project

        :param request: 
        :return:
        """
        if request.is_ajax():
            post_data = json.loads(request.body)
        else:
            post_data = request.POST.copy()

        client = request.user.agave_oauth.client
        sa_client = get_service_account_client(
        )  # service account for updating user permissions

        meta_obj = client.meta.getMetadata(uuid=project_id)
        meta_perms = client.meta.listMetadataPermissions(uuid=project_id)

        # update the meta_obj
        for key, value in post_data.items():
            camel_key = to_camel_case(key)
            meta_obj.value[camel_key] = value

        # get users to add/remove
        admins = ['ds_admin', 'prjadmin']
        users_with_access = [x['username'] for x in meta_perms]
        updated_users = list(
            set(meta_obj['value']['teamMembers'] + meta_obj['value']['coPis'] +
                [meta_obj['value']['pi']]))
        add_perm_usrs = [
            u for u in updated_users + admins if u not in users_with_access
        ]
        rm_perm_usrs = [
            u for u in users_with_access if u not in updated_users + admins
        ]

        prj_class = project_lookup_model(meta_obj)
        project = prj_class(value=meta_obj.value, uuid=project_id)
        project.manager().set_client(sa_client)

        try:
            ds_user = get_user_model().objects.get(username=project.pi)
        except:
            return HttpResponseBadRequest('Project update requires a valid PI')

        # remove permissions for users not on project and add permissions for new members
        if rm_perm_usrs:
            project._remove_team_members_pems(rm_perm_usrs)
        if add_perm_usrs:
            project._add_team_members_pems(add_perm_usrs)

        tasks.check_project_files_meta_pems.apply_async(args=[project.uuid],
                                                        queue='api')
        tasks.set_facl_project.apply_async(args=[project.uuid, add_perm_usrs],
                                           queue='api')
        tasks.email_collaborator_added_to_project.apply_async(args=[
            project.project_id, project.uuid, project.title,
            request.build_absolute_uri('{}/projects/{}/'.format(
                reverse('designsafe_data:data_depot'), project.uuid)),
            add_perm_usrs, []
        ])
        project.save(client)
        return JsonResponse(project.to_body_dict())
Exemplo n.º 16
0
    def post(self, request):
        """
        Create a new Project. Projects and the root File directory for a Project should
        be owned by the portal, with roles/permissions granted to the creating user.

        1. Create the metadata record for the project
        2. Create a directory on the projects storage system named after the metadata uuid
        3. Associate the metadata uuid and file uuid

        :param request:
        :return: The newly created project
        :rtype: JsonResponse
        """

        # portal service account needs to create the objects on behalf of the user
        ag = get_service_account_client()

        if request.is_ajax():
            post_data = json.loads(request.body)
        else:
            post_data = request.POST.copy()

        # create Project (metadata)
        metrics.info('projects',
                     extra={
                         'user': request.user.username,
                         'sessionId': getattr(request.session, 'session_key',
                                              ''),
                         'operation': 'project_create',
                         'info': {
                             'postData': post_data
                         }
                     })
        prj_model = project_lookup_model({
            'name': 'designsafe.project',
            'value': post_data
        })
        prj = prj_model(value=post_data)
        project_uuid = prj.uuid
        prj.manager().set_client(ag)
        prj.save(ag)

        # create Project Directory on Managed system
        metrics.info('projects',
                     extra={
                         'user': request.user.username,
                         'sessionId': getattr(request.session, 'session_key',
                                              ''),
                         'operation': 'base_directory_create',
                         'info': {
                             'systemId': Project.STORAGE_SYSTEM_ID,
                             'uuid': prj.uuid
                         }
                     })
        project_storage_root = BaseFileResource(ag, Project.STORAGE_SYSTEM_ID,
                                                '/')
        project_storage_root.mkdir(prj.uuid)

        # Wrap Project Directory as private system for project
        project_system_tmpl = template_project_storage_system(prj)
        project_system_tmpl['storage']['rootDir'] = \
            project_system_tmpl['storage']['rootDir'].format(project_uuid)
        metrics.info('projects',
                     extra={
                         'user': request.user.username,
                         'sessionId': getattr(request.session, 'session_key',
                                              ''),
                         'operation': 'private_system_create',
                         'info': {
                             'id':
                             project_system_tmpl.get('id'),
                             'site':
                             project_system_tmpl.get('site'),
                             'default':
                             project_system_tmpl.get('default'),
                             'status':
                             project_system_tmpl.get('status'),
                             'description':
                             project_system_tmpl.get('description'),
                             'name':
                             project_system_tmpl.get('name'),
                             'globalDefault':
                             project_system_tmpl.get('globalDefault'),
                             'available':
                             project_system_tmpl.get('available'),
                             'public':
                             project_system_tmpl.get('public'),
                             'type':
                             project_system_tmpl.get('type'),
                             'storage': {
                                 'homeDir':
                                 project_system_tmpl.get('storage',
                                                         {}).get('homeDir'),
                                 'rootDir':
                                 project_system_tmpl.get('storage',
                                                         {}).get('rootDir')
                             }
                         }
                     })
        ag.systems.add(body=project_system_tmpl)

        # grant initial permissions for creating user and PI, if exists
        metrics.info('projects',
                     extra={
                         'user': request.user.username,
                         'sessionId': getattr(request.session, 'session_key',
                                              ''),
                         'operation': 'initial_pems_create',
                         'info': {
                             'collab': request.user.username,
                             'pi': prj.pi
                         }
                     })

        if getattr(prj, 'copi', None):
            prj.add_co_pis(prj.copi)
        elif getattr(prj, 'co_pis', None):
            prj.add_co_pis(prj.co_pis)
        if getattr(prj, 'team', None):
            prj.add_team_members(prj.team)
        elif getattr(prj, 'team_members', None):
            prj.add_team_members(prj.team_members)

        prj._add_team_members_pems([prj.pi])

        if request.user.username not in list(
                set(prj.co_pis + prj.team_members + [prj.pi])):
            # Add creator to project as team member
            prj.add_team_members([request.user.username])

        # Email collaborators
        chain(
            tasks.set_project_id.s(prj.uuid).set(queue="api")
            | tasks.email_collaborator_added_to_project.s(
                prj.uuid, prj.title,
                request.build_absolute_uri('{}/projects/{}/'.format(
                    reverse('designsafe_data:data_depot'), prj.uuid)),
                [
                    u
                    for u in list(set(prj.co_pis + prj.team_members +
                                      [prj.pi])) if u != request.user.username
                ], [])).apply_async()

        tasks.set_facl_project.apply_async(args=[
            prj.uuid,
            list(set(prj.co_pis + prj.team_members + [prj.pi]))
        ],
                                           queue='api')

        prj.add_admin('prjadmin')
        return JsonResponse(prj.to_body_dict(), safe=False)
Exemplo n.º 17
0
    def post(self, request):
        """
        Create a new Project. Projects and the root File directory for a Project should
        be owned by the portal, with roles/permissions granted to the creating user.

        1. Create the metadata record for the project
        2. Create a directory on the projects storage system named after the metadata uuid
        3. Associate the metadata uuid and file uuid

        :param request:
        :return: The newly created project
        :rtype: JsonResponse
        """

        # portal service account needs to create the objects on behalf of the user
        ag = get_service_account_client()

        if request.is_ajax():
            post_data = json.loads(request.body)
        else:
            post_data = request.POST.copy()

        # create Project (metadata)
        metrics.info('projects',
                     extra={
                         'user': request.user.username,
                         'sessionId': getattr(request.session, 'session_key',
                                              ''),
                         'operation': 'project_create',
                         'info': {
                             'postData': post_data
                         }
                     })
        prj_model = project_lookup_model({
            'name': 'designsafe.project',
            'value': post_data
        })
        prj = prj_model(value=post_data)
        project_uuid = prj.uuid
        prj.manager().set_client(ag)
        prj.save(ag)

        # create Project Directory on Managed system
        metrics.info('projects',
                     extra={
                         'user': request.user.username,
                         'sessionId': getattr(request.session, 'session_key',
                                              ''),
                         'operation': 'base_directory_create',
                         'info': {
                             'systemId': Project.STORAGE_SYSTEM_ID,
                             'uuid': prj.uuid
                         }
                     })
        project_storage_root = BaseFileResource(ag, Project.STORAGE_SYSTEM_ID,
                                                '/')
        project_storage_root.mkdir(prj.uuid)

        # Wrap Project Directory as private system for project
        project_system_tmpl = template_project_storage_system(prj)
        project_system_tmpl['storage']['rootDir'] = \
            project_system_tmpl['storage']['rootDir'].format(project_uuid)
        metrics.info('projects',
                     extra={
                         'user': request.user.username,
                         'sessionId': getattr(request.session, 'session_key',
                                              ''),
                         'operation': 'private_system_create',
                         'info': {
                             'id':
                             project_system_tmpl.get('id'),
                             'site':
                             project_system_tmpl.get('site'),
                             'default':
                             project_system_tmpl.get('default'),
                             'status':
                             project_system_tmpl.get('status'),
                             'description':
                             project_system_tmpl.get('description'),
                             'name':
                             project_system_tmpl.get('name'),
                             'globalDefault':
                             project_system_tmpl.get('globalDefault'),
                             'available':
                             project_system_tmpl.get('available'),
                             'public':
                             project_system_tmpl.get('public'),
                             'type':
                             project_system_tmpl.get('type'),
                             'storage': {
                                 'homeDir':
                                 project_system_tmpl.get('storage',
                                                         {}).get('homeDir'),
                                 'rootDir':
                                 project_system_tmpl.get('storage',
                                                         {}).get('rootDir')
                             }
                         }
                     })
        ag.systems.add(body=project_system_tmpl)

        # grant initial permissions for creating user and PI, if exists
        metrics.info('projects',
                     extra={
                         'user': request.user.username,
                         'sessionId': getattr(request.session, 'session_key',
                                              ''),
                         'operation': 'initial_pems_create',
                         'info': {
                             'collab': request.user.username,
                             'pi': prj.pi
                         }
                     })
        prj.add_team_members([request.user.username])
        if getattr(prj, 'copi', None):
            prj.add_co_pis(prj.copi)
        elif getattr(prj, 'co_pis', None):
            prj.add_co_pis(prj.co_pis)
        if getattr(prj, 'team', None):
            prj.add_team_members(prj.team)
        elif getattr(prj, 'team_members', None):
            prj.add_team_members(prj.team_members)

        tasks.set_facl_project.apply_async(
            args=[prj.uuid, [request.user.username]], queue='api')
        if prj.pi and prj.pi != request.user.username:
            tasks.set_facl_project.apply_async(args=[prj.uuid, [prj.pi]],
                                               queue='api')
            collab_users = get_user_model().objects.filter(username=prj.pi)
            if collab_users:
                collab_user = collab_users[0]
                try:
                    collab_user.profile.send_mail(
                        "[Designsafe-CI] You have been added to a project!",
                        "<p>You have been added to the project <em> {title} </em> as PI</p><p>You can visit the project using this url <a href=\"{url}\">{url}</a>"
                        .format(title=prj.title,
                                url=request.build_absolute_uri(
                                    reverse('designsafe_data:data_depot') +
                                    '/projects/%s/' % (prj.uuid, ))))
                except DesignSafeProfile.DoesNotExist as err:
                    logger.info("Could not send email to user %s", collab_user)
                    body = "<p>You have been added to the project <em> {title} </em> as PI</p><p>You can visit the project using this url <a href=\"{url}\">{url}</a>".format(
                        title=prj.title,
                        url=request.build_absolute_uri(
                            reverse('designsafe_data:data_depot') +
                            '/projects/%s/' % (prj.uuid, )))
                    send_mail(
                        "[Designsafe-CI] You have been added to a project!",
                        body,
                        settings.DEFAULT_FROM_EMAIL, [collab_user.email],
                        html_message=body)
        prj.add_admin('prjadmin')
        tasks.set_project_id.apply_async(args=[prj.uuid], queue="api")
        return JsonResponse(prj.to_body_dict(), safe=False)