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.')
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)
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())
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())
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)
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)
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
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)
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)
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)
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)
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)
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
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)
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())
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)
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)