def sample_in_sample_group(user: User, sample: Sample, sample_group: SampleGroup) -> bool: """ Determine if a sample belongs to a sample group. NotFoundException, :param user: :param sample: :param sample_group: :return: """ if is_read_permitted(user, sample_group) and is_read_permitted(user, sample): return sample in sample_group.samples raise AuthException(f'User {user.email} not permitted to check the attachment of {sample.id} to sample group {sample_group.id}')
def create_collection(user: User, samples: List[Sample], data: Dict[str, Any], sort_by: str = 'base_sample_id') -> Collection: """ Create a new collection by concatenating samples. Collection metadata is set with new_data :param user: :param samples: :param data: Collection attributes :param sort_by: :return: """ data['owner_id'] = user.id data['creator_id'] = user.id if 'id' in data: # cannot create with specified id del data['id'] for sample in samples: if not is_read_permitted(user, sample): raise AuthException(f'User {user.id} is not permitted to access sample {sample.id}') filenames = [sample.filename for sample in samples] new_collection = Collection(owner=user, creator=user, last_editor=user, name=data['name']) db.session.add(new_collection) db.session.commit() new_collection.filename = f'{DATADIR}/collections/{new_collection.id}.h5' db.session.commit() if len(filenames): new_collection.merge_samples(samples, sort_by) else: new_collection.create_empty_file() update_collection(user, new_collection, data) return new_collection
def attach_collection(user: User, analysis: Analysis, collection: Collection) -> Dict[str, Any]: """ Add a collection to the list of collections belonging to an analysis :param user: :param analysis: :param collection: :return: """ # check read permissions on analysis and collection if is_read_permitted(user, collection) and is_write_permitted( user, analysis): if collection not in analysis.collections: analysis.collections.append(collection) db.session.commit() return { 'message': f'collection {collection.id} attached to analysis {analysis.id}' } return { 'message': f'Collection {collection.id} already attached to analysis {analysis.id}' } raise AuthException( f'User {user.email} is not permitted to attach collection {collection.id} ' f'to analysis {analysis.id}')
def download_external_file(user: User, external_file: ExternalFile) -> Dict[str, str]: if is_read_permitted(user, external_file): return {'filename': os.path.basename(external_file.filename)} raise AuthException( f'User {user.id} is not permitted to access external file {external_file.id}' )
def list_collection_paths(user: User, collection: Collection) -> List[str]: """ List the paths corresponding to datasets in the collection :param user: :param collection: :return: """ if is_read_permitted(user, collection): return mdt.get_dataset_paths(collection.filename) raise AuthException(f'User {user.email} is not permitted to access collection {collection.id}')
def get_sample_group_members(user: User, sample_group: SampleGroup) -> List[Sample]: """ Get a list of samples which belong to this group. :param user: :param sample_group: :return: """ if is_read_permitted(user, sample_group): return get_read_permitted_records(user, sample_group.samples) raise AuthException(f'User {user.email} not permitted to view sample group {sample_group.id}')
def get_included_sample_groups(user: User, sample: Sample) -> List[SampleGroup]: """ Get a list of sample groups that a sample is found in :param user: :param sample: :return: """ if is_read_permitted(user, sample): return get_read_permitted_records(user, sample.sample_groups) raise AuthException(f'User {user.email} not permitted to view sample {sample.id}')
def download_collection(user: User, collection: Collection) -> Dict[str, str]: """ If the user is permitted to read this collection, get the path to the collection, else throw. The file is sent via the send_from_directory flask method :param user: :param collection: :return: """ if is_read_permitted(user, collection): return {'filename': os.path.basename(collection.filename)} raise AuthException(f'User {user.email} is not permitted to access collection {collection.id}')
def list_sample_paths(user: User, sample: Sample) -> List[str]: """ List all the paths to datasets within the sample file :param user: :param sample: :return: """ if is_read_permitted(user, sample): return mdt.get_dataset_paths(sample.filename) raise AuthException( f'User {user.email} is not permitted to access sample {sample.id}')
def get_analysis(user: User, analysis_id: int) -> Analysis: """ Get analysis information :param user: :param analysis_id: :return: """ analysis = Analysis.query.filter_by(id=analysis_id).first() if is_read_permitted(user, analysis): return analysis raise AuthException( f'User {user.email} is not permitted to access analysis {analysis_id}')
def download_sample(user: User, sample: Sample) -> Dict[str, str]: """ If the user with user_id is permitted to access sample_id, present the filename for the sample with sample_id :param user: :param sample: :return: """ if is_read_permitted(user, sample): return {'filename': f'{sample.id}.h5'} raise AuthException( f'User {user.email} is not permitted to access sample {sample.id}')
def get_attached_collections(user: User, analysis: Analysis) -> List[Collection]: """ Get all collections which belong to an analysis :param user: :param analysis: :return: """ if is_read_permitted(user, analysis): return analysis.collections raise AuthException( f'User {user.email} is not permitted to access analysis {analysis.id}')
def get_sample_group(user: User, group_id: int) -> SampleGroup: """ Get a sample group. :param user: :param group_id: :return: """ sample_group = SampleGroup.query.filter_by(id=group_id).first() if sample_group is None: raise NotFoundException(f'Sample group with id {group_id} not found.') if is_read_permitted(user, sample_group): return sample_group raise AuthException(f'User {user.email} is not authorized to view sample group {group_id}')
def get_attached_analyses(user: User, collection: Collection) -> List[Analysis]: """ Get all analysis that a collection belongs to :param user: :param collection: :return: """ if is_read_permitted(user, collection): return get_all_read_permitted_records(user, collection.analyses) raise AuthException( f'User {user.email} not permitted to access collection {collection.id}' )
def download_collection_dataset(user: User, collection: Collection, path: str) -> Dict[str, str]: """ If the user is allowed to read a collection, get the contents required to send a file containing a dataset as CSV :param user: :param collection: :param path: :return: """ csv_filename = f'{os.path.basename(os.path.normpath(path))}.csv' if is_read_permitted(user, collection): return {'csv': mdt.get_csv(collection.filename, path), 'cd': f'attachment; filename={csv_filename}'} raise AuthException(f'User {user.email} is not permitted to access collection {collection.id}')
def get_collection(user: User, collection_id: int) -> Collection: """ Get the attributes and dataset information of a collection :param user: :param collection_id: :return: """ collection = Collection.query.filter_by(id=collection_id).first() if collection is None: raise NotFoundException(f'No collection with id {collection_id}') if is_read_permitted(user, collection): return collection raise AuthException(f'User {user.email} is not authorized to view collection {collection.id}')
def update_sample_group_attachments(user: User, sample_group: SampleGroup, samples: List[Sample]) -> SampleGroup: """ Make the only samples attached to a group those in sample_ids :param user: :param sample_group: :param samples: :return: """ if is_write_permitted(user, sample_group) and all([is_read_permitted(user, sample) for sample in samples]): sample_group.samples = samples sample_group.last_editor = user db.session.commit() return sample_group raise AuthException(f'User {user.email} not authorized to modify group {sample_group.id}')
def get_workflow(user: User, workflow_id: int) -> Workflow: """ Get workflow metadata. :param user: :param workflow_id: :return: """ workflow = Workflow.query.filter_by(id=workflow_id).first() if workflow is None: raise NotFoundException(f'No workflow with id {workflow_id}.') if is_read_permitted(user, workflow): return workflow raise AuthException( f'User {user.email} is not permitted to access workflow {workflow_id}')
def get_collection_metadata(user: User, collection: Collection) -> Dict[str, Any]: """ Get the attributes of a collection. :param user: :param collection: :return: """ if is_read_permitted(user, collection): collection_info = collection.to_dict() for key, value in collection.get_file_metadata().items(): if key not in collection_info: # ensures that database entries take precedence over file attributes collection_info[key] = value return collection_info raise AuthException(f'User {user.email} is not authorized to view collection {collection.id}')
def get_sample(user: User, sample_id: int) -> Sample: """ Get the attributes and dataset paths of the sample with sample_id :param user: :param sample_id: :return: """ sample = Sample.query.filter_by(id=sample_id).first() if sample is None: raise NotFoundException(f'No sample with id {sample_id}') if is_read_permitted(user, sample): return sample raise AuthException( f'User {user.email} is not permitted to access sample {sample_id}')
def get_external_file(user: User, external_file_id: int) -> ExternalFile: """ Get a record corresponding to an external file :param user: :param external_file_id: :return: """ external_file = ExternalFile.query.filter_by(id=external_file_id).first() if external_file is None: raise NotFoundException(f'No external file with id {external_file_id}') if is_read_permitted(user, external_file): return external_file raise AuthException( f'User {user.id} is not authorized to view external file {external_file.id}' )
def attach_sample(user: User, sample: Sample, sample_group: SampleGroup) -> SampleGroup: """ Make a sample a member of a sample group. :param user: :param sample: :param sample_group: :return: """ if is_write_permitted(user, sample_group) and is_read_permitted(user, sample): if sample not in sample_group.samples: sample_group.samples.append(sample) sample_group.last_editor = user db.session.commit() return sample_group raise AuthException(f'User {user.email} is not permitted to attach {sample.id} to group {sample_group.id}')
def get_external_file_by_path(user: User, filename: str) -> ExternalFile: """ Get the external file record for the file with path filename :param user: :param filename: :return: """ external_file = ExternalFile.query.filter_by(filename=filename).first() if external_file is None: raise NotFoundException( f'No external file record exists for {filename}') if is_read_permitted(user, external_file): return external_file raise AuthException( f'User {user.id} is not authorized to view external file record for {filename}' )
def get_sample_metadata(user: User, sample: Sample) -> Dict[str, Any]: """ Get the attributes of the sample with sample_id :param user: :param sample: :return: """ if is_read_permitted(user, sample): collection_info = sample.to_dict() for key, value in sample.get_file_metadata().items(): if key not in collection_info: # ensures that database entries take precedence over file attributes collection_info[key] = value return collection_info raise AuthException( f'User {user.email} is not authorized to view collection {sample.id}')
def download_collection_dataframe(user: User, collection: Collection, single_column: bool = False, data_format: str = 'csv', json_orient: str = 'records') -> Dict[str, any]: """ If the user is allowed to read a collection, get the contents required to send a file containing the collection as a pandas dataframe as CSV :param user: :param collection: :param single_column: whether to only include single-column attributes in dataframe :param data_format: file format of dataframe :param json_orient: pandas json orientation if json is data_format, otherwise ignored :return: """ if is_read_permitted(user, collection): return {data_format: ct.get_serialized_dataframe(collection.filename, single_column, data_format, json_orient), 'cd': f'attachment; filename={collection.id}.{data_format}'} raise AuthException(f'User {user.email} is not permitted to access collection {collection.id}')
def download_sample_dataset(user: User, sample: Sample, path: str) -> Dict[str, str]: """ Download a CSV file containing a dataset found in this sample :param user: :param sample: :param path: :return: """ csv_filename = f'{os.path.basename(os.path.normpath(path))}.csv' if is_read_permitted(user, sample): return { 'csv': mdt.get_csv(sample.filename, path), 'cd': f'attachment; filename={csv_filename}' } raise AuthException( f'User {user.email} is not permitted to access collection {sample.id}')
def __init__(self, current_user: User, record: Any): super(AttributeTableData, self).__init__(current_user, is_write_permitted(current_user, record)) self.values = { 'ID': AttributeTableRow('id', record.id, False), 'Name': AttributeTableRow('name', record.name, self.editable) } if hasattr(record, 'description'): self.values['Description'] = AttributeTableRow( 'description', record.description if record.description is not None else '', self.editable) if isinstance(record, SampleGroup): job = get_job(record.upload_job_id) self.values['Job'] = AttributeTableRow('job', job.name, href=get_item_link(job)) if isinstance(record, User): self.values['Email'] = AttributeTableRow( 'email', record.email, False, href=f'mailto:{record.email}') if is_read_permitted(current_user, record.primary_user_group): self.values['Primary User Group'] = AttributeTableRow( 'primary_user_group', record.primary_user_group.name if record.primary_user_group is not None else None, False, get_item_link(record.primary_user_group) if record.primary_user_group is not None else None) if isinstance(record, OmicsRecordMixin): if is_read_permitted(current_user, record.owner): self.values['Owner'] = AttributeTableRow( 'owner', record.owner.name if record.owner is not None else None, False, get_item_link(record.owner) if record.owner is not None else None) user_group_options = [ SelectOption(group.id, group.name, group is record.user_group) for group in UserGroup.query.filter( UserGroup.members.contains(current_user)).all() ] self.values['User Group'] = AttributeTableRow( 'user_group_id', record.user_group.name if record.user_group is not None else None, self.editable, None, user_group_options, False) permissions_options = [ SelectOption('all_can_read', 'Anyone can view?', record.all_can_read), SelectOption('group_can_read', 'User group members can view?', record.group_can_read), SelectOption('all_can_write', 'Anyone can edit or delete?', record.all_can_write), SelectOption('group_can_write', 'User group members can edit or delete', record.group_can_write) ] self.values['Permissions'] = AttributeTableRow( 'permissions', None, is_write_permitted(current_user, record), select_options=permissions_options, select_multiple=True, select_composite=True) if isinstance(record, Collection): if record.parent is not None and is_read_permitted( current_user, record.parent): self.values['Parent Collection'] = AttributeTableRow( 'parent', f'{record.parent.name} (Collection {record.parent.id})', href=get_item_link(record.parent)) else: self.values['Parent Collection'] = AttributeTableRow( 'parent', 'None') self.values['Kind'] = AttributeTableRow('kind', record.kind) if isinstance(record, Job): self.values['Type'] = AttributeTableRow('type', record.type) self.values['Status'] = AttributeTableRow('status', record.status) self.values['Submitted'] = AttributeTableRow( 'submitted', record.submission) self.values['Started'] = AttributeTableRow('start', record.start) self.values['Ended'] = AttributeTableRow('end', record.end) if is_read_permitted(current_user, record.owner): self.values['Submitted By'] = AttributeTableRow( 'owner', record.owner.name, href=get_item_link(record.owner)) if isinstance(record, ExternalFile): self.values['Path'] = AttributeTableRow('filename', record.filename) self.values['File Type'] = AttributeTableRow( 'file_type', record.file_type) file_info = record.get_file_info() if file_info is not None: size_mb = file_info['st_size'] / (1024 * 1024.0) self.values['Size'] = AttributeTableRow( 'file_size', f'{size_mb:.3f} MB') self.values['File Modified'] = AttributeTableRow( 'file_mtime', datetime.fromtimestamp( file_info['st_mtime']).strftime('%-d %b %Y %H:%M')) if isinstance(record, Base): self.values['Date Created'] = AttributeTableRow( 'created_on', record.created_on.strftime('%-d %b %Y %H:%M')) self.values['Date Modified'] = AttributeTableRow( 'updated_on', record.updated_on.strftime('%-d %b %Y %H:%M'))